]>
Commit | Line | Data |
---|---|---|
e143e9d8 DM |
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 | ||
e143e9d8 DM |
13 | sub 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 | ||
56 | sub 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 | ||
71 | sub 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 | ||
82 | my $last_proc_stat; | |
83 | ||
84 | sub 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 | 133 | sub 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 | ||
152 | sub 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 | ||
165 | sub 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 | ||
172 | sub 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 | |
210 | sub 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 | ||
227 | sub 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 | ||
248 | 1; |