ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/gclib/scripts/meminfo.pl
Revision: 24
Committed: Tue Jul 26 21:46:39 2011 UTC (8 years, 1 month ago) by gpertea
File size: 7111 byte(s)
Log Message:
Line File contents
1 #!/usr/bin/perl
2 # meminfo.pl - show real memory use by processes, or as close to it as possible
3 # Copyright (C) 2009 Peter Willis <peterwwillis@yahoo.com>
4 #
5 # Stop guessing about how much memory is being used by processes on your system.
6 # Use this tool to find out for real how much real memory is being used.
7 # Requires a kernel with 'smaps' support (cat /proc/$$/smaps to check yours)
8 #
9 # Usage is fairly simple: pass it a pid on the command line and it will gather
10 # memory stats from them and report them.
11 #
12 # If you're trying to get the total memory of a multi-process application, just
13 # pass all the PIDs and look at the Private RSS. That will include all pages
14 # used by your application. It *won't* include shared memory from libraries which
15 # might be used by yet-another-program, but it'll give you a good minimum size
16 # of your app. To find all pages used by other programs, use --find-all-memory.
17 #
18 # If your kernel supports Pss stats, that is the best example of how much real
19 # memory a process is using (it's the private rss combined with 'its share' of
20 # shared memory, so add all the Pss's of your application's processes up and you
21 # will know how much memory they are using, or at least a little more accurately)
22 #
23 # You may need to be root to make this script really useful.
24
25
26 use strict;
27 $|=1;
28
29 if ( ! @ARGV ) {
30 die "Usage: $0 PID|TCOMM [..]\nPass a pid or a process executable name.\nTotals and reports on per-process memory usage.\n";
31 }
32
33 # Global Vars
34 my @smaps_names = qw(Size Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Swap);
35 my $MEG = 1024; # in kilobytes
36 my $GIG = $MEG*1024; # in kilobytes
37 my %procs;
38 my %procstat;
39 my $counted_pids = 0;
40
41 # Main
42
43 # Collect process information
44 foreach my $arg (@ARGV) {
45 my @pids = ( $arg );
46 if ( $arg !~ /^\d+$/ ) {
47 @pids = grep_pids($arg, \%procstat);
48 }
49
50 foreach my $pid (@pids) {
51 if ( !exists $procstat{$pid} ) {
52 $procstat{$pid} = getstat($pid);
53 }
54 if ( keys %{ $procstat{$pid} } ) {
55 my @a = calc_smaps_pid($pid);
56 if ( @a ) {
57 $procs{$pid} = \@a;
58 $counted_pids++;
59 }
60 }
61 }
62 }
63
64 # Summarize
65
66 summarize_pids(\%procs, \%procstat, \@ARGV);
67 my @totals = summarize_totals(\%procs);
68
69 format_print("\nSummary:\n\tTotal Size =>\t\t\t%f\n\tTotal Rss =>\t\t\t%f\n\tTotal Pss =>\t\t\t%f\n\tTotal Shared_Clean =>\t\t%f\n\tTotal Shared_Dirty =>\t\t%f\n\tTotal Private_Clean =>\t\t%f\n\tTotal Private_Dirty =>\t\t%f\n\tTotal Referenced =>\t\t%f\n\tTotal Swap =>\t\t\t%f\n", @totals);
70
71 # Make some guesstimations
72
73 # Shared Clean + Private Clean + Private Dirty (Shared Clean may still be an innacurate addition of multiple shared pages!) 1 - 3 4
74 #my $guessed_mem = ($totals[3] + $totals[5] + $totals[6]);
75 my $guessed_mem = ( $totals[1] - ($totals[3] + $totals[4]) );
76 print "\nAverages:\n";
77 format_print("\tTotal Rss - Shared Rss:\t\t%f\n\tNumber of PIDs:\t\t\t$counted_pids\n\tAverage memory per PID:\t\t%f\n", $guessed_mem, ($guessed_mem/$counted_pids));
78
79 exit 0;
80
81
82 # Subroutines
83
84
85 # By default searches *every pid on the system*
86 sub grep_pids {
87 my $arg = shift;
88 my $pstat_ref = shift;
89 my @foundpids;
90
91 opendir(DIR, "/proc/") || die "Error: let me read /proc/ !!!\n";
92 my @dirs = grep(/^\d+$/, readdir(DIR) );
93 closedir(DIR);
94
95 # Each of these dirs should be a pid, according to grep above
96 foreach my $dir (@dirs) {
97 if ( !exists $pstat_ref->{$dir} ) {
98 $pstat_ref->{$dir} = getstat($dir);
99 }
100 if ( keys %$pstat_ref and $pstat_ref->{$dir}->{'tcomm'} eq $arg ) {
101 push(@foundpids, $dir);
102 }
103 }
104 return @foundpids;
105 }
106
107
108 sub getstat {
109 my $pid = shift;
110
111 if ( ! open(FILE, "/proc/$pid/stat") ) {
112 print STDERR "Could not read stat for $pid: $!\n" if (exists $ENV{VERBOSE});
113 return( {} );
114 }
115 my @stat = split(/\s+/, <FILE>);
116 close(FILE);
117
118 my $hashref = { 'tcomm' => $stat[1], 'state' => $stat[2], 'ppid' => $stat[3], 'pgrp' => $stat[4], 'sid' => $stat[5] };
119 $hashref->{'tcomm'} =~ tr/()//d;
120 return $hashref;
121 }
122
123
124 sub summarize_pids {
125 my $procs_ref = shift;
126 my $pstat_ref = shift;
127 my $pids = shift;
128
129 foreach my $pid (keys %$procs_ref) {
130 print "Pid $pid ($pstat_ref->{$pid}->{tcomm}) :\n";
131 for ( my $i=0 ; $i < @{$procs_ref->{$pid}} ; $i++ ) {
132 format_print("\t$smaps_names[$i] => %f\n", $procs_ref->{$pid}->[$i]);
133 }
134 }
135 }
136
137 # Summarize all proc totals
138 sub summarize_totals {
139 my $procs = shift;
140 my ($size, $rss, $pss, $shared_clean, $shared_dirty, $private_clean, $private_dirty, $referenced, $swap) = ( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
141 foreach my $proc ( keys %$procs ) {
142 $size += $procs->{$proc}->[0];
143 $rss += $procs->{$proc}->[1];
144 $pss += $procs->{$proc}->[2];
145 $shared_clean += $procs->{$proc}->[3];
146 $shared_dirty += $procs->{$proc}->[4];
147 $private_clean += $procs->{$proc}->[5];
148 $private_dirty += $procs->{$proc}->[6];
149 $referenced += $procs->{$proc}->[7];
150 $swap += $procs->{$proc}->[8];
151 }
152 return ($size, $rss, $pss, $shared_clean, $shared_dirty, $private_clean, $private_dirty, $referenced, $swap);
153 }
154
155
156 # Yes, I know about Linux::Smaps.
157 sub calc_smaps_pid {
158 my $pid = shift;
159 my ($size, $rss, $pss, $shared_clean, $shared_dirty, $private_clean, $private_dirty, $referenced, $swap) = ( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
160
161 if ( ! open(FILE,"</proc/$pid/smaps") ) {
162 print STDERR "Could not read smaps for $pid: $!\n" if (exists $ENV{VERBOSE});
163 return ();
164 }
165 my @smaps = map { chomp $_; $_ } <FILE>;
166 close(FILE);
167
168 # FYI: by default these are all kilobytes, assume it'll always be that way...
169 for ( my $i=0; $i<@smaps; $i++ ) {
170 if ( $smaps[$i] =~ /^Size:\s+(\d+)/ ) {
171 $size += $1;
172 } elsif ( $smaps[$i] =~ /^Rss:\s+(\d+)/ ) {
173 $rss += $1;
174 } elsif ( $smaps[$i] =~ /^Pss:\s+(\d+)/ ) {
175 $pss += $1;
176 } elsif ( $smaps[$i] =~ /^Shared_Clean:\s+(\d+)/ ) {
177 $shared_clean += $1;
178 } elsif ( $smaps[$i] =~ /^Shared_Dirty:\s+(\d+)/ ) {
179 $shared_dirty += $1;
180 } elsif ( $smaps[$i] =~ /^Private_Clean:\s+(\d+)/ ) {
181 $private_clean += $1;
182 } elsif ( $smaps[$i] =~ /^Private_Dirty:\s+(\d+)/ ) {
183 $private_dirty += $1;
184 } elsif ( $smaps[$i] =~ /^Referenced:\s+(\d+)/ ) {
185 $referenced += $1;
186 } elsif ( $smaps[$i] =~ /^Swap:\s+(\d+)/ ) {
187 $swap += $1;
188 }
189 }
190
191 return ($size, $rss, $pss, $shared_clean, $shared_dirty, $private_clean, $private_dirty, $referenced, $swap);
192 }
193
194 # Hack to pretty-format any numbers automatically
195 sub format_print {
196 my $format = shift;
197 my @args = @_;
198 my $c = 0;
199
200 # I suck at formatting
201 $format =~ s<\%f>| $args[$c] > $GIG ? sprintf("%.4G GB",$args[$c++]/1024/1024) : $args[$c] > $MEG ? sprintf("%.4G MB",$args[$c++]/1024) : sprintf("%.4G KB",$args[$c++]) |eg;
202
203 print $format;
204 }

Properties

Name Value
svn:executable *