public
Last active

  • Download Gist
memstats.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
#!/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(':','')
return if field == 'vmflags'
value = Float(parts[1]).to_i
self.send( "#{field}=", value ) if respond_to? "#{field}="
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
 
abort 'usage: memstats [pid]' unless ARGV.first
pid = ARGV.shift.to_i
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

Looks like the script hangs on line 121 if you run it against a java application that doesn't have a -jar parameter.

This is really cool, would you mind making it compatible with shard ? All you'd need to do is rename the file to memstats.shard.rb

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.