]> git.proxmox.com Git - pve-common.git/blob - data/PVE/ProcFSTools.pm
fix bug in read_memory_usage
[pve-common.git] / data / PVE / ProcFSTools.pm
1 package PVE::ProcFSTools;
2
3 use strict;
4 use POSIX;
5 use Time::HiRes qw (gettimeofday);
6 use IO::File;
7 use PVE::Tools;
8
9 my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
10
11 my $cpuinfo;
12
13 sub read_cpuinfo {
14 my $fn = '/proc/cpuinfo';
15
16 return $cpuinfo if $cpuinfo;
17
18 my $res = {
19 user_hz => $clock_ticks,
20 model => 'unknown',
21 mhz => 0,
22 cpus => 1,
23 sockets => 1,
24 };
25
26 my $fh = IO::File->new ($fn, "r");
27 return $res if !$fh;
28
29 my $count = 0;
30 while (defined(my $line = <$fh>)) {
31 if ($line =~ m/^processor\s*:\s*\d+\s*$/i) {
32 $count++;
33 } elsif ($line =~ m/^model\s+name\s*:\s*(.*)\s*$/i) {
34 $res->{model} = $1 if $res->{model} eq 'unknown';
35 } elsif ($line =~ m/^cpu\s+MHz\s*:\s*(\d+\.\d+)\s*$/i) {
36 $res->{mhz} = $1 if !$res->{mhz};
37 } elsif ($line =~ m/^flags\s*:.*(vmx|svm)/) {
38 $res->{hvm} = 1; # Hardware Virtual Machine (Intel VT / AMD-V)
39 } elsif ($line =~ m/^physical id\s*:\s*(\d+)\s*$/i) {
40 my $sid = $1 + 1;
41 $res->{sockets} = $sid if $sid > $res->{sockets};
42 }
43
44 }
45
46 $res->{cpus} = $count;
47
48 $fh->close;
49
50 $cpuinfo = $res;
51
52 return $res;
53 }
54
55 sub read_proc_uptime {
56 my $ticks = shift;
57
58 my $line = PVE::Tools::file_read_firstline("/proc/uptime");
59 if ($line && $line =~ m|^(\d+\.\d+)\s+(\d+\.\d+)\s*$|) {
60 if ($ticks) {
61 return (int($1*$clock_ticks), int($2*$clock_ticks));
62 } else {
63 return (int($1), int($2));
64 }
65 }
66
67 return (0, 0);
68 }
69
70 sub read_loadavg {
71
72 my $line = PVE::Tools::file_read_firstline('/proc/loadavg');
73
74 if ($line =~ m|^(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+\d+/\d+\s+\d+\s*$|) {
75 return wantarray ? ($1, $2, $3) : $1;
76 }
77
78 return wantarray ? (0, 0, 0) : 0;
79 }
80
81 my $last_proc_stat;
82
83 sub read_proc_stat {
84 my $res = { user => 0, nice => 0, system => 0, idle => 0 , sum => 0};
85
86 my $cpucount = 0;
87
88 if (my $fh = IO::File->new ("/proc/stat", "r")) {
89 while (defined (my $line = <$fh>)) {
90 if ($line =~ m|^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s|) {
91 $res->{user} = $1;
92 $res->{nice} = $2;
93 $res->{system} = $3;
94 $res->{idle} = $4;
95 $res->{used} = $1+$2+$3;
96 $res->{iowait} = $5;
97 } elsif ($line =~ m|^cpu\d+\s|) {
98 $cpucount++;
99 }
100 }
101 $fh->close;
102 }
103
104 $cpucount = 1 if !$cpucount;
105
106 my $ctime = gettimeofday; # floating point time in seconds
107
108 $res->{ctime} = $ctime;
109 $res->{cpu} = 0;
110 $res->{wait} = 0;
111
112 $last_proc_stat = $res if !$last_proc_stat;
113
114 my $diff = ($ctime - $last_proc_stat->{ctime}) * $clock_ticks * $cpucount;
115
116 if ($diff > 1000) { # don't update too often
117 my $useddiff = $res->{used} - $last_proc_stat->{used};
118 $useddiff = $diff if $useddiff > $diff;
119 $res->{cpu} = $useddiff/$diff;
120 my $waitdiff = $res->{iowait} - $last_proc_stat->{iowait};
121 $waitdiff = $diff if $waitdiff > $diff;
122 $res->{wait} = $waitdiff/$diff;
123 $last_proc_stat = $res;
124 } else {
125 $res->{cpu} = $last_proc_stat->{cpu};
126 $res->{wait} = $last_proc_stat->{wait};
127 }
128
129 return $res;
130 }
131
132 sub read_proc_pid_stat {
133 my $pid = shift;
134
135 my $statstr = PVE::Tools::file_read_firstline("/proc/$pid/stat");
136
137 if ($statstr && $statstr =~ m/^$pid \(.*\) (\S) (-?\d+) -?\d+ -?\d+ -?\d+ -?\d+ \d+ \d+ \d+ \d+ \d+ (\d+) (\d+) (-?\d+) (-?\d+) -?\d+ -?\d+ -?\d+ 0 (\d+) (\d+) (-?\d+) \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ -?\d+ -?\d+ \d+ \d+ \d+/) {
138 return {
139 status => $1,
140 utime => $3,
141 stime => $4,
142 starttime => $7,
143 vsize => $8,
144 rss => $9 * 4096,
145 };
146 }
147
148 return undef;
149 }
150
151 sub check_process_running {
152 my ($pid, $pstart) = @_;
153
154 # note: waitpid only work for child processes, but not
155 # for processes spanned by other processes.
156 # kill(0, pid) return succes for zombies.
157 # So we read the status form /proc/$pid/stat instead
158
159 my $info = read_proc_pid_stat($pid);
160
161 return $info && (!$pstart || ($info->{starttime} eq $pstart)) && ($info->{status} ne 'Z') ? $info : undef;
162 }
163
164 sub read_proc_starttime {
165 my $pid = shift;
166
167 my $info = read_proc_pid_stat($pid);
168 return $info ? $info->{starttime} : 0;
169 }
170
171 sub read_meminfo {
172
173 my $res = {
174 memtotal => 0,
175 memfree => 0,
176 memused => 0,
177 swaptotal => 0,
178 swapfree => 0,
179 swapused => 0,
180 };
181
182 my $fh = IO::File->new ("/proc/meminfo", "r");
183 return $res if !$fh;
184
185 my $d = {};
186 while (my $line = <$fh>) {
187 if ($line =~ m/^(\S+):\s+(\d+)\s*kB/i) {
188 $d->{lc ($1)} = $2 * 1024;
189 }
190 }
191 close($fh);
192
193 $res->{memtotal} = $d->{memtotal};
194 $res->{memfree} = $d->{memfree} + $d->{buffers} + $d->{cached};
195 $res->{memused} = $res->{memtotal} - $res->{memfree};
196
197 $res->{swaptotal} = $d->{swaptotal};
198 $res->{swapfree} = $d->{swapfree};
199 $res->{swapused} = $res->{swaptotal} - $res->{swapfree};
200
201 return $res;
202 }
203
204 # memory usage of current process
205 sub read_memory_usage {
206
207 my $res = { size => 0, resident => 0, shared => 0 };
208
209 my $ps = 4096;
210
211 my $line = PVE::Tools::file_read_firstline("/proc/$$/statm");
212
213 if ($line =~ m/^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*/) {
214 $res->{size} = $1*$ps;
215 $res->{resident} = $2*$ps;
216 $res->{shared} = $3*$ps;
217 }
218
219 return $res;
220 }
221
222 sub read_proc_net_dev {
223
224 my $res = {};
225
226 my $fh = IO::File->new ("/proc/net/dev", "r");
227 return $res if !$fh;
228
229 while (defined (my $line = <$fh>)) {
230 if ($line =~ m/^\s*(.*):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)\s+/) {
231 $res->{$1} = {
232 receive => $2,
233 transmit => $3,
234 };
235 }
236 }
237
238 close($fh);
239
240 return $res;
241 }
242
243 1;