# Utilities for getting process data from recent Linux kernels
# M. Edward (Ed) Borasky
# do we have a recent enough version of Linux?
# the stuff we want is only in kernels since 2.6.22-rc3
def linux_recent_enough?
return false if RUBY_PLATFORM !~/linux/ # well, duh!
zrelease = `uname -r`.sub(/-.*$/, "").chomp.split('.')
return false if zrelease[0] < 2
return false if zrelease[1] < 6
return false if zrelease[2] < 22
return true
end
# we need these to convert to human-readable units
def ticks_per_second
`getconf CLK_TCK`.chomp.to_f
end
def page_bytes
`getconf PAGESIZE`.chomp.to_f
end
# Definitions of what's in /proc/<pid>/statm and /proc/<pid>/statm
# (from Gentoo Linux 2.6.25-r6)
# See /usr/src/linux/Documentation/filesystems/proc.txt on your system if you
# want to hack on this code (for example, to backport it to "stable" versions
# of Linux). If you don't *have*
# /usr/src/linux/Documentation/filesystems/proc.txt, install the Linux source
# package. :)
# Table 1-2: Contents of the statm files (as of 2.6.8-rc3)
# ..............................................................................
# Field Content
# size total program size (pages) (same as VmSize in status)
# resident size of memory portions (pages) (same as VmRSS in status)
# shared number of pages that are shared (i.e. backed by a file)
# trs number of pages that are 'code' (not including libs; broken,
# includes data segment)
# lrs number of pages of library (always 0 on 2.6)
# drs number of pages of data/stack (including libs; broken,
# includes library text)
# dt number of dirty pages (always 0 on 2.6)
# ..............................................................................
# field names in /proc/<pid>/statm
STATM_NAMES = %w(
size
resident
shared
trs
lrs
drs
dt
)
#
# Table 1-3: Contents of the stat files (as of 2.6.22-rc3)
# ..............................................................................
# Field Content
# pid process id
# tcomm filename of the executable
# state state (R is running, S is sleeping, D is sleeping in an
# uninterruptible wait, Z is zombie, T is traced or stopped)
# ppid process id of the parent process
# pgrp pgrp of the process
# sid session id
# tty_nr tty the process uses
# tty_pgrp pgrp of the tty
# flags task flags
# min_flt number of minor faults
# cmin_flt number of minor faults with child's
# maj_flt number of major faults
# cmaj_flt number of major faults with child's
# utime user mode jiffies
# stime kernel mode jiffies
# cutime user mode jiffies with child's
# cstime kernel mode jiffies with child's
# priority priority level
# nice nice level
# num_threads number of threads
# it_real_value (obsolete, always 0)
# start_time time the process started after system boot
# vsize virtual memory size
# rss resident set memory size
# rsslim current limit in bytes on the rss
# start_code address above which program text can run
# end_code address below which program text can run
# start_stack address of the start of the stack
# esp current value of ESP
# eip current value of EIP
# pending bitmap of pending signals (obsolete)
# blocked bitmap of blocked signals (obsolete)
# sigign bitmap of ignored signals (obsolete)
# sigcatch bitmap of catched signals (obsolete)
# wchan address where process went to sleep
# 0 (place holder)
# 0 (place holder)
# exit_signal signal to send to parent thread on exit
# task_cpu which CPU the task is scheduled on
# rt_priority realtime priority
# policy scheduling policy (man sched_setscheduler)
# blkio_ticks time spent waiting for block IO
# field names in /proc/<pid>/stat
STAT_NAMES = %w(
pid
tcomm
state
ppid
pgrp
sid
tty_nr
tty_pgrp
flags
min_flt
cmin_flt
maj_flt
cmaj_flt
utime
stime
cutime
cstime
priority
nice
num_threads
it_real_value
start_time
vsize
rss
rsslim
start_code
end_code
start_stack
esp
eip
pending
blocked
sigign
sigcatch
wchan
unused1
unused2
exit_signal
task_cpu
rt_priority
policy
blkio_ticks
)
class Proc_file_sample
attr_reader :sample # the file we read
def initialize(filename)
@sample = IO.read(filename)
end
# method to return sample as a hash, given names of the fields
def sample_hash(names)
result = Hash.new
fields = @sample.chomp.split(' ')
names.each do |name|
result[name] = fields.shift
end
return result
end
end
class Stat_pid < Proc_file_sample
def initialize(pid)
super("/proc/#{pid}/stat")
end
def to_hash
self.sample_hash(STAT_NAMES)
end
end
class Statm_pid < Proc_file_sample
def initialize(pid)
super("/proc/#{pid}/statm")
end
def to_hash
self.sample_hash(STATM_NAMES)
end
end
def to_megabytes(pages)
pages.to_f*page_bytes/1024.0/1024.0
end
def to_seconds(ticks)
ticks.to_f/ticks_per_second
end
def average_megabytes(stat_before, stat_after)
0.5*(
to_megabytes(stat_after.to_hash['rss']) +
to_megabytes(stat_before.to_hash['rss'])
)
end
def delta(stat_before, stat_after, field)
stat_after.to_hash[field].to_i -
stat_before.to_hash[field].to_i
end
def major_faults(stat_before, stat_after)
delta(stat_before, stat_after, 'maj_flt')
end
def minor_faults(stat_before, stat_after)
delta(stat_before, stat_after, 'min_flt')
end
def blkio_seconds(stat_before, stat_after)
to_seconds(delta(stat_before, stat_after, 'blkio_ticks'))
end
def user_seconds(stat_before, stat_after)
to_seconds(delta(stat_before, stat_after, 'utime'))
end
def system_seconds(stat_before, stat_after)
to_seconds(delta(stat_before, stat_after, 'stime'))
end
def cpu_seconds(stat_before, stat_after)
user_seconds(stat_before, stat_after) +
system_seconds(stat_before, stat_after)
end
def megabyte_seconds(stat_before, stat_after)
cpu_seconds(stat_before, stat_after) *
average_megabytes(stat_before, stat_after)
end
TESTING = false
if (TESTING)
stat_before = Stat_pid.new("self")
(1..10000000).each do |i|
print "." if i%100000 == 0
end
stat_after = Stat_pid.new("self")
print "\n"
print "average megabytes=", average_megabytes(stat_before, stat_after),
"\n"
print "major faults=", major_faults(stat_before, stat_after), "\n"
print "minor faults=", minor_faults(stat_before, stat_after), "\n"
print "blkio seconds=", blkio_seconds(stat_before, stat_after), "\n"
print "user seconds=", user_seconds(stat_before, stat_after), "\n"
print "system seconds=", system_seconds(stat_before, stat_after), "\n"
print "cpu seconds=", cpu_seconds(stat_before, stat_after), "\n"
print "megabyte-seconds=", megabyte_seconds(stat_before, stat_after), "\n"
end