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