Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / ksysguard / ksysguardd / Linux / stat.c
blobcddc2ca3d0d48e4ba49b55f1e5994d388fcfacbc
1 /*
2 KSysGuard, the KDE System Guard
4 Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org>
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.
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.
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.
20 * stat.c is used to read from /proc/[pid]/stat
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>
33 #include "Command.h"
34 #include "ksysguardd.h"
36 #include "stat.h"
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;
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;
57 typedef struct {
58 unsigned long delta;
59 unsigned long old;
60 } DiskLoadSample;
62 typedef struct {
63 /* 5 types of samples are taken:
64 total, rio, wio, rBlk, wBlk */
65 DiskLoadSample s[ 5 ];
66 } DiskLoadInfo;
68 typedef struct DiskIOInfo {
69 int major;
70 int minor;
71 char* devname;
73 int alive;
74 DiskLoadSample total;
75 DiskLoadSample rio;
76 DiskLoadSample wio;
77 DiskLoadSample rblk;
78 DiskLoadSample wblk;
79 struct DiskIOInfo* next;
80 } DiskIOInfo;
82 #define STATBUFSIZE (32 * 1024)
83 #define DISKDEVNAMELEN 16
85 static char StatBuf[ STATBUFSIZE ];
86 static char VmStatBuf[ STATBUFSIZE ];
87 static int Dirty = 0;
89 /* We have observed deviations of up to 5% in the accuracy of the timer
90 * interrupts. So we try to measure the interrupt interval and use this
91 * value to calculate timing dependant values. */
92 static float timeInterval = 0;
93 static struct timeval lastSampling;
94 static struct timeval currSampling;
95 static struct SensorModul* StatSM;
97 static CPULoadInfo CPULoad;
98 static CPULoadInfo* SMPLoad = 0;
99 static unsigned CPUCount = 0;
100 static DiskLoadInfo* DiskLoad = 0;
101 static unsigned DiskCount = 0;
102 static DiskIOInfo* DiskIO = 0;
103 static unsigned long PageIn = 0;
104 static unsigned long OldPageIn = 0;
105 static unsigned long PageOut = 0;
106 static unsigned long OldPageOut = 0;
107 static unsigned long Ctxt = 0;
108 static unsigned long OldCtxt = 0;
109 static unsigned int NumOfInts = 0;
110 static unsigned long* OldIntr = 0;
111 static unsigned long* Intr = 0;
113 static int initStatDisk( char* tag, char* buf, const char* label, const char* shortLabel,
114 int idx, cmdExecutor ex, cmdExecutor iq );
115 static void updateCPULoad( const char* line, CPULoadInfo* load );
116 static int process24Disk( char* tag, char* buf, const char* label, int idx );
117 static void process24Stat( void );
118 static int process24DiskIO( const char* buf );
119 static void cleanup24DiskList( void );
121 static int initStatDisk( char* tag, char* buf, const char* label,
122 const char* shortLabel, int idx, cmdExecutor ex, cmdExecutor iq )
124 char sensorName[ 128 ];
126 gettimeofday( &lastSampling, 0 );
128 if ( strcmp( label, tag ) == 0 ) {
129 unsigned int i;
130 buf = buf + strlen( label ) + 1;
132 for ( i = 0; i < DiskCount; ++i ) {
133 sscanf( buf, "%lu", &DiskLoad[ i ].s[ idx ].old );
134 while ( *buf && isblank( *buf++ ) );
135 while ( *buf && isdigit( *buf++ ) );
136 sprintf( sensorName, "disk/disk%d/%s", i, shortLabel );
137 registerMonitor( sensorName, "float", ex, iq, StatSM );
140 return 1;
143 return 0;
147 * updateCPULoad
149 * Parses the total cpu status line from /proc/stat
151 static void updateCPULoad( const char* line, CPULoadInfo* load ) {
152 unsigned long currUserTicks, currSysTicks, currNiceTicks;
153 unsigned long currIdleTicks, currWaitTicks, totalTicks;
155 sscanf( line, "%*s %lu %lu %lu %lu %lu", &currUserTicks, &currNiceTicks,
156 &currSysTicks, &currIdleTicks, &currWaitTicks );
158 totalTicks = ( currUserTicks - load->userTicks ) +
159 ( currSysTicks - load->sysTicks ) +
160 ( currNiceTicks - load->niceTicks ) +
161 ( currIdleTicks - load->idleTicks ) +
162 ( currWaitTicks - load->waitTicks );
164 if ( totalTicks > 10 ) {
165 load->userLoad = ( 100.0 * ( currUserTicks - load->userTicks ) ) / totalTicks;
166 load->sysLoad = ( 100.0 * ( currSysTicks - load->sysTicks ) ) / totalTicks;
167 load->niceLoad = ( 100.0 * ( currNiceTicks - load->niceTicks ) ) / totalTicks;
168 load->idleLoad = ( 100.0 * ( currIdleTicks - load->idleTicks ) ) / totalTicks;
169 load->waitLoad = ( 100.0 * ( currWaitTicks - load->waitTicks ) ) / totalTicks;
171 else
172 load->userLoad = load->sysLoad = load->niceLoad = load->idleLoad = load->waitLoad = 0.0;
174 load->userTicks = currUserTicks;
175 load->sysTicks = currSysTicks;
176 load->niceTicks = currNiceTicks;
177 load->idleTicks = currIdleTicks;
178 load->waitTicks = currWaitTicks;
181 static int process24Disk( char* tag, char* buf, const char* label, int idx ) {
182 if ( strcmp( label, tag ) == 0 ) {
183 unsigned long val;
184 unsigned int i;
185 buf = buf + strlen( label ) + 1;
187 for ( i = 0; i < DiskCount; ++i ) {
188 sscanf( buf, "%lu", &val );
189 while ( *buf && isblank( *buf++ ) );
190 while ( *buf && isdigit( *buf++ ) );
191 DiskLoad[ i ].s[ idx ].delta = val - DiskLoad[ i ].s[ idx ].old;
192 DiskLoad[ i ].s[ idx ].old = val;
195 return 1;
198 return 0;
201 static int process24DiskIO( const char* buf ) {
202 /* Process disk_io lines as provided by 2.4.x kernels.
203 * disk_io: (2,0):(3,3,6,0,0) (3,0):(1413012,511622,12155382,901390,26486215) */
204 int major, minor;
205 unsigned long total, rblk, rio, wblk, wio;
206 DiskIOInfo* ptr = DiskIO;
207 DiskIOInfo* last = 0;
208 char sensorName[ 128 ];
209 const char* p;
211 p = buf + strlen( "disk_io: " );
212 while ( p && *p ) {
213 if ( sscanf( p, "(%d,%d):(%lu,%lu,%lu,%lu,%lu)", &major, &minor,
214 &total, &rio, &rblk, &wio, &wblk ) != 7 )
215 return -1;
217 last = 0;
218 ptr = DiskIO;
219 while ( ptr ) {
220 if ( ptr->major == major && ptr->minor == minor ) {
221 /* The IO device has already been registered. */
222 ptr->total.delta = total - ptr->total.old;
223 ptr->total.old = total;
224 ptr->rio.delta = rio - ptr->rio.old;
225 ptr->rio.old = rio;
226 ptr->wio.delta = wio - ptr->wio.old;
227 ptr->wio.old = wio;
228 ptr->rblk.delta = rblk - ptr->rblk.old;
229 ptr->rblk.old = rblk;
230 ptr->wblk.delta = wblk - ptr->wblk.old;
231 ptr->wblk.old = wblk;
232 ptr->alive = 1;
233 break;
236 last = ptr;
237 ptr = ptr->next;
240 if ( !ptr ) {
241 /* The IO device has not been registered yet. We need to add it. */
242 ptr = (DiskIOInfo*)malloc( sizeof( DiskIOInfo ) );
243 ptr->major = major;
244 ptr->minor = minor;
246 /* 2.6 gives us a nice device name. On 2.4 we get nothing */
247 ptr->devname = (char *)malloc( DISKDEVNAMELEN );
248 memset( ptr->devname, 0, DISKDEVNAMELEN );
250 ptr->total.delta = 0;
251 ptr->total.old = total;
252 ptr->rio.delta = 0;
253 ptr->rio.old = rio;
254 ptr->wio.delta = 0;
255 ptr->wio.old = wio;
256 ptr->rblk.delta = 0;
257 ptr->rblk.old = rblk;
258 ptr->wblk.delta = 0;
259 ptr->wblk.old = wblk;
260 ptr->alive = 1;
261 ptr->next = 0;
262 if ( last ) {
263 /* Append new entry at end of list. */
264 last->next = ptr;
266 else {
267 /* List is empty, so we insert the fist element into the list. */
268 DiskIO = ptr;
271 sprintf( sensorName, "disk/%s_(%d:%d)24/total", ptr->devname, major, minor );
272 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
273 sprintf( sensorName, "disk/%s_(%d:%d)24/rio", ptr->devname, major, minor );
274 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
275 sprintf( sensorName, "disk/%s_(%d:%d)24/wio", ptr->devname, major, minor );
276 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
277 sprintf( sensorName, "disk/%s_(%d:%d)24/rblk", ptr->devname, major, minor );
278 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
279 sprintf( sensorName, "disk/%s_(%d:%d)24/wblk", ptr->devname, major, minor );
280 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
283 /* Move p after the second ')'. We can safely assume that
284 * those two ')' exist. */
285 p = strchr( p, ')' ) + 1;
286 p = strchr( p, ')' ) + 1;
287 if ( p && *p )
288 p = strchr( p, '(' );
291 return 0;
294 static void cleanup24DiskList( void ) {
295 DiskIOInfo* ptr = DiskIO;
296 DiskIOInfo* last = 0;
298 while ( ptr ) {
299 if ( ptr->alive == 0 ) {
300 DiskIOInfo* newPtr;
301 char sensorName[ 128 ];
303 /* Disk device has disappeared. We have to remove it from
304 * the list and unregister the monitors. */
305 sprintf( sensorName, "disk/%s_(%d:%d)24/total", ptr->devname, ptr->major, ptr->minor );
306 removeMonitor( sensorName );
307 sprintf( sensorName, "disk/%s_(%d:%d)24/rio", ptr->devname, ptr->major, ptr->minor );
308 removeMonitor( sensorName );
309 sprintf( sensorName, "disk/%s_(%d:%d)24/wio", ptr->devname, ptr->major, ptr->minor );
310 removeMonitor( sensorName );
311 sprintf( sensorName, "disk/%s_(%d:%d)24/rblk", ptr->devname, ptr->major, ptr->minor );
312 removeMonitor( sensorName );
313 sprintf( sensorName, "disk/%s_(%d:%d)24/wblk", ptr->devname, ptr->major, ptr->minor );
314 removeMonitor( sensorName );
315 if ( last ) {
316 last->next = ptr->next;
317 newPtr = ptr->next;
319 else {
320 DiskIO = ptr->next;
321 newPtr = DiskIO;
322 last = 0;
325 free ( ptr );
326 ptr = newPtr;
328 else {
329 ptr->alive = 0;
330 last = ptr;
331 ptr = ptr->next;
336 static void process24Stat( void ) {
337 char format[ 32 ];
338 char tagFormat[ 16 ];
339 char buf[ 1024 ];
340 char tag[ 32 ];
341 char* statBufP = StatBuf;
342 char* vmstatBufP = VmStatBuf;
344 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
345 sprintf( tagFormat, "%%%ds", (int)sizeof( tag ) - 1 );
347 while ( sscanf( statBufP, format, buf ) == 1 ) {
348 buf[ sizeof( buf ) - 1 ] = '\0';
349 statBufP += strlen( buf ) + 1; /* move statBufP to next line */
350 sscanf( buf, tagFormat, tag );
352 if ( strcmp( "cpu", tag ) == 0 ) {
353 /* Total CPU load */
354 updateCPULoad( buf, &CPULoad );
356 else if ( strncmp( "cpu", tag, 3 ) == 0 ) {
357 /* Load for each SMP CPU */
358 int id;
359 sscanf( tag + 3, "%d", &id );
360 updateCPULoad( buf, &SMPLoad[ id ] );
362 else if ( process24Disk( tag, buf, "disk", 0 ) ) {
364 else if ( process24Disk( tag, buf, "disk_rio", 1 ) ) {
366 else if ( process24Disk( tag, buf, "disk_wio", 2 ) ) {
368 else if ( process24Disk( tag, buf, "disk_rblk", 3 ) ) {
370 else if ( process24Disk( tag, buf, "disk_wblk", 4 ) ) {
372 else if ( strcmp( "disk_io:", tag ) == 0 ) {
373 process24DiskIO( buf );
375 else if ( strcmp( "page", tag ) == 0 ) {
376 unsigned long v1, v2;
377 sscanf( buf + 5, "%lu %lu", &v1, &v2 );
378 PageIn = v1 - OldPageIn;
379 OldPageIn = v1;
380 PageOut = v2 - OldPageOut;
381 OldPageOut = v2;
383 else if ( strcmp( "intr", tag ) == 0 ) {
384 unsigned int i = 0;
385 char* p = buf + 5;
387 for ( i = 0; i < NumOfInts; i++ ) {
388 unsigned long val;
390 sscanf( p, "%lu", &val );
391 Intr[ i ] = val - OldIntr[ i ];
392 OldIntr[ i ] = val;
393 while ( *p && *p != ' ' )
394 p++;
395 while ( *p && *p == ' ' )
396 p++;
398 } else if ( strcmp( "ctxt", tag ) == 0 ) {
399 unsigned long val;
401 sscanf( buf + 5, "%lu", &val );
402 Ctxt = val - OldCtxt;
403 OldCtxt = val;
407 /* Read Linux 2.5.x /proc/vmstat */
408 while ( sscanf( vmstatBufP, format, buf ) == 1 ) {
409 buf[ sizeof( buf ) - 1 ] = '\0';
410 vmstatBufP += strlen( buf ) + 1; /* move vmstatBufP to next line */
411 sscanf( buf, tagFormat, tag );
413 if ( strcmp( "pgpgin", tag ) == 0 ) {
414 unsigned long v1;
415 sscanf( buf + 7, "%lu", &v1 );
416 PageIn = v1 - OldPageIn;
417 OldPageIn = v1;
419 else if ( strcmp( "pgpgout", tag ) == 0 ) {
420 unsigned long v1;
421 sscanf( buf + 7, "%lu", &v1 );
422 PageOut = v1 - OldPageOut;
423 OldPageOut = v1;
427 /* save exact time inverval between this and the last read of /proc/stat */
428 timeInterval = currSampling.tv_sec - lastSampling.tv_sec +
429 ( currSampling.tv_usec - lastSampling.tv_usec ) / 1000000.0;
430 lastSampling = currSampling;
432 cleanup24DiskList();
434 Dirty = 0;
438 ================================ public part =================================
441 void initStat( struct SensorModul* sm ) {
442 /* The CPU load is calculated from the values in /proc/stat. The cpu
443 * entry contains 7 counters. These counters count the number of ticks
444 * the system has spend on user processes, system processes, nice
445 * processes, idle and IO-wait time, hard and soft interrupts.
447 * SMP systems will have cpu1 to cpuN lines right after the cpu info. The
448 * format is identical to cpu and reports the information for each cpu.
449 * Linux kernels <= 2.0 do not provide this information!
451 * The /proc/stat file looks like this:
453 * cpu <user> <nice> <system> <idling> <waiting> <hardinterrupt> <softinterrupt>
454 * disk 7797 0 0 0
455 * disk_rio 6889 0 0 0
456 * disk_wio 908 0 0 0
457 * disk_rblk 13775 0 0 0
458 * disk_wblk 1816 0 0 0
459 * page 27575 1330
460 * swap 1 0
461 * intr 50444 38672 2557 0 0 0 0 2 0 2 0 0 3 1429 1 7778 0
462 * ctxt 54155
463 * btime 917379184
464 * processes 347
466 * Linux kernel >= 2.4.0 have one or more disk_io: lines instead of
467 * the disk_* lines.
469 * Linux kernel >= 2.6.x(?) have disk I/O stats in /proc/diskstats
470 * and no disk relevant lines are found in /proc/stat
473 char format[ 32 ];
474 char tagFormat[ 16 ];
475 char buf[ 1024 ];
476 char tag[ 32 ];
477 char* statBufP = StatBuf;
478 char* vmstatBufP = VmStatBuf;
480 StatSM = sm;
482 updateStat();
484 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
485 sprintf( tagFormat, "%%%ds", (int)sizeof( tag ) - 1 );
487 while ( sscanf( statBufP, format, buf ) == 1 ) {
488 buf[ sizeof( buf ) - 1 ] = '\0';
489 statBufP += strlen( buf ) + 1; /* move statBufP to next line */
490 sscanf( buf, tagFormat, tag );
492 if ( strcmp( "cpu", tag ) == 0 ) {
493 /* Total CPU load */
494 registerMonitor( "cpu/system/user", "float", printCPUUser, printCPUUserInfo, StatSM );
495 registerMonitor( "cpu/system/nice", "float", printCPUNice, printCPUNiceInfo, StatSM );
496 registerMonitor( "cpu/system/sys", "float", printCPUSys, printCPUSysInfo, StatSM );
497 registerMonitor( "cpu/system/TotalLoad", "float", printCPUTotalLoad, printCPUTotalLoadInfo, StatSM );
498 registerMonitor( "cpu/system/idle", "float", printCPUIdle, printCPUIdleInfo, StatSM );
499 registerMonitor( "cpu/system/wait", "float", printCPUWait, printCPUWaitInfo, StatSM );
501 /* Monitor names changed from kde3 => kde4. Remain compatible with legacy requests when possible. */
502 registerLegacyMonitor( "cpu/user", "float", printCPUUser, printCPUUserInfo, StatSM );
503 registerLegacyMonitor( "cpu/nice", "float", printCPUNice, printCPUNiceInfo, StatSM );
504 registerLegacyMonitor( "cpu/sys", "float", printCPUSys, printCPUSysInfo, StatSM );
505 registerLegacyMonitor( "cpu/TotalLoad", "float", printCPUTotalLoad, printCPUTotalLoadInfo, StatSM );
506 registerLegacyMonitor( "cpu/idle", "float", printCPUIdle, printCPUIdleInfo, StatSM );
507 registerLegacyMonitor( "cpu/wait", "float", printCPUWait, printCPUWaitInfo, StatSM );
509 else if ( strncmp( "cpu", tag, 3 ) == 0 ) {
510 char cmdName[ 24 ];
511 /* Load for each SMP CPU */
512 int id;
514 sscanf( tag + 3, "%d", &id );
515 CPUCount++;
516 sprintf( cmdName, "cpu/cpu%d/user", id );
517 registerMonitor( cmdName, "float", printCPUxUser, printCPUxUserInfo, StatSM );
518 sprintf( cmdName, "cpu/cpu%d/nice", id );
519 registerMonitor( cmdName, "float", printCPUxNice, printCPUxNiceInfo, StatSM );
520 sprintf( cmdName, "cpu/cpu%d/sys", id );
521 registerMonitor( cmdName, "float", printCPUxSys, printCPUxSysInfo, StatSM );
522 sprintf( cmdName, "cpu/cpu%d/TotalLoad", id );
523 registerMonitor( cmdName, "float", printCPUxTotalLoad, printCPUxTotalLoadInfo, StatSM );
524 sprintf( cmdName, "cpu/cpu%d/idle", id );
525 registerMonitor( cmdName, "float", printCPUxIdle, printCPUxIdleInfo, StatSM );
526 sprintf( cmdName, "cpu/cpu%d/wait", id );
527 registerMonitor( cmdName, "float", printCPUxWait, printCPUxWaitInfo, StatSM );
529 else if ( strcmp( "disk", tag ) == 0 ) {
530 unsigned long val;
531 char* b = buf + 5;
533 /* Count the number of registered disks */
534 for ( DiskCount = 0; *b && sscanf( b, "%lu", &val ) == 1; DiskCount++ ) {
535 while ( *b && isblank( *b++ ) );
536 while ( *b && isdigit( *b++ ) );
539 if ( DiskCount > 0 )
540 DiskLoad = (DiskLoadInfo*)malloc( sizeof( DiskLoadInfo ) * DiskCount );
542 initStatDisk( tag, buf, "disk", "disk", 0, print24DiskTotal, print24DiskTotalInfo );
544 else if ( initStatDisk( tag, buf, "disk_rio", "rio", 1, print24DiskRIO, print24DiskRIOInfo ) );
545 else if ( initStatDisk( tag, buf, "disk_wio", "wio", 2, print24DiskWIO, print24DiskWIOInfo ) );
546 else if ( initStatDisk( tag, buf, "disk_rblk", "rblk", 3, print24DiskRBlk, print24DiskRBlkInfo ) );
547 else if ( initStatDisk( tag, buf, "disk_wblk", "wblk", 4, print24DiskWBlk, print24DiskWBlkInfo ) );
548 else if ( strcmp( "disk_io:", tag ) == 0 )
549 process24DiskIO( buf );
550 else if ( strcmp( "page", tag ) == 0 ) {
551 sscanf( buf + 5, "%lu %lu", &OldPageIn, &OldPageOut );
552 registerMonitor( "cpu/pageIn", "float", printPageIn, printPageInInfo, StatSM );
553 registerMonitor( "cpu/pageOut", "float", printPageOut, printPageOutInfo, StatSM );
555 else if ( strcmp( "intr", tag ) == 0 ) {
556 unsigned int i;
557 char cmdName[ 32 ];
558 char* p = buf + 5;
560 /* Count the number of listed values in the intr line. */
561 NumOfInts = 0;
562 while ( *p )
563 if ( *p++ == ' ' )
564 NumOfInts++;
566 /* It looks like anything above 24 is always 0. So let's just
567 * ignore this for the time being. */
568 if ( NumOfInts > 25 )
569 NumOfInts = 25;
570 OldIntr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) );
571 Intr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) );
572 i = 0;
573 p = buf + 5;
574 for ( i = 0; p && i < NumOfInts; i++ ) {
575 sscanf( p, "%lu", &OldIntr[ i ] );
576 while ( *p && *p != ' ' )
577 p++;
578 while ( *p && *p == ' ' )
579 p++;
580 sprintf( cmdName, "cpu/interrupts/int%02d", i );
581 registerMonitor( cmdName, "float", printInterruptx, printInterruptxInfo, StatSM );
584 else if ( strcmp( "ctxt", tag ) == 0 ) {
585 sscanf( buf + 5, "%lu", &OldCtxt );
586 registerMonitor( "cpu/context", "float", printCtxt, printCtxtInfo, StatSM );
590 while ( sscanf( vmstatBufP, format, buf ) == 1 ) {
591 buf[ sizeof( buf ) - 1 ] = '\0';
592 vmstatBufP += strlen( buf ) + 1; /* move vmstatBufP to next line */
593 sscanf( buf, tagFormat, tag );
595 if ( strcmp( "pgpgin", tag ) == 0 ) {
596 sscanf( buf + 7, "%lu", &OldPageIn );
597 registerMonitor( "cpu/pageIn", "float", printPageIn, printPageInInfo, StatSM );
599 else if ( strcmp( "pgpgout", tag ) == 0 ) {
600 sscanf( buf + 7, "%lu", &OldPageOut );
601 registerMonitor( "cpu/pageOut", "float", printPageOut, printPageOutInfo, StatSM );
605 if ( CPUCount > 0 )
606 SMPLoad = (CPULoadInfo*)malloc( sizeof( CPULoadInfo ) * CPUCount );
608 /* Call process24Stat to eliminate initial peek values. */
609 process24Stat();
612 void exitStat( void ) {
613 free( DiskLoad );
614 DiskLoad = 0;
616 free( SMPLoad );
617 SMPLoad = 0;
619 free( OldIntr );
620 OldIntr = 0;
622 free( Intr );
623 Intr = 0;
625 removeMonitor("cpu/system/user");
626 removeMonitor("cpu/system/nice");
627 removeMonitor("cpu/system/sys");
628 removeMonitor("cpu/system/idle");
630 /* Todo: Dynamically registered monitors (per cpu, per disk) are not removed yet) */
632 /* These were registered as legacy monitors */
633 removeMonitor("cpu/user");
634 removeMonitor("cpu/nice");
635 removeMonitor("cpu/sys");
636 removeMonitor("cpu/idle");
639 int updateStat( void ) {
640 size_t n;
641 int fd;
643 gettimeofday( &currSampling, 0 );
644 Dirty = 1;
646 StatBuf[ 0 ] = '\0';
647 if ( ( fd = open( "/proc/stat", O_RDONLY ) ) < 0 ) {
648 print_error( "Cannot open file \'/proc/stat\'!\n"
649 "The kernel needs to be compiled with support\n"
650 "for /proc file system enabled!\n" );
652 return -1;
654 n = read( fd, StatBuf, STATBUFSIZE - 1 );
655 if ( n == STATBUFSIZE - 1 || n <= 0) {
656 log_error( "Internal buffer too small to read \'/proc/stat\'" );
658 close( fd );
659 return -1;
661 close( fd );
662 StatBuf[ n ] = '\0';
665 VmStatBuf[ 0 ] = '\0';
666 if ( ( fd = open( "/proc/vmstat", O_RDONLY ) ) < 0 )
667 return 0; /* failure is okay, only exists for Linux >= 2.5.x */
669 n = read( fd, VmStatBuf, STATBUFSIZE - 1 );
670 if ( n == STATBUFSIZE - 1 || n <= 0 ) {
671 log_error( "Internal buffer too small to read \'/proc/vmstat\'" );
673 close( fd );
674 return -1;
676 close( fd );
677 VmStatBuf[ n ] = '\0';
679 return 0;
682 void printCPUUser( const char* cmd ) {
683 (void)cmd;
685 if ( Dirty )
686 process24Stat();
688 fprintf( CurrentClient, "%f\n", CPULoad.userLoad );
691 void printCPUUserInfo( const char* cmd ) {
692 (void)cmd;
694 fprintf( CurrentClient, "CPU User Load\t0\t100\t%%\n" );
697 void printCPUNice( const char* cmd ) {
698 (void)cmd;
700 if ( Dirty )
701 process24Stat();
703 fprintf( CurrentClient, "%f\n", CPULoad.niceLoad );
706 void printCPUNiceInfo( const char* cmd ) {
707 (void)cmd;
709 fprintf( CurrentClient, "CPU Nice Load\t0\t100\t%%\n" );
712 void printCPUSys( const char* cmd ) {
713 (void)cmd;
715 if ( Dirty )
716 process24Stat();
718 fprintf( CurrentClient, "%f\n", CPULoad.sysLoad );
721 void printCPUSysInfo( const char* cmd ) {
722 (void)cmd;
724 fprintf( CurrentClient, "CPU System Load\t0\t100\t%%\n" );
727 void printCPUTotalLoad( const char* cmd ) {
728 (void)cmd;
730 if ( Dirty )
731 process24Stat();
733 fprintf( CurrentClient, "%f\n", CPULoad.userLoad + CPULoad.sysLoad + CPULoad.niceLoad + CPULoad.waitLoad );
736 void printCPUTotalLoadInfo( const char* cmd ) {
737 (void)cmd;
739 fprintf( CurrentClient, "CPU Total Load\t0\t100\t%%\n" );
742 void printCPUIdle( const char* cmd ) {
743 (void)cmd;
745 if ( Dirty )
746 process24Stat();
748 fprintf( CurrentClient, "%f\n", CPULoad.idleLoad );
751 void printCPUIdleInfo( const char* cmd ) {
752 (void)cmd;
754 fprintf( CurrentClient, "CPU Idle Load\t0\t100\t%%\n" );
757 void printCPUWait( const char* cmd )
759 (void)cmd;
761 if ( Dirty )
762 process24Stat();
764 fprintf( CurrentClient, "%f\n", CPULoad.waitLoad );
767 void printCPUWaitInfo( const char* cmd )
769 (void)cmd;
770 fprintf( CurrentClient, "CPU Wait Load\t0\t100\t%%\n" );
773 void printCPUxUser( const char* cmd ) {
774 int id;
776 if ( Dirty )
777 process24Stat();
779 sscanf( cmd + 7, "%d", &id );
780 fprintf( CurrentClient, "%f\n", SMPLoad[ id ].userLoad );
783 void printCPUxUserInfo( const char* cmd ) {
784 int id;
786 sscanf( cmd + 7, "%d", &id );
787 fprintf( CurrentClient, "CPU%d User Load\t0\t100\t%%\n", id );
790 void printCPUxNice( const char* cmd ) {
791 int id;
793 if ( Dirty )
794 process24Stat();
796 sscanf( cmd + 7, "%d", &id );
797 fprintf( CurrentClient, "%f\n", SMPLoad[ id ].niceLoad );
800 void printCPUxNiceInfo( const char* cmd ) {
801 int id;
803 sscanf( cmd + 7, "%d", &id );
804 fprintf( CurrentClient, "CPU%d Nice Load\t0\t100\t%%\n", id );
807 void printCPUxSys( const char* cmd ) {
808 int id;
810 if ( Dirty )
811 process24Stat();
813 sscanf( cmd + 7, "%d", &id );
814 fprintf( CurrentClient, "%f\n", SMPLoad[ id ].sysLoad );
817 void printCPUxSysInfo( const char* cmd ) {
818 int id;
820 sscanf( cmd + 7, "%d", &id );
821 fprintf( CurrentClient, "CPU%d System Load\t0\t100\t%%\n", id );
824 void printCPUxTotalLoad( const char* cmd ) {
825 int id;
827 if ( Dirty )
828 process24Stat();
830 sscanf( cmd + 7, "%d", &id );
831 fprintf( CurrentClient, "%f\n", SMPLoad[ id ].userLoad + SMPLoad[ id ].sysLoad + SMPLoad[ id ].niceLoad + SMPLoad[ id ].waitLoad );
834 void printCPUxTotalLoadInfo( const char* cmd ) {
835 int id;
837 sscanf( cmd + 7, "%d", &id );
838 fprintf( CurrentClient, "CPU%d Total Load\t0\t100\t%%\n", id );
841 void printCPUxIdle( const char* cmd ) {
842 int id;
844 if ( Dirty )
845 process24Stat();
847 sscanf( cmd + 7, "%d", &id );
848 fprintf( CurrentClient, "%f\n", SMPLoad[ id ].idleLoad );
851 void printCPUxIdleInfo( const char* cmd ) {
852 int id;
854 sscanf( cmd + 7, "%d", &id );
855 fprintf( CurrentClient, "CPU%d Idle Load\t0\t100\t%%\n", id );
858 void printCPUxWait( const char* cmd )
860 int id;
862 if ( Dirty )
863 process24Stat();
865 sscanf( cmd + 7, "%d", &id );
866 fprintf( CurrentClient, "%f\n", SMPLoad[ id ].waitLoad );
869 void printCPUxWaitInfo( const char* cmd )
871 int id;
873 sscanf( cmd + 7, "%d", &id );
874 fprintf( CurrentClient, "CPU%d Wait Load\t0\t100\t%%\n", id );
877 void print24DiskTotal( const char* cmd ) {
878 int id;
880 if ( Dirty )
881 process24Stat();
883 sscanf( cmd + 9, "%d", &id );
884 fprintf( CurrentClient, "%f\n", (float)( DiskLoad[ id ].s[ 0 ].delta
885 / timeInterval ) );
888 void print24DiskTotalInfo( const char* cmd ) {
889 int id;
891 sscanf( cmd + 9, "%d", &id );
892 fprintf( CurrentClient, "Disk%d Total Load\t0\t0\tkBytes/s\n", id );
895 void print24DiskRIO( const char* cmd ) {
896 int id;
898 if ( Dirty )
899 process24Stat();
901 sscanf( cmd + 9, "%d", &id );
902 fprintf( CurrentClient, "%f\n", (float)( DiskLoad[ id ].s[ 1 ].delta
903 / timeInterval ) );
906 void print24DiskRIOInfo( const char* cmd ) {
907 int id;
909 sscanf( cmd + 9, "%d", &id );
910 fprintf( CurrentClient, "Disk%d Read\t0\t0\tkBytes/s\n", id );
913 void print24DiskWIO( const char* cmd ) {
914 int id;
916 if ( Dirty )
917 process24Stat();
919 sscanf( cmd + 9, "%d", &id );
920 fprintf( CurrentClient, "%f\n", (float)( DiskLoad[ id ].s[ 2 ].delta
921 / timeInterval ) );
924 void print24DiskWIOInfo( const char* cmd ) {
925 int id;
927 sscanf( cmd + 9, "%d", &id );
928 fprintf( CurrentClient, "Disk%d Write\t0\t0\tkBytes/s\n", id );
931 void print24DiskRBlk( const char* cmd ) {
932 int id;
934 if ( Dirty )
935 process24Stat();
937 sscanf( cmd + 9, "%d", &id );
938 /* a block is 512 bytes or 1/2 kBytes */
939 fprintf( CurrentClient, "%f\n", (float)( DiskLoad[ id ].s[ 3 ].delta / timeInterval * 2 ) );
942 void print24DiskRBlkInfo( const char* cmd ) {
943 int id;
945 sscanf( cmd + 9, "%d", &id );
946 fprintf( CurrentClient, "Disk%d Read Data\t0\t0\tkBytes/s\n", id );
949 void print24DiskWBlk( const char* cmd ) {
950 int id;
952 if ( Dirty )
953 process24Stat();
955 sscanf( cmd + 9, "%d", &id );
956 /* a block is 512 bytes or 1/2 kBytes */
957 fprintf( CurrentClient, "%f\n", (float)( DiskLoad[ id ].s[ 4 ].delta / timeInterval * 2 ) );
960 void print24DiskWBlkInfo( const char* cmd ) {
961 int id;
963 sscanf( cmd + 9, "%d", &id );
964 fprintf( CurrentClient, "Disk%d Write Data\t0\t0\tkBytes/s\n", id );
967 void printPageIn( const char* cmd ) {
968 (void)cmd;
970 if ( Dirty )
971 process24Stat();
973 fprintf( CurrentClient, "%f\n", (float)( PageIn / timeInterval ) );
976 void printPageInInfo( const char* cmd ) {
977 (void)cmd;
979 fprintf( CurrentClient, "Paged in Pages\t0\t0\t1/s\n" );
982 void printPageOut( const char* cmd ) {
983 (void)cmd;
985 if ( Dirty )
986 process24Stat();
988 fprintf( CurrentClient, "%f\n", (float)( PageOut / timeInterval ) );
991 void printPageOutInfo( const char* cmd ) {
992 (void)cmd;
994 fprintf( CurrentClient, "Paged out Pages\t0\t0\t1/s\n" );
997 void printInterruptx( const char* cmd ) {
998 int id;
1000 if ( Dirty )
1001 process24Stat();
1003 sscanf( cmd + strlen( "cpu/interrupts/int" ), "%d", &id );
1004 fprintf( CurrentClient, "%f\n", (float)( Intr[ id ] / timeInterval ) );
1007 void printInterruptxInfo( const char* cmd ) {
1008 int id;
1010 sscanf( cmd + strlen( "cpu/interrupt/int" ), "%d", &id );
1011 fprintf( CurrentClient, "Interrupt %d\t0\t0\t1/s\n", id );
1014 void printCtxt( const char* cmd ) {
1015 (void)cmd;
1017 if ( Dirty )
1018 process24Stat();
1020 fprintf( CurrentClient, "%f\n", (float)( Ctxt / timeInterval ) );
1023 void printCtxtInfo( const char* cmd ) {
1024 (void)cmd;
1026 fprintf( CurrentClient, "Context switches\t0\t0\t1/s\n" );
1029 void print24DiskIO( const char* cmd ) {
1030 int major, minor;
1031 char devname[DISKDEVNAMELEN];
1032 char name[ 17 ];
1033 DiskIOInfo* ptr;
1035 sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s", devname, &major, &minor, name );
1037 if ( Dirty )
1038 process24Stat();
1040 ptr = DiskIO;
1041 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
1042 ptr = ptr->next;
1044 if ( !ptr ) {
1045 print_error( "RECONFIGURE" );
1046 fprintf( CurrentClient, "0\n" );
1048 log_error( "Disk device disappeared" );
1049 return;
1052 if ( strcmp( name, "total" ) == 0 )
1053 fprintf( CurrentClient, "%f\n", (float)( ptr->total.delta / timeInterval ) );
1054 else if ( strcmp( name, "rio" ) == 0 )
1055 fprintf( CurrentClient, "%f\n", (float)( ptr->rio.delta / timeInterval ) );
1056 else if ( strcmp( name, "wio" ) == 0 )
1057 fprintf( CurrentClient, "%f\n", (float)( ptr->wio.delta / timeInterval ) );
1058 else if ( strcmp( name, "rblk" ) == 0 )
1059 fprintf( CurrentClient, "%f\n", (float)( ptr->rblk.delta / ( timeInterval * 2 ) ) );
1060 else if ( strcmp( name, "wblk" ) == 0 )
1061 fprintf( CurrentClient, "%f\n", (float)( ptr->wblk.delta / ( timeInterval * 2 ) ) );
1062 else {
1063 fprintf( CurrentClient, "0\n" );
1064 log_error( "Unknown disk device property \'%s\'", name );
1068 void print24DiskIOInfo( const char* cmd ) {
1069 int major, minor;
1070 char devname[DISKDEVNAMELEN];
1071 char name[ 17 ];
1072 DiskIOInfo* ptr = DiskIO;
1074 sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s", devname, &major, &minor, name );
1076 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
1077 ptr = ptr->next;
1079 if ( !ptr ) {
1080 /* Disk device has disappeared. Print a dummy answer. */
1081 fprintf( CurrentClient, "Dummy\t0\t0\t\n" );
1082 return;
1085 /* remove trailing '?' */
1086 name[ strlen( name ) - 1 ] = '\0';
1088 if ( strcmp( name, "total" ) == 0 )
1089 fprintf( CurrentClient, "Total accesses device %s (%d:%d)\t0\t0\t1/s\n",
1090 devname, major, minor );
1091 else if ( strcmp( name, "rio" ) == 0 )
1092 fprintf( CurrentClient, "Read data device %s (%d:%d)\t0\t0\t1/s\n",
1093 devname, major, minor );
1094 else if ( strcmp( name, "wio" ) == 0 )
1095 fprintf( CurrentClient, "Write data device %s (%d:%d)\t0\t0\t1/s\n",
1096 devname, major, minor );
1097 else if ( strcmp( name, "rblk" ) == 0 )
1098 fprintf( CurrentClient, "Read accesses device %s (%d:%d)\t0\t0\tkBytes/s\n",
1099 devname, major, minor );
1100 else if ( strcmp( name, "wblk" ) == 0 )
1101 fprintf( CurrentClient, "Write accesses device %s (%d:%d)\t0\t0\tkBytes/s\n",
1102 devname, major, minor );
1103 else {
1104 fprintf( CurrentClient, "Dummy\t0\t0\t\n" );
1105 log_error( "Request for unknown device property \'%s\'", name );