linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
To: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: "linux-mm@kvack.org" <linux-mm@kvack.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: [RFC][PATCH ex/9] for debug
Date: Fri, 3 Apr 2009 17:24:53 +0900	[thread overview]
Message-ID: <20090403172453.3a229bb7.kamezawa.hiroyu@jp.fujitsu.com> (raw)
In-Reply-To: <20090403170835.a2d6cbc3.kamezawa.hiroyu@jp.fujitsu.com>

[-- Attachment #1: Type: text/plain, Size: 396 bytes --]

This mail attaches a patch and scirpt for debug.

soft_limit_show_prio.patch is for showing priority in memory.stat file.
I wonder I should add this to patch series or now...

cgroup.rb and ctop.rb is my personal ruby script, an utility to manage cgroup.
I sometimes use this. place both files to the same directory and run ctop.rb

#ruby ctop.rb

help will show this is for what.

Thanks,
-Kame

[-- Attachment #2: soft_limit_show_prio.patch --]
[-- Type: application/octet-stream, Size: 698 bytes --]

From: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>

Show internal control information of soft limit when DEBUG_VM is on.

Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
---
 mm/memcontrol.c |    1 +
 1 file changed, 1 insertion(+)

Index: softlimit-test2/mm/memcontrol.c
===================================================================
--- softlimit-test2.orig/mm/memcontrol.c
+++ softlimit-test2/mm/memcontrol.c
@@ -2617,6 +2617,7 @@ static int mem_control_stat_show(struct 
 #ifdef CONFIG_DEBUG_VM
 	cb->fill(cb, "inactive_ratio",
 			calc_inactive_ratio(mem_cont, NULL, NULL));
+	cb->fill(cb, "soft_limit_prio", mem_cont->soft_limit_priority);
 
 	{
 		int nid, zid;

[-- Attachment #3: cgroup.rb --]
[-- Type: application/octet-stream, Size: 10798 bytes --]


require 'find'

$subsys_array = Array.new
$allsubsys = Hash.new
$allmounts = Hash.new

class Sub_system
  def initialize(name, mount, option)
    @name = name
    @mount= mount
    @hierarchy = Array.new
    if (option =~ /.*noprefix.*/) then
      @prefix =""
    else
      @prefix = name +"."
    end
   @option = option
   @writable_files = Array.new
  end

  def mount_point
    @mount
  end

  def type
    @name
  end

  def myfile(name, attr)
    name + "/" + @prefix + attr
  end

  def option
    @option
  end
  #
  # walk directroy tree and add Cgroups to hash.
  #
  def reload
    @hierarchy.clear
    len = @mount.size
    Find.find(@mount) do |file|
      if File.directory?(file) then
	@hierarchy.push(file);
      end
    end
  end

  def each_cgroup(&block)
    @hierarchy.each(&block)
  end

  def ent(id)
    if (id < 0) then return nil
    end
    return @hierarchy.at(id)
  end

  def size
    @hierarchy.size
  end

  def stat (name)
    [["Not implemented", ""]]
  end

  def each_writable_files(name)
    @writable_files.each {|x| yield myfile(name,x)}
  end

  def tasks(name)
    list=Array.new
    begin
      File.open(name+"/tasks", "r") do |file|
        file.each_line do |x|
        x.chomp!
        list.push(x)
        end
      end
    rescue
      return nil
    end
    return list
  end
end

def read_oneline_file(filename)
  val=nil
  begin
    f = File.open(filename, "r")
    line = f.readline
    val = line.to_i
  rescue
    throw :readfailure,false
  ensure
    f.close if f != nil
  end
  return val
end

#
#for CPU subsystem
#
class Cpu_Subsys < Sub_system
  def initialize(mount, option)
    super("cpu", mount, option)
    @writable_files += ["shares"]
  end

  def read_share (name)
    ret = nil
    catch :readfailure do
      val = read_oneline_file(myfile(name,"shares"))
      return [val.to_s, val.to_s+" (100%)"] if (name == @mount)
      all=0

      dirname = File.dirname(name)
      Dir.foreach(dirname) do |x|
        next if ((x == ".") || (x == ".."))
        x = "#{dirname}/#{x}"
        next unless File.directory?(x)
        next unless File.exist?(myfile(name,"shares"))
        got = read_oneline_file(myfile(name,"shares"))
        all+=got
      end
      share=sprintf("%d (%.1f%%)", all, val*100.0/all)
      ret = [val.to_s, share]
    end
    return ret
  end

  def stat(name)
    level=0
    data = Array.new
    pos = @mount
    name_array = Array.new
    loop do
      name_array.push(name)
      break if name == @mount
      name = File.dirname(name)
    end
    name_array.reverse!
    name_array.each do |x|
      val = read_share(x)
      if val == nil then
        data = nil
        break
      end
      str = sprintf("%5s / %s", val[0], val[1])
      data.push([x, str])
    end
    return data if (data != nil && data.size > 0)
    return nil
  end
end

#
#for CPUacct subsystem
#
class Cpuacct_Subsys < Sub_system
  def initialize(mount, option)
    super("cpuacct", mount, option)
  end
  def stat(name)
    data = Array.new
    catch :read_failure do
      val = read_oneline_file(myfile(name, "usage"))
      data.push(["All", val.to_s])
      begin
        f = File.open(myfile(name,"usage_percpu"), "r")
        id=0
        line = f.readline
        while (line =~/\d+/) do
          line =$'
          data.push(["cpu"+id.to_s, $&])
          id += 1
        end 
      rescue
        data.clear
      ensure
        f.close if f != nil
      end
    return data if data.size > 0
    return nil
    end
  end
end

#
# For cpuset
#
class Cpuset_Subsys < Sub_system
  def initialize(mount, option)
    super("cpuset", mount, option)
    @elements =["cpu_exclusive","cpus","mems", "mem_exclusive","mem_hardwall",
                "memory_migrate", "memory_pressure", "memory_pressure_enabled",
                "memory_spread_page","memory_spread_slab",
                "sched_load_balance","sched_relax_domain_level"]
    @writable_files += @elements
  end
  def stat(name)
    data = Array.new
    for x in @elements
      begin
        filename = myfile(name, x)
        next unless (File.file?(filename))
        File.open(filename, "r") do | file |
          str = file.readline
          str.chomp!
          case x
          when "cpus", "mems"
            str = "empty" if (str == "")
          end
          data.push([x,str])
        end
      rescue
        #data = nil
        break
      end
    end
    return data
  end
end
#
#for Memory Subsys
#
def convert_bytes(bytes, precise)
  case
  when (precise == 0) && (bytes > 64 * 1024*1024*1024*1024)
    sprintf("Unlimited")
  when (precise == 0) && (bytes > 1024*1024*1024*1024)
    sprintf("%dT",bytes/1024/1024/1024/1024)
  when (precise == 0) && (bytes > 1024*1024*1024)
    sprintf("%dG", bytes/1024/1024/1024)
  when (bytes > 1024*1024)
    sprintf("%dM", bytes/1024/1024)
  when (bytes > 1024)
    sprintf("%dk", bytes/1024)
  else
    sprintf("%d", bytes)
  end
end

#
#for Memory Subsystem
#
class Memory_Subsys < Sub_system
  def initialize(mount, option)
    super("memory", mount, option)
    if (File.exist?("#{mount}/memory.memsw.usage_in_bytes")) then
      @memsw=true
    else
      @memsw=false
    end
    @writable_files += ["limit_in_bytes", "use_hierarchy","swappiness", "soft_limit_in_bytes"]
    if (@memsw) then
      @writable_files += ["memsw.limit_in_bytes"]
    end
  end
  #
  # Find a root directroy of hierarchy.
  #
  def find_hierarchy_root(name)
    cur=[name, File.dirname(name)]
    ret=@mount
    while (cur[0] != @mount)
      ret="hoge"
      under = read_oneline_file("#{cur[1]}/memory.use_hierarchy")
      if (under == 0) then
        return cur[0]
      end
      cur[0] = cur[1]
      cur[1] = File.dirname(cur[1])
    end
    return ret
  end
  #
  # Generate an array for reporintg status
  #
  def stat(name)
    data = Array.new

    success = catch(:readfailure) do
      under =read_oneline_file(myfile(name,"use_hierarchy"))
      if (under == 1) then
        str=find_hierarchy_root(name)
        if (str != name) then
          str="under #{str}"
          under=2
        else
          str="hierarchy ROOT"
        end
      else #Not under hierarchy
        str=""
      end
      ent = ["Memory Subsys", str]
      data.push(ent)
      
      # Limit and Usage
      x=Array.new
      x.push("Usage/Limit")

      bytes = read_oneline_file(myfile(name,"usage_in_bytes"))
      usage = convert_bytes(bytes, 1)

      if (@memsw) then
        bytes = read_oneline_file(myfile(name,"memsw.usage_in_bytes"))
        usage2 = convert_bytes(bytes, 1)
        usage = "#{usage} (#{usage2})"
      end
      bytes = read_oneline_file(myfile(name,"limit_in_bytes"))
      limit = convert_bytes(bytes, 0)
      usage = "#{usage} / #{limit}"
      if (@memsw) then
        bytes = read_oneline_file(myfile(name,"memsw.limit_in_bytes"))
        limit2 = convert_bytes(bytes, 0)
        usage = "#{usage} (#{limit2})"
      end
      x.push(usage)

      data.push(x)

      # MAX USAGE
      x = Array.new
      x.push("Max Usage")
      bytes = read_oneline_file(myfile(name, "max_usage_in_bytes"))
      usage = convert_bytes(bytes, 1)
      if (@memsw) then
        bytes = read_oneline_file(myfile(name,"memsw.max_usage_in_bytes"))
        usage2 = convert_bytes(bytes, 1)
        usage = "#{usage} (#{usage2})"
      end
      x.push(usage)
      data.push(x)

      # soft limit
      x = Array.new
      x.push("Soft limit")
      bytes = read_oneline_file(myfile(name, "soft_limit_in_bytes"))
      usage = convert_bytes(bytes, 1)
      x.push(usage)
      data.push(x)

      # failcnt
      x = Array.new
      x.push("Fail Count")
      cnt = read_oneline_file(myfile(name,"failcnt"))
      failcnt = cnt.to_s
      if (@memsw) then
        cnt = read_oneline_file(myfile(name, "memsw.failcnt"))
        failcnt="#{failcnt} (#{cnt.to_s})"
      end
      x.push(failcnt)
      data.push(x)

      begin
        f = File.open(myfile(name,"stat"), "r")
        for x in ["Cache","Rss","Pagein","Pageout",nil, nil, nil, nil, nil,
                  "HierarchyLimit","SubtreeCache","SubtreeRss", nil, nil,
		  nil, nil, nil, nil, nil, nil, "soft_limit_prio"]
          line =f.readline
          next if x == nil
          line =~ /^\S+\s+(.+)/
          val=$1
          case x
          when "Cache","Rss","SubtreeCache","SubtreeRss"
            bytes = convert_bytes(val.to_i, 1)
            data.push([x, bytes])
          when "Pagein","Pageout"
            data.push([x, val])
	  when "soft_limit_prio"
	    data.push([x, val])
          when "HierarchyLimit"
            memlimit = convert_bytes(val.to_i, 0)
            if (@memsw) then
              line =f.readline
              line =~ /^\S+\s+(.+)/
              memswlimit = convert_bytes(val.to_i, 0)
              memlimit += " (" + memswlimit + ")"
            end
            data.push([x, memlimit])
          end
        end
      ensure
        f.close if f != nil
      end
    true
    end
    return data if success==true
    return nil
  end
end

#
# Read /proc/mounts and parse each lines.
# When cgroup mount point is found, each subsystem's cgroups are added
# to subsystem's Hash.
#

def register_subsys(name, mount, option)
  if $allsubsys[name] == nil then
    subsys = nil
    case name
    when "cpu" then subsys = Cpu_Subsys.new(mount, option)
    when "cpuacct" then subsys = Cpuacct_Subsys.new(mount, option)
    when "memory" then  subsys = Memory_Subsys.new(mount, option)
    when "cpuset" then  subsys = Cpuset_Subsys.new(mount, option)
    end
    if subsys != nil then
      $subsys_array.push(name)
      $allsubsys[name] = subsys
    end
  end
end

#
# Read /proc/mounts and prepare subsys array
#
def parse_mount(line)
  parsed = line.split(/\s+/)
  if parsed[2] == "cgroup" then
    mount=parsed[1]
    opts=parsed[3].split(/\,/)
    opts.each do |name|
      case name
      when "rw" then next
      else
        register_subsys(name, mount, parsed[3])
        $allmounts[mount]=name
      end
    end
  end
end


def read_mount
  File.open("/proc/mounts", "r") do |file|
    file.each_line {|line| parse_mount(line) }
  end
  $subsys_array.sort!
end

#
# Read all /proc/mounts and scan directory under mount point.
#
def refresh_all
   $allmounts.clear
   $subsys_array.clear
   $allsubsys.clear
   read_mount
end

def check_and_refresh_mount_info
  
  mysubsys=Array.new
  File.open("/proc/mounts", "r") do |file|
    file.each_line do |line|
      parsed = line.split(/\s+/)
      if (parsed[2] == "cgroup")  then
        mysubsys.push(parsed[1])
      end
    end
  end

  if (mysubsys.size != $allmounts.size) then
    refresh_all
    return true 
  end
  
  mysubsys.each do |x|
    if ($allmounts[x] == nil) then
      refresh_all
      return true
    end
  end
  return false
end


[-- Attachment #4: ctop.rb --]
[-- Type: application/octet-stream, Size: 20106 bytes --]

#
# ctop.rb 
# written by KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
# Copyright 2009 Fujitsu Limited
#
# Changelog:
#
# v003
#   - fixed bug in rmdir/mkdir
#   - changed command-mode interface
#   - added comments and made codes clean
# 
# v002 (2009/02/25)
#   - fixed leak of file descriptor
#   - mount/umount <-> reload data problem is fixed.
#   - "mount twice" problem is fixed.
#   - removed R key for reload all. it's now automatic
#   - handle "noprefix" mount option
#   - show mount option in help window
#   - add cpuset support
#   - add command-mode
#
# v001 (2009/02/04)
#   - first version released
#   - cpu, cpuacct, memory subsys is supported
#   known bugs -> noprefix, umount, mount twice
#
require 'cgroup.rb'
require 'curses'
require 'etc'
require 'timeout'
require 'singleton'

DIRWIN_LINES=7
DIRWIN_FIELDS= DIRWIN_LINES - 2
UPKEY=256
DOWNKEY=257
RIGHTKEY=258
LEFTKEY=259

#mode
SHOWMOUNT=0
SHOWTASKS=1
SHOWSUBSYS=2

#for 'ps'
PID=0
STATE=1
PPID=2
UID=3
COMMAND=4
PGID=5

#for process status filter
RUNNING=0

#
# Helper function for curses
#

def hit_any_key(str, window)
  window.addstr(str) if str != nil
  window.addstr("\n[Hit Any Key]")
  window.getch
end

def window_printf(window, format, *arg)
  str = sprintf(format, *arg)
  window.addstr(str)
end
#
# Cursor holds current status of subsys's window.
# 
#
class Cursor
  def initialize(name)
    @subsysname=name            # name of subsys
    @cursor=0                   # current directroy position
    @mode=SHOWTASKS             # current mode (ps-mode/stat-mode)
    @info_startline=0           # used for scroll in infowin
    @info_endline=0             # used for scorll in infowin
    @show_only_running = 0      # a filter for ps-mode
    @user_name_filter=nil       # a filter for ps-mode
    @command_name_filter=nil    # a filter for ps-mode
  end

  def pos
    @cursor
  end

  def mode
    @mode
  end

  def change_mode # switch mode ps-mode <-> stat-mode
    case @mode
    when SHOWTASKS then @mode=SHOWSUBSYS
    when SHOWSUBSYS then @mode=SHOWTASKS
    end
  end
  #
  # Filter for PS-MODE
  #
  def process_status_filter(stat)
    return true if (@show_only_running == 0)
    return true if (stat =="R")
    return false
  end

  def user_name_filter(str)
    return true if (@user_name_filter == nil)
    return true if (@user_name_filter == str)
    return false
  end

  def command_name_filter(str)
    return true if (@command_name_filter == nil)
    return true if (str =~ /#{@command_name_filter}/)
    return false
  end

  def toggle_show_only_running
    if (@show_only_running == 0) then
      @show_only_running = 1 # show only running process in ps-mode
    else
      @show_only_running = 0 # show all processes
    end
  end

  def set_user_name_filter(str)
    str = nil if (str == "")
    @user_name_filter=str
  end

  def set_command_name_filter(str)
    str=nil if (str == "")
    @command_name_filter=str
  end

  #
  # Scroll management for infowin 
  #
  def info_startline
    @info_startline
  end

  def set_infoendline(num)
    @info_endline=num
  end

  def set_infoline(num)
    if ((num < 0) || (num >= @info_endline)) then
      @info_startline=0
    else
      @info_startline=num
    end
  end

  #
  # chdir() for subsys.
  #
  def move(direction)
    subsys =$allsubsys[@subsysname]
    if (subsys == nil) then return
    end
    if (direction == -1) then
      @cursor -= 1 if @cursor > 0
    elsif (direction == 1)
      @cursor += 1 if @cursor < subsys.size-1
    end
  end
end

#
# Current is a singleton holds current status of this program.
#
class Current
  include Singleton
  def initialize
    @index=-1    #current susbsys index in $subsys_array[]
    @name=nil    #current name of subsys
    @cursor=nil  #reference to current Cursor 
    @subsys=nil  #reference to current Subsys 
    @subsys_cursor = Hash.new
  end

  def set(x)
    @index=x
    if (x == -1) then
      @index, @name, @cursor, @subsys = -1, "help", nil, nil
    else
      @name = $subsys_array[x]
      @subsys = $allsubsys[@name]
      if (@subsys_cursor[@name] == nil) then
        @subsys_cursor[@name] = Cursor.new(@name)
      end
      @cursor = @subsys_cursor[@name]
    end
  end

  #change subsys view
  def move (dir)
    case dir
    when "left"
      @index -= 1 if (@index > -1)
    when "right"
      @index += 1 if (@index < $subsys_array.size - 1)
    end
    set(@index)
  end

  #change directroy view of current cursor
  def chdir(direction)
    if (@cursor != nil) then
      @cursor.move(direction)  
    end
  end
  #switch current mode of cursor
  def change_mode
    if (@cursor != nil) then
      @cursor.change_mode
    end
  end

  def name
    @name
  end

  def cursor
    @cursor
  end

  def subsys
    @subsys
  end
end

$cur = Current.instance

#
# Show directory window
#

def detect_dirlist_position(subsysname, subsys)
  pos = 0
  size=subsys.size
  cursor = $cur.cursor
  return [0, 0, 0] if cursor == nil

  pos = cursor.pos
  if ((size < 4) || (pos <= 2)) then 
      head=0
      tail=4
  elsif (pos < size - 2) then
      head=pos-1
      tail=pos+2
  else
      head = size - 4
      tail = size - 1
  end
  return [pos, head, tail]
end

def get_owner_name(name)
  begin
    stat = File.stat(name)
  rescue
    return ""
  end
  begin
    info = Etc::getpwuid(stat.uid)
    uname = info.name
  rescue
    $barwin.addstr($!)
    uname = stat.uid.to_s
  end

  begin
    info = Etc::getgrgid(stat.gid)
    gname = info.name
  rescue
    gname = stat.gid.to_s
  end
  sprintf("\t-\t(%s/%s)", uname, gname)
end

def draw_dirlist(dirwin, subsys)

  now, head, tail = detect_dirlist_position($cur.name, subsys)

  lines=1
  i=head
  while i <= tail
    name = subsys.ent(i)
    if (name == nil) then break
    end

    dirwin.setpos(lines, 3)
    dirwin.standout if (i == now)
    dirwin.addstr(name + get_owner_name(name))
    dirwin.standend if (i == now)
    lines+=1
    i += 1
  end
end

#
# Fill dirwin contents.
#
def draw_dirwin(dirwin)
  dirwin.clear
  dirwin.box(?|,?-,?*)
  dirwin.setpos(0, 1)

  #show all subsyss in head
  -1.upto($subsys_array.size) do |x|
    dirwin.addstr("-")
    if (x == -1) then
      str="help"
    else
      str=sprintf("%s",$subsys_array[x])
    end
    break if (str == nil)

    dirwin.standout if (str == $cur.name)
    dirwin.addstr(str)
    dirwin.standend if (str == $cur.name)
  end

  #show current time
  dirwin.setpos(6,dirwin.maxx-32)
  dirwin.addstr("[#{Time.now.asctime}]")
  #
  # Show directory list
  #
  if $cur.subsys != nil then
    #Reload information 
    $cur.subsys.reload
    draw_dirlist(dirwin, $cur.subsys)
  end
end

#
#
# for infowin
#

#
# Contents of infowin will be passed by data[]
# This function shows contents based on current scroll infromation.
# for converting contents of array to string, code block is called by yield
#
def draw_infowin_limited(infowin, cursor, data)
  #
  # Generate Header
  #
  str = yield nil # write a header if necessary
  if (str != nil) then
    draw=1
    infowin.setpos(0,2)
    infowin.addstr(str)
  else
    draw=0
  end
  #
  # print a line whici is in the window
  #
  startline = cursor.info_startline
  endline = cursor.info_startline + infowin.maxy-2
  startline.upto(endline) do |linenumber|
    x = data.at(linenumber)
    return if (x == nil) #no more data
    str = yield(x)
    infowin.setpos(draw, 2)
    infowin.addstr(str)
    draw = 1+infowin.cury
    break if (draw == infowin.maxy)
  end

  cursor.set_infoendline(data.size)  
end

#
#
# Show help and current mount information in help window
#
def show_mount_info(infowin)
  if ($allsubsys.empty?) then
    $barwin.addstr("cgroups are not mounted\n")
  end
  $allsubsys.each do |name, subsys|
    window_printf(infowin, "%12s\t%s\t#%s\n",
                       name, subsys.mount_point, subsys.option)
  end
  #$barwin.addstr("mounted subsystems")
  #
  # Help
  #
  infowin.addstr("Command\n")
  infowin.addstr("[LEFT, RIGHT]\t move subsystems\n")
  infowin.addstr("[UP, DOWN]\t move directory\n")
  infowin.addstr("[n, b]\t\t scorll information window\n")
  infowin.addstr("[s]\t\t switch shown information (ps-mode/stat-mode)\n")
  infowin.addstr("[r]\t\t set refresh rate\n")
  infowin.addstr("[c]\t\t Enter command-mode\n")

  infowin.addstr("ps mode option\n")
  infowin.addstr("[t]\t\t (ps-mode)toggle show only running process\n")
  infowin.addstr("[u]\t\t (ps-mode)set/unset user name filter\n")
  infowin.addstr("[f]\t\t (ps-mode)set/unset command name filter")

end

#
# Read /proc/<pid>/status file and fill data[] array, return it
#

def parse_pid_status(f, es)
  input = f.readline
  input =~ es
  return $1
end

#
# data[] =[PID, State, PPID, UID, COMMAND, PGID]
#
def parse_process(pid)
  #
  # Status
  #
  data = Array.new
  stat = nil

  stat = catch(:bad_task_status) do
    data[PID]=pid.to_i
    begin
      f = File.open("/proc/#{pid}/status", "r")

      #Name
      data[COMMAND] = parse_pid_status(f,/^Name:\s+(.+)/)
      unless (File.exist?("/proc/#{pid}/exe")) then
        data[COMMAND] = "[" + data[COMMAND] + "]"
      end
      #State
      data[STATE] = parse_pid_status(f, /^State:\s+([A-Z]).+/)
      # TGID: Is thread grouo leader ?
      if (parse_pid_status(f, /^Tgid:\s+(.+)/) != pid) then
        throw :bad_task_status, false
      end
      #skip PID
      input = f.readline
      #PPID
      data[PPID]= parse_pid_status(f,/^PPid:\s+(.+)/)
      ppid=data[PPID]
      #TracerPID
      input = f.readline 
      #UID
      uid = parse_pid_status(f,/^Uid:\s+([0-9]+).+/)
      begin 
        info=Etc::getpwuid(uid.to_i)
        data[UID]=info.name
      rescue 
        data[UID]=uid
      end
    rescue
      throw :bad_task_status, false 
    ensure
      f.close unless f.nil?
    end
  end
  return data unless stat.nil?
  return nil
end

#
# PS-MODE
# Cat "tasks" file and visit all /proc/<pid>/status file
# All information will be pushed into "ps" array
#
def show_tasks(subsys, cursor, infowin)
  # Get Name of Current Cgroup and read task file
  ps = Array.new  
  catch :quit do
    group = subsys.ent(cursor.pos)
    throw :quit,"nogroup" if group==nil
    tasks = subsys.tasks(group)
    throw :quit,"nogroup" if tasks==nil
     
    tasks.each do |x|
      data = parse_process(x)
      next if (data == nil)
      next unless (cursor.process_status_filter(data[STATE]))
      next unless (cursor.command_name_filter(data[COMMAND]))
      ps.push(data) if (cursor.user_name_filter(data[UID]))
    end
    #
    # Sort ps's result, "R" first.
    #
    ps.sort! do |x , y|
      if (x[STATE] == "R" && y[STATE] != "R") then
        -1
      elsif (x[STATE] != "R" && y[STATE] == "R") then
        1
      else
        0
      end
    end
  end
  
  return if (ps.size == 0)

  draw_infowin_limited(infowin, cursor, ps)do |x|
    if (x == nil) then
      sprintf("%6s %6s %8s %5s %16s", "PID","PPID","USER","STATE", "COMMAND")
    else
      sprintf("%6d %6d %8s %5s %16s",
              x[PID], x[PPID], x[UID], x[STATE], x[COMMAND])
    end
  end

  unless ($cur.cursor.process_status_filter("S")) then
    $barwin.addstr("[r]")
  end
  unless ($cur.cursor.user_name_filter("badnamemandab")) then
    $barwin.addstr("[u]")
  end
  unless ($cur.cursor.command_name_filter("badnamemandab")) then
    $barwin.addstr("[c]")
  end
end

def show_subsys_stat(subsys, cursor, infowin)
  group = subsys.ent(cursor.pos)
  return if group == nil
  data = subsys.stat(group)
  return if data == nil
  draw_infowin_limited(infowin, cursor, data) do |x|
    next if x == nil
    if (x[0].size > 24) then
      len = x[0].size - 24
      x[0].slice!(0..len)
    end
    sprintf("%24s\t%s", x[0], x[1])
  end
end


#
# [n],[b]  Move cursor's current position in infowin
#
def set_scroll(infowin, direction)
  cursor = $cur.cursor
  return if (cursor == nil)

  if (direction == 1) then 
    curline=cursor.info_startline
    cursor.set_infoline(curline+infowin.maxy)
  else
    curline=cursor.info_startline
    cursor.set_infoline(curline-infowin.maxy)
  end
end

#
# [t] Set/Unset Show-Running-Only filter
#
def toggle_running_filter
  if ($cur.cursor != nil) then
    $cur.cursor.toggle_show_only_running
  end
end


#
# Filters for ps-mode
#

#
# [u] Filter by UID
#
def user_name_filter(infowin)
  infowin.clear
  window_printf(infowin, "user name filter:")
  str=infowin.getstr
  cursor= $cur.cursor
  cursor.set_user_name_filter(str) if (cursor != nil)
end

#
# [f] Filter by name of command
#
def command_name_filter(infowin)
  infowin.clear
  window_printf(infowin, "command name filter:")
  str=infowin.getstr
  cursor =$cur.cursor
  cursor.set_command_name_filter(str) if (cursor != nil)
end

#
# [r] set refresh time
#
def set_refresh_time(time, infowin)
  infowin.clear
  window_printf(infowin, "set refresh time(now %ds)",time)
  str=infowin.getstr
  return time if (str.to_i == 0)
  return str.to_i
end

#
# [c] Below are sub routines for command-mode.
#

def smart_print(str, window)
  if (window.maxx - window.curx < str.size-2) then
    window.addstr("\n"+str)
  else
    window.addstr(str)
  end
end

def show_writable_files(subsys, cursor, infowin)
  group = subsys.ent(cursor.pos)
  return nil if group == nil
  ent=1
  data = Array.new
  subsys.each_writable_files(group) do |x|
    str = sprintf("%2d: %s ", ent, File.basename(x))
    ent=ent+1
    smart_print(str, infowin)
    data.push(x)
  end
  infowin.refresh
  return data
end

#
# Scan directroy and change owner/group of all regular files
# and current directory.
#
def chown_all_files(uid, gid, group, infowin)
  # change owner/group of current dir
  begin
    File.chown(uid, gid, group)
  rescue
    hit_any_key("Error:"+$!, infowin)
    return
  end
  # change owner/group of regular files
  Dir.foreach(group) do |x|
    name = group+"/"+x
    next if File.directory?(name)
    begin
      File.chown(nil, gid, name)
    rescue
      hit_any_key("Error:"+$!, infowin)
      break
    end
  end
end

#
# Check "/" is included or not at mkdir/rmdir
#
def check_mkrmdir_string(str, infowin)
  if (str =~ /\//) then
    infowin.addstr("don't include /\n")
    return false
  elsif (str == ".") then
    infowin.addstr("can't remove current\n")
  end
  return true
end

#
# Get string and retuns uid or gid as integer
#
def parse_id(window, uid, str)
  if (str =~ /\D/) then
    begin
      if (uid == 1) then
        info = Etc::getpwnam(str)
        id = info.uid
      else
        info = Etc::getgrnam(str)
        id = info.gid
      end
    rescue
      hit_any_key("Error:"+$!, window)
      id=nil
    end
  else
    id = str.to_i
  end
  return id
end

#
#
# Command mode interface
#
#
def command_mode(infowin)
  return if ($cur.subsys == nil)
  infowin.clear
  $barwin.clear
  $barwin.addstr("[command-mode]")
  $barwin.refresh
  #
  # Subsys special files are in number
  #
  infowin.addstr("====subsys command====\n")
  data = show_writable_files($cur.subsys, $cur.cursor, infowin)
  if data==nil then
    infowin.addstr("no subsys command")
  end
  #
  # Cgroup generic ops are in alphabet 
  #
  infowin.addstr("\n====cgroup command====\n")
  smart_print("[A] attach task(PID)", infowin)
  smart_print(" [M] mkdir", infowin)
  smart_print(" [R] rmdir",infowin)
  smart_print(" [O] chown(OWNER)", infowin)
  smart_print(" [G] chown(GID)", infowin)
  infowin.addstr("\n\nModify which ? [and Hit return]:")

  #line to show prompt
  endline = infowin.cury+1
  #wait for the numbers or AOGMR
  str=infowin.getstr
  #target directory is this.
  group = $cur.subsys.ent($cur.cursor.pos)

  case str.to_i # if str is not number, returns 0.
  # Subsystem commands
  when 1..99
    if (data != nil) then
      name = data.at(str.to_i - 1)
      #get input
      infowin.setpos(endline, 0)
      window_printf(infowin, "#echo to >%s:", File.basename(name))
      str = infowin.getstr
      #write
      begin
        f = File.open(name, "w") {|f| f.write(str) }
      rescue
        hit_any_key("Error:"+$!, infowin)
      end
    end
  # Cgroup commands (str.to_i returns 0)
  when 0
    case str
    when "a","A" #Attach
      window_printf(infowin, "Attach task to %s:", group)
      str = infowin.getstr
      begin 
        File.open(group + "/tasks", "w") {|f| f.write(str) }
      rescue
        hit_any_key("Error:"+$!, infowin)
      end

    when "o","O" #chown (OWNER)
      infowin.addstr("change owner id of all files to:")
      id = parse_id(infowin, 1, infowin.getstr)
      chown_all_files(id, -1, group, infowin) if id != nil

    when "g","G" #chown (GROUP)
      infowin.addstr("change group id of all files to:")
      id = parse_id(infowin, 0, infowin.getstr)
      chown_all_files(-1, id, group, infowin) if id != nil

    when "m","M" #mkdir
      infowin.addstr("mkdir -.enter name:")
      str = infowin.getstr
      if (check_mkrmdir_string(str, infowin)) then
        begin
          if (Dir.mkdir(group+"/"+str) != 0) then
            hit_any_key("Error:"+$!, infowin)
	 end
        rescue
          hit_any_key("Error:"+$!, infowin)
        end
      else
        hit_any_key(nil, infowin)
      end

    when "r","R" #rmdir
      infowin.addstr("rmdir -.enter name:")
      str = infowin.getstr
      if (check_mkrmdir_string(str, infowin)) then
        begin
          if (Dir.rmdir(group+"/"+str) != 0) then
            hit_any_key("Error:"+$!, infowin)
          end
        rescue
          hit_any_key("Error:"+$!, infowin)
        end
      else
        hit_any_key(nil, infowin)
      end
    end
  end
  $barwin.clear
end

#
# Main draw routine
#
def draw_infowin(infowin)
  infowin.clear
  cursor = $cur.cursor
  if cursor == nil then
    mode = SHOWMOUNT
  else
    mode = cursor.mode
  end
  #
  # If no subsys is specified, just show mount information.
  #

  case mode
  when SHOWMOUNT
    show_mount_info(infowin)
  when SHOWTASKS
    $barwin.addstr("[ps-mode]")
    show_tasks($cur.subsys, cursor, infowin)
  when SHOWSUBSYS
    $barwin.addstr("[stat-mode]")
    show_subsys_stat($cur.subsys, cursor, infowin)
  end
end

#
# Main loop
#
#
# For stdscreen
#
# Check /proc/mounts and read all subsys.
#
refresh_all

#
# Main loop. create windows and wait for inputs
#
Curses::init_screen
begin
  $lines=Curses::lines
  $cols=Curses::cols
  off=0
  #
  # Create window
  #
  dirwin = Curses::stdscr.subwin(DIRWIN_LINES, $cols, off, 0)
  #for misc info
  off+=DIRWIN_LINES
  $barwin = Curses::stdscr.subwin(1, $cols, off, 0);
  $barwin.standout
  off+=1
  infowin = Curses::stdscr.subwin($lines-off, $cols, off, 0)
  mode=SHOWTASKS  
  quit=0
  refresh_time=15
  
  while quit == 0 
    #$barwin.clear

    #$barwin.addstr("Info:")
    draw_dirwin(dirwin)
    draw_infowin(infowin)
    dirwin.refresh
    infowin.refresh
    $barwin.refresh
    #
    # handle input.
    # 
    $barwin.clear
    Curses::setpos(0,0)
    ch=0
    Curses::noecho
    begin
      Timeout::timeout(refresh_time) do
        ch=Curses::getch
      end
    rescue Timeout::Error
      #$barwin.addstr("timeout")
    end
    Curses::echo
    #check espace sequence
    if ch == 27 then
      ch = Curses::getch
      if ch == 91 then
        ch = Curses::getch
        case ch
          when 65 then ch = UPKEY
          when 66 then ch = DOWNKEY
          when 67 then ch = RIGHTKEY
          when 68 then ch = LEFTKEY
        end
      end
    end
    #
    #
    #
    if (check_and_refresh_mount_info) then
      $cur.set(-1)
    end
   
    #$barwin.addstr(Time.now.asctime)
    case ch
      when ?q
        quit=1
        break
      when LEFTKEY then $cur.move("left")
      when RIGHTKEY then $cur.move("right")
      when UPKEY then $cur.chdir(-1)
      when DOWNKEY then $cur.chdir(1)
      when ?s then $cur.change_mode
      when ?n then set_scroll(infowin, 1)
      when ?b then set_scroll(infowin, -1)
      when ?t then toggle_running_filter
      when ?u then user_name_filter(infowin)
      when ?f then command_name_filter(infowin)
      when ?c then command_mode(infowin)
      when ?r then refresh_time=set_refresh_time(refresh_time, infowin)
    end
  end
ensure
  Curses::close_screen
end

  parent reply	other threads:[~2009-04-03  8:25 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-04-03  8:08 [RFC][PATCH 0/9] memcg soft limit v2 (new design) KAMEZAWA Hiroyuki
2009-04-03  8:09 ` [RFC][PATCH 1/9] " KAMEZAWA Hiroyuki
2009-04-03  8:10 ` [RFC][PATCH 2/9] soft limit framework for memcg KAMEZAWA Hiroyuki
2009-04-03  8:12 ` [RFC][PATCH 3/9] soft limit update filter KAMEZAWA Hiroyuki
2009-04-06  9:43   ` Balbir Singh
2009-04-07  0:04     ` KAMEZAWA Hiroyuki
2009-04-07  2:26       ` Balbir Singh
2009-04-03  8:12 ` [RFC][PATCH 4/9] soft limit queue and priority KAMEZAWA Hiroyuki
2009-04-06 11:05   ` Balbir Singh
2009-04-06 23:55     ` KAMEZAWA Hiroyuki
2009-04-06 18:42   ` Balbir Singh
2009-04-06 23:54     ` KAMEZAWA Hiroyuki
2009-04-03  8:13 ` [RFC][PATCH 5/9] add more hooks and check in lazy manner KAMEZAWA Hiroyuki
2009-04-03  8:14 ` [RFC][PATCH 6/9] active inactive ratio for private KAMEZAWA Hiroyuki
2009-04-03  8:15 ` [RFC][PATCH 7/9] vicitim selection logic KAMEZAWA Hiroyuki
2009-04-03  8:17 ` [RFC][PATCH 8/9] lru reordering KAMEZAWA Hiroyuki
2009-04-03  8:18 ` [RFC][PATCH 9/9] more event filter depend on priority KAMEZAWA Hiroyuki
2009-04-03  8:24 ` KAMEZAWA Hiroyuki [this message]
2009-04-06  9:08 ` [RFC][PATCH 0/9] memcg soft limit v2 (new design) Balbir Singh
2009-04-07  0:16   ` KAMEZAWA Hiroyuki
2009-04-24 12:24 ` Balbir Singh
2009-04-24 15:19   ` KAMEZAWA Hiroyuki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090403172453.3a229bb7.kamezawa.hiroyu@jp.fujitsu.com \
    --to=kamezawa.hiroyu@jp.fujitsu.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox