-
-
Save danp/5161455 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 16228 17155 17161 17164 | |
# pid cmd size rss shared_clean shared_dirty private_clean private_dirty swap pss | |
# 16228 unicorn master -c config/unicorn.rb 177472 49552 4724 22432 532 21864 0 28516 | |
# 17155 unicorn worker[0] -c config/unicorn.rb 181904 52008 3532 22416 0 26060 0 32175 | |
# 17161 unicorn worker[1] -c config/unicorn.rb 181764 51892 3532 22428 0 25932 0 32050 | |
# 17164 unicorn worker[2] -c config/unicorn.rb 181728 51928 3532 22416 0 25980 0 32096 | |
#------------------------------------------------------------------------------ | |
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 ) 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 | |
pids = ARGV.map {|a| Integer(a) } | |
abort "pid(s) required" if pids.empty? | |
totals = Hash.new {|h, k| h[k] = Hash.new {|h2, k2| h2[k2] = 0 } } | |
pids.each do |pid| | |
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 | |
consume_mapping( map_lines, totals[pid] ) | |
end | |
map_lines.clear | |
map_lines << line | |
else | |
break | |
end | |
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((%w(pid cmd) + Mapping::FIELDS).join("\t")) | |
pids.each do |pid| | |
puts(([pid, get_commandline(pid)] + Mapping::FIELDS.map {|f| totals[pid][f] }).join("\t")) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment