1 | /* |
2 | KSysGuard, the KDE System Guard |
3 | |
4 | Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org> |
5 | |
6 | This program is free software; you can redistribute it and/or |
7 | modify it under the terms of version 2 of the GNU General Public |
8 | License as published by the Free Software Foundation. |
9 | |
10 | This program is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | GNU General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | */ |
19 | /* |
20 | * stat.c is used to read from /proc/[pid]/stat |
21 | */ |
22 | |
23 | #include <sys/types.h> |
24 | #include <sys/stat.h> |
25 | #include <sys/time.h> |
26 | #include <fcntl.h> |
27 | #include <unistd.h> |
28 | #include <stdio.h> |
29 | #include <stdlib.h> |
30 | #include <string.h> |
31 | #include <ctype.h> |
32 | |
33 | #include "Command.h" |
34 | #include "ksysguardd.h" |
35 | |
36 | #include "stat.h" |
37 | |
38 | typedef struct { |
39 | /* A CPU can be loaded with user processes, reniced processes and |
40 | * system processes. Unused processing time is called idle load. |
41 | * These variable store the percentage of each load type. */ |
42 | float userLoad; |
43 | float niceLoad; |
44 | float sysLoad; |
45 | float idleLoad; |
46 | float waitLoad; |
47 | |
48 | /* To calculate the loads we need to remember the tick values for each |
49 | * load type. */ |
50 | unsigned long userTicks; |
51 | unsigned long niceTicks; |
52 | unsigned long sysTicks; |
53 | unsigned long idleTicks; |
54 | unsigned long waitTicks; |
55 | } CPULoadInfo; |
56 | |
57 | typedef struct { |
58 | unsigned long delta; |
59 | unsigned long old; |
60 | } DiskLoadSample; |
61 | |
62 | typedef struct { |
63 | /* 5 types of samples are taken: |
64 | total, rio, wio, rBlk, wBlk */ |
65 | DiskLoadSample s[ 5 ]; |
66 | } DiskLoadInfo; |
67 | |
68 | typedef struct DiskIOInfo { |
69 | int major; |
70 | int minor; |
71 | char* devname; |
72 | |
73 | int alive; |
74 | DiskLoadSample total; |
75 | DiskLoadSample rio; |
76 | DiskLoadSample wio; |
77 | DiskLoadSample rblk; |
78 | DiskLoadSample wblk; |
79 | struct DiskIOInfo* next; |
80 | } DiskIOInfo; |
81 | |
82 | #define DISKDEVNAMELEN 16 |
83 | |
84 | static int StatDirty = 0; |
85 | |
86 | /* We have observed deviations of up to 5% in the accuracy of the timer |
87 | * interrupts. So we try to measure the interrupt interval and use this |
88 | * value to calculate timing dependant values. */ |
89 | static float timeInterval = 0; |
90 | static struct timeval lastSampling; |
91 | static struct timeval currSampling; |
92 | static struct SensorModul* StatSM; |
93 | |
94 | static CPULoadInfo CPULoad; |
95 | static CPULoadInfo* SMPLoad = 0; |
96 | static unsigned CPUCount = 0; |
97 | static DiskLoadInfo* DiskLoad = 0; |
98 | static unsigned DiskCount = 0; |
99 | static DiskIOInfo* DiskIO = 0; |
100 | static unsigned long PageIn = 0; |
101 | static unsigned long OldPageIn = 0; |
102 | static unsigned long PageOut = 0; |
103 | static unsigned long OldPageOut = 0; |
104 | static unsigned long Ctxt = 0; |
105 | static unsigned long OldCtxt = 0; |
106 | static unsigned int NumOfInts = 0; |
107 | static unsigned long* OldIntr = 0; |
108 | static unsigned long* Intr = 0; |
109 | |
110 | static int initStatDisk( char* tag, char* buf, const char* label, const char* shortLabel, |
111 | int idx, cmdExecutor ex, cmdExecutor iq ); |
112 | static void updateCPULoad( const char* line, CPULoadInfo* load ); |
113 | static int process24Disk( char* tag, char* buf, const char* label, int idx ); |
114 | static void processStat( void ); |
115 | static int process24DiskIO( const char* buf ); |
116 | static void cleanup24DiskList( void ); |
117 | |
118 | static int initStatDisk( char* tag, char* buf, const char* label, |
119 | const char* shortLabel, int idx, cmdExecutor ex, cmdExecutor iq ) |
120 | { |
121 | char sensorName[ 128 ]; |
122 | |
123 | gettimeofday( &lastSampling, 0 ); |
124 | |
125 | if ( strcmp( label, tag ) == 0 ) { |
126 | unsigned int i; |
127 | buf = buf + strlen( label ) + 1; |
128 | |
129 | for ( i = 0; i < DiskCount; ++i ) { |
130 | sscanf( buf, "%lu" , &DiskLoad[ i ].s[ idx ].old ); |
131 | while ( *buf && isblank( *buf++ ) ); |
132 | while ( *buf && isdigit( *buf++ ) ); |
133 | sprintf( sensorName, "disk/disk%d/%s" , i, shortLabel ); |
134 | registerMonitor( sensorName, "float" , ex, iq, StatSM ); |
135 | } |
136 | |
137 | return 1; |
138 | } |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | /** |
144 | * updateCPULoad |
145 | * |
146 | * Parses the total cpu status line from /proc/stat |
147 | */ |
148 | static void updateCPULoad( const char* line, CPULoadInfo* load ) { |
149 | unsigned long currUserTicks, currSysTicks, currNiceTicks; |
150 | unsigned long currIdleTicks, currWaitTicks, totalTicks; |
151 | |
152 | if(sscanf( line, "%*s %lu %lu %lu %lu %lu" , &currUserTicks, &currNiceTicks, |
153 | &currSysTicks, &currIdleTicks, &currWaitTicks ) != 5) { |
154 | return; |
155 | } |
156 | |
157 | totalTicks = ( currUserTicks - load->userTicks ) + |
158 | ( currSysTicks - load->sysTicks ) + |
159 | ( currNiceTicks - load->niceTicks ) + |
160 | ( currIdleTicks - load->idleTicks ) + |
161 | ( currWaitTicks - load->waitTicks ); |
162 | |
163 | if ( totalTicks > 10 ) { |
164 | load->userLoad = ( 100.0 * ( currUserTicks - load->userTicks ) ) / totalTicks; |
165 | load->sysLoad = ( 100.0 * ( currSysTicks - load->sysTicks ) ) / totalTicks; |
166 | load->niceLoad = ( 100.0 * ( currNiceTicks - load->niceTicks ) ) / totalTicks; |
167 | load->idleLoad = ( 100.0 * ( currIdleTicks - load->idleTicks ) ) / totalTicks; |
168 | load->waitLoad = ( 100.0 * ( currWaitTicks - load->waitTicks ) ) / totalTicks; |
169 | } |
170 | else |
171 | load->userLoad = load->sysLoad = load->niceLoad = load->idleLoad = load->waitLoad = 0.0; |
172 | |
173 | load->userTicks = currUserTicks; |
174 | load->sysTicks = currSysTicks; |
175 | load->niceTicks = currNiceTicks; |
176 | load->idleTicks = currIdleTicks; |
177 | load->waitTicks = currWaitTicks; |
178 | } |
179 | |
180 | static int process24Disk( char* tag, char* buf, const char* label, int idx ) { |
181 | if ( strcmp( label, tag ) == 0 ) { |
182 | unsigned long val; |
183 | unsigned int i; |
184 | buf = buf + strlen( label ) + 1; |
185 | |
186 | for ( i = 0; i < DiskCount; ++i ) { |
187 | sscanf( buf, "%lu" , &val ); |
188 | while ( *buf && isblank( *buf++ ) ); |
189 | while ( *buf && isdigit( *buf++ ) ); |
190 | DiskLoad[ i ].s[ idx ].delta = val - DiskLoad[ i ].s[ idx ].old; |
191 | DiskLoad[ i ].s[ idx ].old = val; |
192 | } |
193 | |
194 | return 1; |
195 | } |
196 | |
197 | return 0; |
198 | } |
199 | |
200 | static int process24DiskIO( const char* buf ) { |
201 | /* Process disk_io lines as provided by 2.4.x kernels. |
202 | * disk_io: (2,0):(3,3,6,0,0) (3,0):(1413012,511622,12155382,901390,26486215) */ |
203 | int major, minor; |
204 | unsigned long total, rblk, rio, wblk, wio; |
205 | DiskIOInfo* ptr = DiskIO; |
206 | DiskIOInfo* last = 0; |
207 | char sensorName[ 128 ]; |
208 | const char* p; |
209 | |
210 | p = buf + strlen( "disk_io: " ); |
211 | while ( p && *p ) { |
212 | if ( sscanf( p, "(%d,%d):(%lu,%lu,%lu,%lu,%lu)" , &major, &minor, |
213 | &total, &rio, &rblk, &wio, &wblk ) != 7 ) |
214 | return -1; |
215 | |
216 | last = 0; |
217 | ptr = DiskIO; |
218 | while ( ptr ) { |
219 | if ( ptr->major == major && ptr->minor == minor ) { |
220 | /* The IO device has already been registered. */ |
221 | ptr->total.delta = total - ptr->total.old; |
222 | ptr->total.old = total; |
223 | ptr->rio.delta = rio - ptr->rio.old; |
224 | ptr->rio.old = rio; |
225 | ptr->wio.delta = wio - ptr->wio.old; |
226 | ptr->wio.old = wio; |
227 | ptr->rblk.delta = rblk - ptr->rblk.old; |
228 | ptr->rblk.old = rblk; |
229 | ptr->wblk.delta = wblk - ptr->wblk.old; |
230 | ptr->wblk.old = wblk; |
231 | ptr->alive = 1; |
232 | break; |
233 | } |
234 | |
235 | last = ptr; |
236 | ptr = ptr->next; |
237 | } |
238 | |
239 | if ( !ptr ) { |
240 | /* The IO device has not been registered yet. We need to add it. */ |
241 | ptr = (DiskIOInfo*)malloc( sizeof( DiskIOInfo ) ); |
242 | ptr->major = major; |
243 | ptr->minor = minor; |
244 | |
245 | /* 2.6 gives us a nice device name. On 2.4 we get nothing */ |
246 | ptr->devname = (char *)malloc( DISKDEVNAMELEN ); |
247 | memset( ptr->devname, 0, DISKDEVNAMELEN ); |
248 | |
249 | ptr->total.delta = 0; |
250 | ptr->total.old = total; |
251 | ptr->rio.delta = 0; |
252 | ptr->rio.old = rio; |
253 | ptr->wio.delta = 0; |
254 | ptr->wio.old = wio; |
255 | ptr->rblk.delta = 0; |
256 | ptr->rblk.old = rblk; |
257 | ptr->wblk.delta = 0; |
258 | ptr->wblk.old = wblk; |
259 | ptr->alive = 1; |
260 | ptr->next = 0; |
261 | if ( last ) { |
262 | /* Append new entry at end of list. */ |
263 | last->next = ptr; |
264 | } |
265 | else { |
266 | /* List is empty, so we insert the fist element into the list. */ |
267 | DiskIO = ptr; |
268 | } |
269 | |
270 | sprintf( sensorName, "disk/%s_(%d:%d)24/total" , ptr->devname, major, minor ); |
271 | registerMonitor( sensorName, "float" , print24DiskIO, print24DiskIOInfo, StatSM ); |
272 | sprintf( sensorName, "disk/%s_(%d:%d)24/rio" , ptr->devname, major, minor ); |
273 | registerMonitor( sensorName, "float" , print24DiskIO, print24DiskIOInfo, StatSM ); |
274 | sprintf( sensorName, "disk/%s_(%d:%d)24/wio" , ptr->devname, major, minor ); |
275 | registerMonitor( sensorName, "float" , print24DiskIO, print24DiskIOInfo, StatSM ); |
276 | sprintf( sensorName, "disk/%s_(%d:%d)24/rblk" , ptr->devname, major, minor ); |
277 | registerMonitor( sensorName, "float" , print24DiskIO, print24DiskIOInfo, StatSM ); |
278 | sprintf( sensorName, "disk/%s_(%d:%d)24/wblk" , ptr->devname, major, minor ); |
279 | registerMonitor( sensorName, "float" , print24DiskIO, print24DiskIOInfo, StatSM ); |
280 | } |
281 | |
282 | /* Move p after the second ')'. We can safely assume that |
283 | * those two ')' exist. */ |
284 | p = strchr( p, ')' ) + 1; |
285 | p = strchr( p, ')' ) + 1; |
286 | if ( p && *p ) |
287 | p = strchr( p, '(' ); |
288 | } |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static void cleanup24DiskList( void ) { |
294 | DiskIOInfo* ptr = DiskIO; |
295 | DiskIOInfo* last = 0; |
296 | |
297 | while ( ptr ) { |
298 | if ( ptr->alive == 0 ) { |
299 | DiskIOInfo* newPtr; |
300 | char sensorName[ 128 ]; |
301 | |
302 | /* Disk device has disappeared. We have to remove it from |
303 | * the list and unregister the monitors. */ |
304 | sprintf( sensorName, "disk/%s_(%d:%d)24/total" , ptr->devname, ptr->major, ptr->minor ); |
305 | removeMonitor( sensorName ); |
306 | sprintf( sensorName, "disk/%s_(%d:%d)24/rio" , ptr->devname, ptr->major, ptr->minor ); |
307 | removeMonitor( sensorName ); |
308 | sprintf( sensorName, "disk/%s_(%d:%d)24/wio" , ptr->devname, ptr->major, ptr->minor ); |
309 | removeMonitor( sensorName ); |
310 | sprintf( sensorName, "disk/%s_(%d:%d)24/rblk" , ptr->devname, ptr->major, ptr->minor ); |
311 | removeMonitor( sensorName ); |
312 | sprintf( sensorName, "disk/%s_(%d:%d)24/wblk" , ptr->devname, ptr->major, ptr->minor ); |
313 | removeMonitor( sensorName ); |
314 | if ( last ) { |
315 | last->next = ptr->next; |
316 | newPtr = ptr->next; |
317 | } |
318 | else { |
319 | DiskIO = ptr->next; |
320 | newPtr = DiskIO; |
321 | last = 0; |
322 | } |
323 | |
324 | free ( ptr ); |
325 | ptr = newPtr; |
326 | } |
327 | else { |
328 | ptr->alive = 0; |
329 | last = ptr; |
330 | ptr = ptr->next; |
331 | } |
332 | } |
333 | } |
334 | |
335 | static void processStat( void ) { |
336 | char format[ 32 ]; |
337 | char tagFormat[ 16 ]; |
338 | char buf[ 1024 ]; |
339 | char tag[ 32 ]; |
340 | |
341 | sprintf( format, "%%%d[^\n]\n" , (int)sizeof( buf ) - 1 ); |
342 | sprintf( tagFormat, "%%%ds" , (int)sizeof( tag ) - 1 ); |
343 | |
344 | gettimeofday( &currSampling, 0 ); |
345 | StatDirty = 0; |
346 | |
347 | FILE *stat = fopen("/proc/stat" , "r" ); |
348 | if(!stat) { |
349 | print_error( "Cannot open file \'/proc/stat\'!\n" |
350 | "The kernel needs to be compiled with support\n" |
351 | "for /proc file system enabled!\n" ); |
352 | return; |
353 | } |
354 | |
355 | while ( fscanf( stat, format, buf ) == 1 ) { |
356 | buf[ sizeof( buf ) - 1 ] = '\0'; |
357 | sscanf( buf, tagFormat, tag ); |
358 | |
359 | if ( strcmp( "cpu" , tag ) == 0 ) { |
360 | /* Total CPU load */ |
361 | updateCPULoad( buf, &CPULoad ); |
362 | } |
363 | else if ( strncmp( "cpu" , tag, 3 ) == 0 ) { |
364 | /* Load for each SMP CPU */ |
365 | int id; |
366 | sscanf( tag + 3, "%d" , &id ); |
367 | updateCPULoad( buf, &SMPLoad[ id ] ); |
368 | } |
369 | else if ( process24Disk( tag, buf, "disk" , 0 ) ) { |
370 | } |
371 | else if ( process24Disk( tag, buf, "disk_rio" , 1 ) ) { |
372 | } |
373 | else if ( process24Disk( tag, buf, "disk_wio" , 2 ) ) { |
374 | } |
375 | else if ( process24Disk( tag, buf, "disk_rblk" , 3 ) ) { |
376 | } |
377 | else if ( process24Disk( tag, buf, "disk_wblk" , 4 ) ) { |
378 | } |
379 | else if ( strcmp( "disk_io:" , tag ) == 0 ) { |
380 | process24DiskIO( buf ); |
381 | } |
382 | else if ( strcmp( "page" , tag ) == 0 ) { |
383 | unsigned long v1, v2; |
384 | sscanf( buf + 5, "%lu %lu" , &v1, &v2 ); |
385 | PageIn = v1 - OldPageIn; |
386 | OldPageIn = v1; |
387 | PageOut = v2 - OldPageOut; |
388 | OldPageOut = v2; |
389 | } |
390 | else if ( strcmp( "intr" , tag ) == 0 ) { |
391 | unsigned int i = 0; |
392 | char* p = buf + 5; |
393 | |
394 | for ( i = 0; i < NumOfInts; i++ ) { |
395 | unsigned long val; |
396 | |
397 | sscanf( p, "%lu" , &val ); |
398 | Intr[ i ] = val - OldIntr[ i ]; |
399 | OldIntr[ i ] = val; |
400 | while ( *p && *p != ' ' ) |
401 | p++; |
402 | while ( *p && *p == ' ' ) |
403 | p++; |
404 | } |
405 | } else if ( strcmp( "ctxt" , tag ) == 0 ) { |
406 | unsigned long val; |
407 | |
408 | sscanf( buf + 5, "%lu" , &val ); |
409 | Ctxt = val - OldCtxt; |
410 | OldCtxt = val; |
411 | } |
412 | } |
413 | fclose(stat); |
414 | |
415 | /* Read Linux 2.5.x /proc/vmstat */ |
416 | stat = fopen("/proc/vmstat" , "r" ); |
417 | if(stat) { |
418 | while ( fscanf( stat, format, buf ) == 1 ) { |
419 | buf[ sizeof( buf ) - 1 ] = '\0'; |
420 | sscanf( buf, tagFormat, tag ); |
421 | |
422 | if ( strcmp( "pgpgin" , tag ) == 0 ) { |
423 | unsigned long v1; |
424 | sscanf( buf + 7, "%lu" , &v1 ); |
425 | PageIn = v1 - OldPageIn; |
426 | OldPageIn = v1; |
427 | } |
428 | else if ( strcmp( "pgpgout" , tag ) == 0 ) { |
429 | unsigned long v1; |
430 | sscanf( buf + 7, "%lu" , &v1 ); |
431 | PageOut = v1 - OldPageOut; |
432 | OldPageOut = v1; |
433 | } |
434 | } |
435 | fclose(stat); |
436 | } |
437 | |
438 | /* save exact time interval between this and the last read of /proc/stat */ |
439 | timeInterval = currSampling.tv_sec - lastSampling.tv_sec + |
440 | ( currSampling.tv_usec - lastSampling.tv_usec ) / 1000000.0; |
441 | lastSampling = currSampling; |
442 | |
443 | cleanup24DiskList(); |
444 | |
445 | } |
446 | |
447 | /* |
448 | ================================ public part ================================= |
449 | */ |
450 | |
451 | void initStat( struct SensorModul* sm ) { |
452 | /* The CPU load is calculated from the values in /proc/stat. The cpu |
453 | * entry contains 7 counters. These counters count the number of ticks |
454 | * the system has spend on user processes, system processes, nice |
455 | * processes, idle and IO-wait time, hard and soft interrupts. |
456 | * |
457 | * SMP systems will have cpu1 to cpuN lines right after the cpu info. The |
458 | * format is identical to cpu and reports the information for each cpu. |
459 | * Linux kernels <= 2.0 do not provide this information! |
460 | * |
461 | * The /proc/stat file looks like this: |
462 | * |
463 | * cpu <user> <nice> <system> <idling> <waiting> <hardinterrupt> <softinterrupt> |
464 | * disk 7797 0 0 0 |
465 | * disk_rio 6889 0 0 0 |
466 | * disk_wio 908 0 0 0 |
467 | * disk_rblk 13775 0 0 0 |
468 | * disk_wblk 1816 0 0 0 |
469 | * page 27575 1330 |
470 | * swap 1 0 |
471 | * intr 50444 38672 2557 0 0 0 0 2 0 2 0 0 3 1429 1 7778 0 |
472 | * ctxt 54155 |
473 | * btime 917379184 |
474 | * processes 347 |
475 | * |
476 | * Linux kernel >= 2.4.0 have one or more disk_io: lines instead of |
477 | * the disk_* lines. |
478 | * |
479 | * Linux kernel >= 2.6.x(?) have disk I/O stats in /proc/diskstats |
480 | * and no disk relevant lines are found in /proc/stat |
481 | */ |
482 | |
483 | char format[ 32 ]; |
484 | char tagFormat[ 16 ]; |
485 | char buf[ 1024 ]; |
486 | char tag[ 32 ]; |
487 | |
488 | StatSM = sm; |
489 | |
490 | sprintf( format, "%%%d[^\n]\n" , (int)sizeof( buf ) - 1 ); |
491 | sprintf( tagFormat, "%%%ds" , (int)sizeof( tag ) - 1 ); |
492 | |
493 | FILE *stat = fopen("/proc/stat" , "r" ); |
494 | if(!stat) { |
495 | print_error( "Cannot open file \'/proc/stat\'!\n" |
496 | "The kernel needs to be compiled with support\n" |
497 | "for /proc file system enabled!\n" ); |
498 | return; |
499 | } |
500 | while ( fscanf( stat, format, buf ) == 1 ) { |
501 | buf[ sizeof( buf ) - 1 ] = '\0'; |
502 | sscanf( buf, tagFormat, tag ); |
503 | |
504 | if ( strcmp( "cpu" , tag ) == 0 ) { |
505 | /* Total CPU load */ |
506 | registerMonitor( "cpu/system/user" , "float" , printCPUUser, printCPUUserInfo, StatSM ); |
507 | registerMonitor( "cpu/system/nice" , "float" , printCPUNice, printCPUNiceInfo, StatSM ); |
508 | registerMonitor( "cpu/system/sys" , "float" , printCPUSys, printCPUSysInfo, StatSM ); |
509 | registerMonitor( "cpu/system/TotalLoad" , "float" , printCPUTotalLoad, printCPUTotalLoadInfo, StatSM ); |
510 | registerMonitor( "cpu/system/idle" , "float" , printCPUIdle, printCPUIdleInfo, StatSM ); |
511 | registerMonitor( "cpu/system/wait" , "float" , printCPUWait, printCPUWaitInfo, StatSM ); |
512 | |
513 | /* Monitor names changed from kde3 => kde4. Remain compatible with legacy requests when possible. */ |
514 | registerLegacyMonitor( "cpu/user" , "float" , printCPUUser, printCPUUserInfo, StatSM ); |
515 | registerLegacyMonitor( "cpu/nice" , "float" , printCPUNice, printCPUNiceInfo, StatSM ); |
516 | registerLegacyMonitor( "cpu/sys" , "float" , printCPUSys, printCPUSysInfo, StatSM ); |
517 | registerLegacyMonitor( "cpu/TotalLoad" , "float" , printCPUTotalLoad, printCPUTotalLoadInfo, StatSM ); |
518 | registerLegacyMonitor( "cpu/idle" , "float" , printCPUIdle, printCPUIdleInfo, StatSM ); |
519 | registerLegacyMonitor( "cpu/wait" , "float" , printCPUWait, printCPUWaitInfo, StatSM ); |
520 | } |
521 | else if ( strncmp( "cpu" , tag, 3 ) == 0 ) { |
522 | char cmdName[ 24 ]; |
523 | /* Load for each SMP CPU */ |
524 | int id; |
525 | |
526 | sscanf( tag + 3, "%d" , &id ); |
527 | CPUCount++; |
528 | sprintf( cmdName, "cpu/cpu%d/user" , id ); |
529 | registerMonitor( cmdName, "float" , printCPUxUser, printCPUxUserInfo, StatSM ); |
530 | sprintf( cmdName, "cpu/cpu%d/nice" , id ); |
531 | registerMonitor( cmdName, "float" , printCPUxNice, printCPUxNiceInfo, StatSM ); |
532 | sprintf( cmdName, "cpu/cpu%d/sys" , id ); |
533 | registerMonitor( cmdName, "float" , printCPUxSys, printCPUxSysInfo, StatSM ); |
534 | sprintf( cmdName, "cpu/cpu%d/TotalLoad" , id ); |
535 | registerMonitor( cmdName, "float" , printCPUxTotalLoad, printCPUxTotalLoadInfo, StatSM ); |
536 | sprintf( cmdName, "cpu/cpu%d/idle" , id ); |
537 | registerMonitor( cmdName, "float" , printCPUxIdle, printCPUxIdleInfo, StatSM ); |
538 | sprintf( cmdName, "cpu/cpu%d/wait" , id ); |
539 | registerMonitor( cmdName, "float" , printCPUxWait, printCPUxWaitInfo, StatSM ); |
540 | } |
541 | else if ( strcmp( "disk" , tag ) == 0 ) { |
542 | unsigned long val; |
543 | char* b = buf + 5; |
544 | |
545 | /* Count the number of registered disks */ |
546 | for ( DiskCount = 0; *b && sscanf( b, "%lu" , &val ) == 1; DiskCount++ ) { |
547 | while ( *b && isblank( *b++ ) ); |
548 | while ( *b && isdigit( *b++ ) ); |
549 | } |
550 | |
551 | if ( DiskCount > 0 ) |
552 | DiskLoad = (DiskLoadInfo*)malloc( sizeof( DiskLoadInfo ) * DiskCount ); |
553 | |
554 | initStatDisk( tag, buf, "disk" , "disk" , 0, print24DiskTotal, print24DiskTotalInfo ); |
555 | } |
556 | else if ( initStatDisk( tag, buf, "disk_rio" , "rio" , 1, print24DiskRIO, print24DiskRIOInfo ) ); |
557 | else if ( initStatDisk( tag, buf, "disk_wio" , "wio" , 2, print24DiskWIO, print24DiskWIOInfo ) ); |
558 | else if ( initStatDisk( tag, buf, "disk_rblk" , "rblk" , 3, print24DiskRBlk, print24DiskRBlkInfo ) ); |
559 | else if ( initStatDisk( tag, buf, "disk_wblk" , "wblk" , 4, print24DiskWBlk, print24DiskWBlkInfo ) ); |
560 | else if ( strcmp( "disk_io:" , tag ) == 0 ) |
561 | process24DiskIO( buf ); |
562 | else if ( strcmp( "page" , tag ) == 0 ) { |
563 | sscanf( buf + 5, "%lu %lu" , &OldPageIn, &OldPageOut ); |
564 | registerMonitor( "cpu/pageIn" , "float" , printPageIn, printPageInInfo, StatSM ); |
565 | registerMonitor( "cpu/pageOut" , "float" , printPageOut, printPageOutInfo, StatSM ); |
566 | } |
567 | else if ( strcmp( "intr" , tag ) == 0 ) { |
568 | unsigned int i; |
569 | char cmdName[ 32 ]; |
570 | char* p = buf + 5; |
571 | |
572 | /* Count the number of listed values in the intr line. */ |
573 | NumOfInts = 0; |
574 | while ( *p ) |
575 | if ( *p++ == ' ' ) |
576 | NumOfInts++; |
577 | |
578 | /* It looks like anything above 24 is always 0. So let's just |
579 | * ignore this for the time being. */ |
580 | if ( NumOfInts > 25 ) |
581 | NumOfInts = 25; |
582 | OldIntr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) ); |
583 | Intr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) ); |
584 | i = 0; |
585 | p = buf + 5; |
586 | for ( i = 0; p && i < NumOfInts; i++ ) { |
587 | sscanf( p, "%lu" , &OldIntr[ i ] ); |
588 | while ( *p && *p != ' ' ) |
589 | p++; |
590 | while ( *p && *p == ' ' ) |
591 | p++; |
592 | sprintf( cmdName, "cpu/interrupts/int%02d" , i ); |
593 | registerMonitor( cmdName, "float" , printInterruptx, printInterruptxInfo, StatSM ); |
594 | } |
595 | } |
596 | else if ( strcmp( "ctxt" , tag ) == 0 ) { |
597 | sscanf( buf + 5, "%lu" , &OldCtxt ); |
598 | registerMonitor( "cpu/context" , "float" , printCtxt, printCtxtInfo, StatSM ); |
599 | } |
600 | } |
601 | fclose(stat); |
602 | |
603 | stat = fopen("/proc/vmstat" , "r" ); |
604 | if(!stat) { |
605 | print_error( "Cannot open file \'/proc/vmstat\'\n" ); |
606 | } else { |
607 | while ( fscanf( stat, format, buf ) == 1 ) { |
608 | buf[ sizeof( buf ) - 1 ] = '\0'; |
609 | sscanf( buf, tagFormat, tag ); |
610 | |
611 | if ( strcmp( "pgpgin" , tag ) == 0 ) { |
612 | sscanf( buf + 7, "%lu" , &OldPageIn ); |
613 | registerMonitor( "cpu/pageIn" , "float" , printPageIn, printPageInInfo, StatSM ); |
614 | } |
615 | else if ( strcmp( "pgpgout" , tag ) == 0 ) { |
616 | sscanf( buf + 7, "%lu" , &OldPageOut ); |
617 | registerMonitor( "cpu/pageOut" , "float" , printPageOut, printPageOutInfo, StatSM ); |
618 | } |
619 | } |
620 | fclose(stat); |
621 | } |
622 | if ( CPUCount > 0 ) |
623 | SMPLoad = (CPULoadInfo*)calloc( CPUCount, sizeof( CPULoadInfo ) ); |
624 | |
625 | /* Call processStat to eliminate initial peek values. */ |
626 | processStat(); |
627 | } |
628 | |
629 | void exitStat( void ) { |
630 | free( DiskLoad ); |
631 | DiskLoad = 0; |
632 | |
633 | free( SMPLoad ); |
634 | SMPLoad = 0; |
635 | |
636 | free( OldIntr ); |
637 | OldIntr = 0; |
638 | |
639 | free( Intr ); |
640 | Intr = 0; |
641 | |
642 | removeMonitor("cpu/system/user" ); |
643 | removeMonitor("cpu/system/nice" ); |
644 | removeMonitor("cpu/system/sys" ); |
645 | removeMonitor("cpu/system/idle" ); |
646 | |
647 | /* Todo: Dynamically registered monitors (per cpu, per disk) are not removed yet) */ |
648 | |
649 | /* These were registered as legacy monitors */ |
650 | removeMonitor("cpu/user" ); |
651 | removeMonitor("cpu/nice" ); |
652 | removeMonitor("cpu/sys" ); |
653 | removeMonitor("cpu/idle" ); |
654 | } |
655 | |
656 | int updateStat( void ) |
657 | { |
658 | StatDirty = 1; |
659 | return 0; |
660 | } |
661 | |
662 | void printCPUUser( const char* cmd ) { |
663 | (void)cmd; |
664 | |
665 | if ( StatDirty ) |
666 | processStat(); |
667 | |
668 | output( "%f\n" , CPULoad.userLoad ); |
669 | } |
670 | |
671 | void printCPUUserInfo( const char* cmd ) { |
672 | (void)cmd; |
673 | |
674 | output( "CPU User Load\t0\t100\t%%\n" ); |
675 | } |
676 | |
677 | void printCPUNice( const char* cmd ) { |
678 | (void)cmd; |
679 | |
680 | if ( StatDirty ) |
681 | processStat(); |
682 | |
683 | output( "%f\n" , CPULoad.niceLoad ); |
684 | } |
685 | |
686 | void printCPUNiceInfo( const char* cmd ) { |
687 | (void)cmd; |
688 | |
689 | output( "CPU Nice Load\t0\t100\t%%\n" ); |
690 | } |
691 | |
692 | void printCPUSys( const char* cmd ) { |
693 | (void)cmd; |
694 | |
695 | if ( StatDirty ) |
696 | processStat(); |
697 | |
698 | output( "%f\n" , CPULoad.sysLoad ); |
699 | } |
700 | |
701 | void printCPUSysInfo( const char* cmd ) { |
702 | (void)cmd; |
703 | |
704 | output( "CPU System Load\t0\t100\t%%\n" ); |
705 | } |
706 | |
707 | void printCPUTotalLoad( const char* cmd ) { |
708 | (void)cmd; |
709 | |
710 | if ( StatDirty ) |
711 | processStat(); |
712 | |
713 | output( "%f\n" , CPULoad.userLoad + CPULoad.sysLoad + CPULoad.niceLoad + CPULoad.waitLoad ); |
714 | } |
715 | |
716 | void printCPUTotalLoadInfo( const char* cmd ) { |
717 | (void)cmd; |
718 | |
719 | output( "CPU Total Load\t0\t100\t%%\n" ); |
720 | } |
721 | |
722 | void printCPUIdle( const char* cmd ) { |
723 | (void)cmd; |
724 | |
725 | if ( StatDirty ) |
726 | processStat(); |
727 | |
728 | output( "%f\n" , CPULoad.idleLoad ); |
729 | } |
730 | |
731 | void printCPUIdleInfo( const char* cmd ) { |
732 | (void)cmd; |
733 | |
734 | output( "CPU Idle Load\t0\t100\t%%\n" ); |
735 | } |
736 | |
737 | void printCPUWait( const char* cmd ) |
738 | { |
739 | (void)cmd; |
740 | |
741 | if ( StatDirty ) |
742 | processStat(); |
743 | |
744 | output( "%f\n" , CPULoad.waitLoad ); |
745 | } |
746 | |
747 | void printCPUWaitInfo( const char* cmd ) |
748 | { |
749 | (void)cmd; |
750 | output( "CPU Wait Load\t0\t100\t%%\n" ); |
751 | } |
752 | |
753 | void printCPUxUser( const char* cmd ) { |
754 | int id; |
755 | |
756 | if ( StatDirty ) |
757 | processStat(); |
758 | |
759 | sscanf( cmd + 7, "%d" , &id ); |
760 | output( "%f\n" , SMPLoad[ id ].userLoad ); |
761 | } |
762 | |
763 | void printCPUxUserInfo( const char* cmd ) { |
764 | int id; |
765 | |
766 | sscanf( cmd + 7, "%d" , &id ); |
767 | output( "CPU %d User Load\t0\t100\t%%\n" , id+1 ); |
768 | } |
769 | |
770 | void printCPUxNice( const char* cmd ) { |
771 | int id; |
772 | |
773 | if ( StatDirty ) |
774 | processStat(); |
775 | |
776 | sscanf( cmd + 7, "%d" , &id ); |
777 | output( "%f\n" , SMPLoad[ id ].niceLoad ); |
778 | } |
779 | |
780 | void printCPUxNiceInfo( const char* cmd ) { |
781 | int id; |
782 | |
783 | sscanf( cmd + 7, "%d" , &id ); |
784 | output( "CPU %d Nice Load\t0\t100\t%%\n" , id+1 ); |
785 | } |
786 | |
787 | void printCPUxSys( const char* cmd ) { |
788 | int id; |
789 | |
790 | if ( StatDirty ) |
791 | processStat(); |
792 | |
793 | sscanf( cmd + 7, "%d" , &id ); |
794 | output( "%f\n" , SMPLoad[ id ].sysLoad ); |
795 | } |
796 | |
797 | void printCPUxSysInfo( const char* cmd ) { |
798 | int id; |
799 | |
800 | sscanf( cmd + 7, "%d" , &id ); |
801 | output( "CPU %d System Load\t0\t100\t%%\n" , id+1 ); |
802 | } |
803 | |
804 | void printCPUxTotalLoad( const char* cmd ) { |
805 | int id; |
806 | |
807 | if ( StatDirty ) |
808 | processStat(); |
809 | |
810 | sscanf( cmd + 7, "%d" , &id ); |
811 | output( "%f\n" , SMPLoad[ id ].userLoad + SMPLoad[ id ].sysLoad + SMPLoad[ id ].niceLoad + SMPLoad[ id ].waitLoad ); |
812 | } |
813 | |
814 | void printCPUxTotalLoadInfo( const char* cmd ) { |
815 | int id; |
816 | |
817 | sscanf( cmd + 7, "%d" , &id ); |
818 | output( "CPU %d\t0\t100\t%%\n" , id+1 ); |
819 | } |
820 | |
821 | void printCPUxIdle( const char* cmd ) { |
822 | int id; |
823 | |
824 | if ( StatDirty ) |
825 | processStat(); |
826 | |
827 | sscanf( cmd + 7, "%d" , &id ); |
828 | output( "%f\n" , SMPLoad[ id ].idleLoad ); |
829 | } |
830 | |
831 | void printCPUxIdleInfo( const char* cmd ) { |
832 | int id; |
833 | |
834 | sscanf( cmd + 7, "%d" , &id ); |
835 | output( "CPU %d Idle Load\t0\t100\t%%\n" , id+1 ); |
836 | } |
837 | |
838 | void printCPUxWait( const char* cmd ) |
839 | { |
840 | int id; |
841 | |
842 | if ( StatDirty ) |
843 | processStat(); |
844 | |
845 | sscanf( cmd + 7, "%d" , &id ); |
846 | output( "%f\n" , SMPLoad[ id ].waitLoad ); |
847 | } |
848 | |
849 | void printCPUxWaitInfo( const char* cmd ) |
850 | { |
851 | int id; |
852 | |
853 | sscanf( cmd + 7, "%d" , &id ); |
854 | output( "CPU %d Wait Load\t0\t100\t%%\n" , id+1 ); |
855 | } |
856 | |
857 | void print24DiskTotal( const char* cmd ) { |
858 | int id; |
859 | |
860 | if ( StatDirty ) |
861 | processStat(); |
862 | |
863 | sscanf( cmd + 9, "%d" , &id ); |
864 | output( "%f\n" , (float)( DiskLoad[ id ].s[ 0 ].delta |
865 | / timeInterval ) ); |
866 | } |
867 | |
868 | void print24DiskTotalInfo( const char* cmd ) { |
869 | int id; |
870 | |
871 | sscanf( cmd + 9, "%d" , &id ); |
872 | output( "Disk %d Total Load\t0\t0\tKB/s\n" , id ); |
873 | } |
874 | |
875 | void print24DiskRIO( const char* cmd ) { |
876 | int id; |
877 | |
878 | if ( StatDirty ) |
879 | processStat(); |
880 | |
881 | sscanf( cmd + 9, "%d" , &id ); |
882 | output( "%f\n" , (float)( DiskLoad[ id ].s[ 1 ].delta |
883 | / timeInterval ) ); |
884 | } |
885 | |
886 | void print24DiskRIOInfo( const char* cmd ) { |
887 | int id; |
888 | |
889 | sscanf( cmd + 9, "%d" , &id ); |
890 | output( "Disk %d Read\t0\t0\tKB/s\n" , id ); |
891 | } |
892 | |
893 | void print24DiskWIO( const char* cmd ) { |
894 | int id; |
895 | |
896 | if ( StatDirty ) |
897 | processStat(); |
898 | |
899 | sscanf( cmd + 9, "%d" , &id ); |
900 | output( "%f\n" , (float)( DiskLoad[ id ].s[ 2 ].delta |
901 | / timeInterval ) ); |
902 | } |
903 | |
904 | void print24DiskWIOInfo( const char* cmd ) { |
905 | int id; |
906 | |
907 | sscanf( cmd + 9, "%d" , &id ); |
908 | output( "Disk %d Write\t0\t0\tKB/s\n" , id ); |
909 | } |
910 | |
911 | void print24DiskRBlk( const char* cmd ) { |
912 | int id; |
913 | |
914 | if ( StatDirty ) |
915 | processStat(); |
916 | |
917 | sscanf( cmd + 9, "%d" , &id ); |
918 | /* a block is 512 bytes or 1/2 kBytes */ |
919 | output( "%f\n" , (float)( DiskLoad[ id ].s[ 3 ].delta / timeInterval * 2 ) ); |
920 | } |
921 | |
922 | void print24DiskRBlkInfo( const char* cmd ) { |
923 | int id; |
924 | |
925 | sscanf( cmd + 9, "%d" , &id ); |
926 | output( "Disk %d Read Data\t0\t0\tKB/s\n" , id ); |
927 | } |
928 | |
929 | void print24DiskWBlk( const char* cmd ) { |
930 | int id; |
931 | |
932 | if ( StatDirty ) |
933 | processStat(); |
934 | |
935 | sscanf( cmd + 9, "%d" , &id ); |
936 | /* a block is 512 bytes or 1/2 kBytes */ |
937 | output( "%f\n" , (float)( DiskLoad[ id ].s[ 4 ].delta / timeInterval * 2 ) ); |
938 | } |
939 | |
940 | void print24DiskWBlkInfo( const char* cmd ) { |
941 | int id; |
942 | |
943 | sscanf( cmd + 9, "%d" , &id ); |
944 | output( "Disk %d Write Data\t0\t0\tKB/s\n" , id ); |
945 | } |
946 | |
947 | void printPageIn( const char* cmd ) { |
948 | (void)cmd; |
949 | |
950 | if ( StatDirty ) |
951 | processStat(); |
952 | |
953 | output( "%f\n" , (float)( PageIn / timeInterval ) ); |
954 | } |
955 | |
956 | void printPageInInfo( const char* cmd ) { |
957 | (void)cmd; |
958 | |
959 | output( "Paged in Pages\t0\t0\t1/s\n" ); |
960 | } |
961 | |
962 | void printPageOut( const char* cmd ) { |
963 | (void)cmd; |
964 | |
965 | if ( StatDirty ) |
966 | processStat(); |
967 | |
968 | output( "%f\n" , (float)( PageOut / timeInterval ) ); |
969 | } |
970 | |
971 | void printPageOutInfo( const char* cmd ) { |
972 | (void)cmd; |
973 | |
974 | output( "Paged out Pages\t0\t0\t1/s\n" ); |
975 | } |
976 | |
977 | void printInterruptx( const char* cmd ) { |
978 | int id; |
979 | |
980 | if ( StatDirty ) |
981 | processStat(); |
982 | |
983 | sscanf( cmd + strlen( "cpu/interrupts/int" ), "%d" , &id ); |
984 | output( "%f\n" , (float)( Intr[ id ] / timeInterval ) ); |
985 | } |
986 | |
987 | void printInterruptxInfo( const char* cmd ) { |
988 | int id; |
989 | |
990 | sscanf( cmd + strlen( "cpu/interrupt/int" ), "%d" , &id ); |
991 | output( "Interrupt %d\t0\t0\t1/s\n" , id ); |
992 | } |
993 | |
994 | void printCtxt( const char* cmd ) { |
995 | (void)cmd; |
996 | |
997 | if ( StatDirty ) |
998 | processStat(); |
999 | |
1000 | output( "%f\n" , (float)( Ctxt / timeInterval ) ); |
1001 | } |
1002 | |
1003 | void printCtxtInfo( const char* cmd ) { |
1004 | (void)cmd; |
1005 | |
1006 | output( "Context switches\t0\t0\t1/s\n" ); |
1007 | } |
1008 | |
1009 | void print24DiskIO( const char* cmd ) { |
1010 | int major, minor; |
1011 | char devname[DISKDEVNAMELEN]; |
1012 | char name[ 17 ]; |
1013 | DiskIOInfo* ptr; |
1014 | |
1015 | sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s" , devname, &major, &minor, name ); |
1016 | |
1017 | if ( StatDirty ) |
1018 | processStat(); |
1019 | |
1020 | ptr = DiskIO; |
1021 | while ( ptr && ( ptr->major != major || ptr->minor != minor ) ) |
1022 | ptr = ptr->next; |
1023 | |
1024 | if ( !ptr ) { |
1025 | print_error( "RECONFIGURE" ); |
1026 | output( "0\n" ); |
1027 | |
1028 | log_error( "Disk device disappeared" ); |
1029 | return; |
1030 | } |
1031 | |
1032 | if ( strcmp( name, "total" ) == 0 ) |
1033 | output( "%f\n" , (float)( ptr->total.delta / timeInterval ) ); |
1034 | else if ( strcmp( name, "rio" ) == 0 ) |
1035 | output( "%f\n" , (float)( ptr->rio.delta / timeInterval ) ); |
1036 | else if ( strcmp( name, "wio" ) == 0 ) |
1037 | output( "%f\n" , (float)( ptr->wio.delta / timeInterval ) ); |
1038 | else if ( strcmp( name, "rblk" ) == 0 ) |
1039 | output( "%f\n" , (float)( ptr->rblk.delta / ( timeInterval * 2 ) ) ); |
1040 | else if ( strcmp( name, "wblk" ) == 0 ) |
1041 | output( "%f\n" , (float)( ptr->wblk.delta / ( timeInterval * 2 ) ) ); |
1042 | else { |
1043 | output( "0\n" ); |
1044 | log_error( "Unknown disk device property \'%s\'" , name ); |
1045 | } |
1046 | } |
1047 | |
1048 | void print24DiskIOInfo( const char* cmd ) { |
1049 | int major, minor; |
1050 | char devname[DISKDEVNAMELEN]; |
1051 | char name[ 17 ]; |
1052 | DiskIOInfo* ptr = DiskIO; |
1053 | |
1054 | sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s" , devname, &major, &minor, name ); |
1055 | |
1056 | while ( ptr && ( ptr->major != major || ptr->minor != minor ) ) |
1057 | ptr = ptr->next; |
1058 | |
1059 | if ( !ptr ) { |
1060 | /* Disk device has disappeared. Print a dummy answer. */ |
1061 | output( "Dummy\t0\t0\t\n" ); |
1062 | return; |
1063 | } |
1064 | |
1065 | /* remove trailing '?' */ |
1066 | name[ strlen( name ) - 1 ] = '\0'; |
1067 | |
1068 | if ( strcmp( name, "total" ) == 0 ) |
1069 | output( "Total accesses device %s (%d:%d)\t0\t0\t1/s\n" , |
1070 | devname, major, minor ); |
1071 | else if ( strcmp( name, "rio" ) == 0 ) |
1072 | output( "Read data device %s (%d:%d)\t0\t0\t1/s\n" , |
1073 | devname, major, minor ); |
1074 | else if ( strcmp( name, "wio" ) == 0 ) |
1075 | output( "Write data device %s (%d:%d)\t0\t0\t1/s\n" , |
1076 | devname, major, minor ); |
1077 | else if ( strcmp( name, "rblk" ) == 0 ) |
1078 | output( "Read accesses device %s (%d:%d)\t0\t0\tKB/s\n" , |
1079 | devname, major, minor ); |
1080 | else if ( strcmp( name, "wblk" ) == 0 ) |
1081 | output( "Write accesses device %s (%d:%d)\t0\t0\tKB/s\n" , |
1082 | devname, major, minor ); |
1083 | else { |
1084 | output( "Dummy\t0\t0\t\n" ); |
1085 | log_error( "Request for unknown device property \'%s\'" , name ); |
1086 | } |
1087 | } |
1088 | |