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