Skip to content

Instantly share code, notes, and snippets.

@copiousfreetime
Created November 15, 2011 03:06
Show Gist options
  • Save copiousfreetime/1366013 to your computer and use it in GitHub Desktop.
Save copiousfreetime/1366013 to your computer and use it in GitHub Desktop.
useful info from /proc/[pid]/smaps
#!/usr/bin/env ruby
#------------------------------------------------------------------------------
# Aggregate Print useful information from /proc/[pid]/smaps
#
# pss - Roughly the amount of memory that is "really" being used by the pid
# swap - Amount of swap this process is currently using
#
# Reference:
# http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt#361
#
# Example:
# # ./memstats.rb 4386
# Process: 4386
# Command Line: /usr/bin/mongod -f /etc/mongo/mongod.conf
# Memory Summary:
# private_clean 107,132 kB
# private_dirty 2,020,676 kB
# pss 2,127,860 kB
# rss 2,128,536 kB
# shared_clean 728 kB
# shared_dirty 0 kB
# size 149,281,668 kB
# swap 1,719,792 kB
#------------------------------------------------------------------------------
class Mapping
FIELDS = %w[ size rss shared_clean shared_dirty private_clean private_dirty swap pss ]
attr_reader :address_start
attr_reader :address_end
attr_reader :perms
attr_reader :offset
attr_reader :device_major
attr_reader :device_minor
attr_reader :inode
attr_reader :region
attr_accessor :size
attr_accessor :rss
attr_accessor :shared_clean
attr_accessor :shared_dirty
attr_accessor :private_dirty
attr_accessor :private_clean
attr_accessor :swap
attr_accessor :pss
def initialize( lines )
FIELDS.each do |field|
self.send("#{field}=", 0)
end
parse_first_line( lines.shift )
lines.each do |l|
parse_field_line(l)
end
end
def parse_first_line( line )
parts = line.strip.split
@address_start, @address_end = parts[0].split("-")
@perms = parts[1]
@offset = parts[2]
@device_major, @device_minor = parts[3].split(":")
@inode = parts[4]
@region = parts[5] || "anonymous"
end
def parse_field_line( line )
parts = line.strip.split
field = parts[0].downcase.sub(':','')
value = Float(parts[1]).to_i
self.send( "#{field}=", value )
end
end
def consume_mapping( map_lines, totals )
m = Mapping.new( map_lines )
Mapping::FIELDS.each do |field|
totals[field] += m.send( field )
end
return m
end
pid = Float(ARGV.shift).to_i
abort "pid required" unless pid
totals = Hash.new(0)
mappings = []
File.open( "/proc/#{pid}/smaps" ) do |smaps|
map_lines = []
loop do
break if smaps.eof?
line = smaps.readline.strip
case line
when /\w+:\s+/
map_lines << line
when /[0-9a-f]+:[0-9a-f]+\s+/
if map_lines.size > 0 then
mappings << consume_mapping( map_lines, totals )
end
map_lines.clear
map_lines << line
else
break
end
end
end
# http://rubyforge.org/snippet/download.php?type=snippet&id=511
def format_number( n )
n.to_s.gsub(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2')
end
def get_commandline( pid )
commandline = IO.read( "/proc/#{pid}/cmdline" ).split("\0")
if commandline.first =~ /java$/ then
loop { break if commandline.shift == "-jar" }
return "[java] #{commandline.shift}"
end
return commandline.join(' ')
end
puts "#{"Process:".ljust(20)} #{pid}"
puts "#{"Command Line:".ljust(20)} #{get_commandline(pid)}"
puts "Memory Summary:"
totals.keys.sort.each do |k|
puts " #{k.ljust(20)} #{format_number( totals[k] ).rjust(12)} kB"
end
#!/usr/bin/env ruby
#------------------------------------------------------------------------------
# Print out the processes that have swapped out regions of memory.
#
# Reference:
# http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt#361
#
# Example:
# #./swapstats.rb | head -3
# 4386 1,719,792 kB /usr/bin/mongod -f /etc/mongo/mongod.conf
# 5579 1,156,320 kB [java] /var/lib/term/term_service/start.jar
# 1863 618,072 kB [java] /var/lib/savi/savi/start.jar
#------------------------------------------------------------------------------
def get_pids
Dir.entries("/proc").grep(/^\d+$/)
end
def swap_total( pid )
total = 0
File.open( "/proc/#{pid}/smaps" ) do |f|
f.each_line do |line|
if line =~ /Swap:\s+(\d+)\s+/
total += Float($1).to_i
end
end
end
return total
end
# http://rubyforge.org/snippet/download.php?type=snippet&id=511
def format_number( n )
n.to_s.gsub(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2')
end
def get_commandline( pid )
commandline = IO.read( "/proc/#{pid}/cmdline" ).split("\0")
if commandline.first =~ /java$/ then
loop { break if commandline.shift == "-jar" }
return "[java] #{commandline.shift}"
end
return commandline.join(' ')
end
class SwapInfo
attr_reader :pid
attr_reader :commandline
attr_reader :swap
def initialize( pid, commandline, swap )
@pid = pid
@commandline = commandline
@swap = swap
end
end
swaps = []
get_pids.each do |pid|
begin
swap = swap_total( pid )
if swap > 0 then
swaps << SwapInfo.new( pid, get_commandline( pid ), swap )
end
rescue => e
end
end
swaps.sort_by { |s| s.swap }.reverse.each do |swap|
puts "#{swap.pid.to_s.rjust(6)} #{format_number(swap.swap).rjust(12)} kB #{swap.commandline}"
end
@kenn
Copy link

kenn commented Feb 12, 2013

I got a NoMethod error at https://gist.github.com/copiousfreetime/1366013#file-memstats-rb-L73

it should be

self.send( "#{field}=", value ) if respond_to? "#{field}="

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment