Test Setup Failed
Push — master ( a68822...5c8348 )
by Steven
01:26
created

Mapping.parse_first_line()   A

Complexity

Conditions 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
1
#!/usr/bin/env ruby
2
# from: https://gist.github.com/kenn/5105061/raw/ac7ebc6be7008c35b72560cc4e05b7cc14eb4919/memstats.rb
3
4
#------------------------------------------------------------------------------
5
# Aggregate Print useful information from /proc/[pid]/smaps
6
#
7
# pss  - Roughly the amount of memory that is "really" being used by the pid
8
# swap - Amount of swap this process is currently using
9
#
10
# Reference:
11
#  http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt#361
12
#
13
# Example:
14
#   # ./memstats.rb 4386
15
#   Process:             4386
16
#   Command Line:        /usr/bin/mongod -f /etc/mongo/mongod.conf
17
#   Memory Summary:
18
#     private_clean             107,132 kB
19
#     private_dirty           2,020,676 kB
20
#     pss                     2,127,860 kB
21
#     rss                     2,128,536 kB
22
#     shared_clean                  728 kB
23
#     shared_dirty                    0 kB
24
#     size                  149,281,668 kB
25
#     swap                    1,719,792 kB
26
#------------------------------------------------------------------------------
27
28
class Mapping
29
  FIELDS = %w[ size rss shared_clean shared_dirty private_clean private_dirty swap pss ]
30
  attr_reader :address_start
31
  attr_reader :address_end
32
  attr_reader :perms
33
  attr_reader :offset
34
  attr_reader :device_major
35
  attr_reader :device_minor
36
  attr_reader :inode
37
  attr_reader :region
38
39
  attr_accessor :size
40
  attr_accessor :rss
41
  attr_accessor :shared_clean
42
  attr_accessor :shared_dirty
43
  attr_accessor :private_dirty
44
  attr_accessor :private_clean
45
  attr_accessor :swap
46
  attr_accessor :pss
47
48
  def initialize(lines)
49
50
    FIELDS.each do |field|
51
      self.send("#{field}=", 0)
52
    end
53
54
    parse_first_line(lines.shift)
55
    lines.each do |l|
56
      parse_field_line(l)
57
    end
58
  end
59
60
  def parse_first_line(line)
61
    parts = line.strip.split
62
    @address_start, @address_end = parts[0].split("-")
63
    @perms = parts[1]
64
    @offset = parts[2]
65
    @device_major, @device_minor = parts[3].split(":")
66
    @inode = parts[4]
67
    @region = parts[5] || "anonymous"
68
  end
69
70
  def parse_field_line(line)
71
    parts = line.strip.split
72
    field = parts[0].downcase.sub(':', '')
73
    if respond_to? "#{field}="
74
      value = Float(parts[1]).to_i
75
      self.send("#{field}=", value)
76
    end
77
  end
78
end
79
80
def consume_mapping(map_lines, totals)
81
  m = Mapping.new(map_lines)
82
83
  Mapping::FIELDS.each do |field|
84
    totals[field] += m.send(field)
85
  end
86
  return m
87
end
88
89
def create_memstats_not_available(totals)
90
  Mapping::FIELDS.each do |field|
91
    totals[field] += Float::NAN
92
  end
93
end
94
95
abort 'usage: memstats [pid]' unless ARGV.first
96
pid = ARGV.shift.to_i
97
totals = Hash.new(0)
98
mappings = []
99
100
begin
101
  File.open("/proc/#{pid}/smaps") do |smaps|
102
103
    map_lines = []
104
105
    loop do
106
      break if smaps.eof?
107
      line = smaps.readline.strip
108
      case line
109
      when /\w+:\s+/
110
        map_lines << line
111
      when /[0-9a-f]+:[0-9a-f]+\s+/
112
        if map_lines.size > 0 then
113
          mappings << consume_mapping(map_lines, totals)
114
        end
115
        map_lines.clear
116
        map_lines << line
117
      else
118
        break
119
      end
120
    end
121
  end
122
rescue
123
  create_memstats_not_available(totals)
124
end
125
126
# http://rubyforge.org/snippet/download.php?type=snippet&id=511
127
def format_number(n)
128
  n.to_s.gsub(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/, '\1,\2')
129
end
130
131
def get_commandline(pid)
132
  commandline = IO.read("/proc/#{pid}/cmdline").split("\0")
133
  if commandline.first =~ /java$/ then
134
    loop { break if commandline.shift == "-jar" }
135
    return "[java] #{commandline.shift}"
136
  end
137
  return commandline.join(' ')
138
end
139
140
if ARGV.include? '--yaml'
141
  require 'yaml'
142
  puts Hash[*totals.map do |k, v|
143
    [k + '_kb', v]
144
  end.flatten].to_yaml
145
else
146
  puts "#{"Process:".ljust(20)} #{pid}"
147
  puts "#{"Command Line:".ljust(20)} #{get_commandline(pid)}"
148
  puts "Memory Summary:"
149
  totals.keys.sort.each do |k|
150
    puts "  #{k.ljust(20)} #{format_number(totals[k]).rjust(12)} kB"
151
  end
152
end
153