1
/**********************************************************************/
2
/* TimeMon (c) 1994 Helmut Maierhofer */
3
/* KDE-ified M. Maierhofer 1998 */
4
/**********************************************************************/
9
* Definitions for the system dependent sampling class.
19
#include <sys/types.h>
28
#include <sys/table.h>
29
#elif defined(__sun__)
31
#include <sys/sysinfo.h>
38
#include <kmessagebox.h>
43
// -- global definitions -------------------------------------------------
45
#if defined(__osf__) || defined(__sun__)
46
extern "C" int getpagesize(); // argh, have to define prototype!
50
// -- global constants ---------------------------------------------------
51
static const char *STAT_NAME = "stat";
52
static const char *MEMINFO_NAME = "meminfo";
53
static const char *MTAB_NAME = "/etc/mtab";
56
// -- KSample::Sample definition -----------------------------------------
58
// Fill sample with some default values (e.g. used in preview widget
60
void KSample::Sample::fill(unsigned scale)
62
user = scale * 40; user /= 100;
63
nice = scale * 25; user /= 100;
64
kernel = scale * 15; kernel /= 100;
66
buffers = scale * 20; buffers /= 100;
67
used = scale * 30; used /= 100;
68
cached = scale * 20; cached /= 100;
69
sused = scale * 25; sused /= 100;
70
pout = scale * 10; pout /= 100;
71
pin = scale * 20; pin /= 100;
72
swout = scale * 30; swout /= 100;
73
swin = scale * 15; swin /= 100;
74
cswitches = scale * 75; cswitches /= 100;
77
// -- KSample definition -------------------------------------------------
79
// Initialise the member variables and try to open the standard files in
80
// the proc filesystem; for other platforms perform equivalent initialisation
81
KSample::KSample(KTimeMon *t, bool a, unsigned p, unsigned s, unsigned c) :
84
memFD(-1), statFD(-1),
85
#elif defined (__sun__)
88
pageScale(p), swapScale(s), cxScale(c), autoscale(a)
95
snprintf(file, sizeof(file), "%s/%s", proc, MEMINFO_NAME);
97
if ((memFD = open(file, O_RDONLY)) == -1) {
98
KMessageBox::error(timemon,
99
i18n("Sorry, I cannot open the file `%1'.\nThe diagnostics are: %2.\n\n"
100
"I need this file to determine current memory usage.\n"
101
"Maybe your proc filesystem is non Linux-standard?").arg(file).arg(strerror(errno)));
105
snprintf(file, sizeof(file), "%s/%s", proc, STAT_NAME);
106
if ((statFD = open(file, O_RDONLY)) == -1) {
107
KMessageBox::error(timemon,
108
i18n("Sorry, I cannot open the file `%1'.\nThe diagnostics are: %2.\n\n"
109
"I need this file to determine current system info.\n"
110
"Maybe your proc filesystem is non Linux-standard?").arg(file).arg(strerror(errno)));
114
#elif defined (__sun__)
115
if ((kc = kstat_open()) == 0) {
116
KMessageBox::error(timemon, i18n("Sorry, I cannot initialise the `kstat' library.\n"
117
"This library is used for accessing kernel information.\n"
118
"The diagnostics are: `%1'.\n\n"
119
"Are you running Solaris at all?\n"
120
"You may want to mail me at mueller@kde.org,\n"
121
"I will try to figure out what went wrong.").arg(strerror(errno)));
126
#if defined(__sun__) || defined(__osf__)
127
pagesPerMB = (1024*1024) / getpagesize();
128
if (pagesPerMB == 0) pagesPerMB = 1; // paranoia sanity check
135
// Get rid of the resources we acquired in the constructor.
141
#elif defined (__sun__)
142
if (kc != 0) kstat_close(kc);
147
// Parse /etc/mtab to determine the proc filesystem mount point.
148
void KSample::parseMtab(char *dest)
150
ifstream *mtab = new ifstream(MTAB_NAME);
152
KMessageBox::error(timemon, i18n("I cannot open file `%1' to determine where the proc\n"
153
"filesystem is mounted. The diagnostics are:\n %2\n\n"
154
"Are you running UNIX at all?!?").arg( MTAB_NAME).arg( strerror(errno)));
164
mtab->getline(line, sizeof(line));
167
errormsg = i18n("I cannot read file `%1' to determine where the proc\n"
168
"filesystem is mounted. The diagnostics are:\n %2").arg(MTAB_NAME).arg(strerror(errno));
173
errormsg = i18n("Hmm, I could not figure out where the proc filesystem\n"
174
"is mounted (there is no entry in `%1').\n\n"
175
"I need information from the proc filesystem to determine\n"
176
"current system usage. Maybe you are not running Linux (I'm\n"
177
"afraid the proc filesystem is Linux specific)?\n\n"
178
"If you can provide help with porting KTimeMon to your\n"
179
"platform, please contact me at <mueller@kde.org>").arg(MTAB_NAME);
184
errormsg = i18n("Hmm, I encountered a very long line while reading\n"
185
"information in `%1' (where \"very long\" is defined\n"
186
"as > %2). This happened at line %3.\n\n"
187
"Is %4 the mount table on your platform?\n\n")
188
.arg(MTAB_NAME).arg(sizeof(line)).arg(lineno).arg(MTAB_NAME);
194
if ((p = m = strchr(line, ' ')) != 0) p = strchr(m+1, ' ');
195
if (p == 0 || strncmp(p+1, "proc ", 5) != 0) continue;
198
strncpy(dest, m+1, 256);
204
KMessageBox::error(timemon, errormsg);
209
// Set the appropriate scaling parameters
210
void KSample::setScaling(bool a, unsigned p, unsigned s, unsigned c)
218
// -----------------------------------------------------------------------------
219
// Show a message box with the given message and terminate the application.
221
void KSample::fatal(const QString& msg)
225
KMessageBox::error(timemon, msg);
230
// -----------------------------------------------------------------------------
231
// Show a message box with the given message and don't terminate the app ;-)
233
void KSample::nonfatal(const QString& msg)
237
KMessageBox::sorry(timemon, msg);
242
// -----------------------------------------------------------------------------
243
// Read a new sample from the files or whatever resource the OS implements
245
void KSample::readSample()
247
sample.cpus = 0; // just to make sure...
249
#ifdef __linux__ // linux makes it simple: use the /proc if
254
if ((l = read(memFD, buffer, sizeof(buffer) - 1)) < 0)
255
fatal(i18n("Sorry, I cannot read the memory usage file `%1/%2'.\n"
256
"The diagnostics are: %3").arg(proc).arg(MEMINFO_NAME).arg(strerror(errno)));
263
{ "SwapTotal:", &sample.stotal },
264
{ "MemTotal:", &sample.mtotal },
265
{ "MemFree:", &sample.free },
266
{ "Buffers:", &sample.buffers },
267
{ "Cached:", &sample.cached },
268
{ "SwapFree:", &sample.sfree },
274
while (memstats[l].name != 0) {
275
p = strstr(buffer, memstats[l].name);
277
sscanf(p + strlen(memstats[l].name), "%lu kB", memstats[l].stat) < 1)
278
fatal(i18n("Sorry, the memory usage file `%1/%2'\nseems to use a "
279
"different file format than I expect.\n\n"
280
"Maybe your version of the proc filesystem is\n"
281
"incompatible with mine. Mail me at\n"
282
"m.maierhofer@tees.ac.uk and I will try to sort this out.").arg(proc).arg(MEMINFO_NAME));
287
if ((l = read(statFD, buffer, sizeof(buffer)-1)) < 0)
288
fatal(i18n("Sorry, I cannot read the system usage file `%1/%2'.\n"
289
"The diagnostics are: %3").arg(proc).arg(STAT_NAME).arg(strerror(errno)));
293
bool ok = (sscanf(buffer, "cpu %lu %lu %lu %lu\n", &sample.user,
294
&sample.nice, &sample.kernel, &sample.idle) == 4);
297
for (l = 0; l < MAX_CPU; l++) { // get individual stat for SMP machines
299
sprintf(cpuname, "cpu%d", l);
301
if ((p = strstr(buffer, cpuname)) == NULL) break;
303
unsigned long u, n, k, i;
304
ok = sscanf(p, "cpu%*d %lu %lu %lu %lu\n", &u, &n, &k, &i);
307
sample.smptotal[l] = u+n+k+i;
308
sample.smpbusy[l] = sample.smptotal[l] - i;
314
if ((p = strstr(buffer, "page")) == NULL) ok = false;
315
else ok = (sscanf(p, "page %lu %lu\n", &sample.pin, &sample.pout) == 2);
319
if ((p = strstr(buffer, "swap")) == NULL) ok = false;
320
else ok = (sscanf(p, "swap %lu %lu\n", &sample.swin, &sample.swout) == 2);
324
if ((p = strstr(buffer, "ctxt")) == NULL) ok = false;
325
else ok = (sscanf(p, "ctxt %lu\n", &sample.cswitches) == 1);
329
fatal(i18n("Sorry, the system usage file `%1/%2'\n"
330
"seems to use a different file format than I expect.\n\n"
331
"Maybe your version of the proc filesystem is\n"
332
"incompatible with mine. Mail me at\n"
333
"mueller@kde.org and I will try to sort this out.").arg(proc).arg(STAT_NAME));
335
#elif defined(__osf__) // in OSF/2, we can use table()
337
QString msg = i18n("Sorry, I cannot get system information.\n\n"
338
"The table(2) system call returned an error\n"
340
"You may want to mail me at mueller@kde.org, "
341
"I will try to figure out what went wrong.");
343
struct tbl_sysinfo sysinfo;
344
if (table(TBL_SYSINFO, 0, &sysinfo, 1, sizeof(sysinfo)) != 1)
345
fatal(msg.arg("TBL_SYSINFO"));
347
sample.user = sysinfo.si_user;
348
sample.nice = sysinfo.si_nice;
349
sample.kernel = sysinfo.si_sys;
350
sample.idle = sysinfo.si_idle + sysinfo.wait;
352
struct tbl_vmstats vmstats;
353
if (table(TBL_VMSTATS, 0, &vmstats, 1, sizeof(vmstats)) != 1)
354
fatal(msg.arg("TBL_VMSTATS"));
356
sample.mtotal = vmstats.free_count + vmstats.active_count +
357
vmstats.inactive_count + vmstats.wire_count;
358
sample.free = vmstats.free_count;
359
sample.buffers = vmstats.inactive_count; // pages not used for some time
360
sample.cached = vmstats.wire_count; // kernel/driver memory
362
struct tbl_swapinfo swapinfo;
363
if (table(TBL_SWAPINFO, -1, &swapinfo, 1, sizeof(swapinfo)) != 1)
364
fatal(msg.arg("TBL_SWAPINFO"));
366
sample.stotal = swapinfo.size;
367
sample.sfree = swapinfo.free;
369
struct tbl_paging paging;
370
if (table(TBL_PAGING, 0, &paging, 1, sizeof(paging)) != 1)
371
fatal(msg.arg("TBL_PAGING"));
373
sample.pin = sample.swin = paging.v_pgpgin; // MM no separate swap
374
sample.pout = sample.swout = paging.v_pgout;
376
struct tbl_intr intr;
377
if (table(TBL_INTR, 0, &intr, 1, sizeof(intr)) != 1)
378
fatal(msg.arg("TBL_INTR"));
380
sample.cswitches = intr.in_context;
382
#elif defined(__sun__)
386
for (ksp = kc->kc_chain; ksp != 0; ksp = ksp->ks_next) {
387
if (strncmp(ksp->ks_name, "cpu_stat", 8) != 0) continue;
391
if (sample.cpus == 0)
392
fatal(i18n("Sorry, I cannot find any entries for CPU statistics\n"
393
"in the `kstat' library. Are you running a non-standard\n"
394
"version of Solaris?\n\n"
395
"Please contact me at mueller@kde.org and I will try to sort this out."));
397
sample.user = sample.nice = sample.kernel = sample.idle = 0;
398
sample.pin = sample.pout = sample.swin = sample.swout = 0;
399
sample.cswitches = 0;
400
sample.stotal = sample.sfree = 0;
403
for (ksp = kc->kc_chain; ksp != 0; ksp = ksp->ks_next) {
404
if (strncmp(ksp->ks_name, "cpu_stat", 8) != 0) continue;
408
if (kstat_read(kc, ksp, 0) == -1 || // update from kernel
409
kstat_read(kc, ksp, &cstat) == -1) // and read into buffer
410
fatal(i18n("Sorry, I cannot read the CPU statistics entry\n"
411
"from the `kstat' library. The diagnostics are `%1'.\n\n"
412
"You might want to contact me at\n"
413
"mueller@kde.org and I will try to sort this out.").arg(strerror(errno)));
415
// fields are: idle user kernel iowait
416
sample.user += cstat.cpu_sysinfo.cpu[1] / sample.cpus;
417
sample.nice += cstat.cpu_sysinfo.cpu[3] / sample.cpus;
418
sample.kernel += cstat.cpu_sysinfo.cpu[2] / sample.cpus;
419
sample.idle += cstat.cpu_sysinfo.cpu[0] / sample.cpus;
421
sample.pin += cstat.cpu_vminfo.pgin / sample.cpus;
422
sample.pout += cstat.cpu_vminfo.pgout / sample.cpus;
423
sample.swin += cstat.cpu_vminfo.swapin / sample.cpus;
424
sample.swout += cstat.cpu_vminfo.swapout / sample.cpus;
426
sample.cswitches += cstat.cpu_sysinfo.pswitch / sample.cpus;
429
if (cpus != sample.cpus)
430
fatal(i18n("Huhh, now this is very strange.\n"
431
"The number of CPUs seems to have changed at\n"
432
"very short notice. Or the `kstat' library returns\n"
433
"inconsistent results (%1 vs. %2 CPUs).\n"
434
"Please contact me at mueller@kde.org and I will try to sort this out.").arg(sample.cpus).arg(cpus));
436
// availrmem = pages of core for user-proc ( == physmem - kernelmem)
437
// freemem = no of free pages
438
// physmem == total mem in 4KB blocks
441
if ((ksp = kstat_lookup(kc, "unix", -1, "system_pages")) == 0 ||
442
kstat_read(kc, ksp, 0) == -1)
443
fatal(i18n("Sorry, I cannot read the memory statistics entry\n"
444
"from the `kstat' library. The diagnostics are `%1'\n\n"
445
"You might want to contact me at\n"
446
"mueller@kde.org and I will try to sort this out.").arg(strerror(errno)));
449
unsigned long physmem = 0, freemem = 0, availrmem = 0;
451
kstat_named_t *kn = (kstat_named_t *)ksp->ks_data;
452
for (i = 0; i < (int) ksp->ks_ndata; i++) {
453
if (strcmp(kn->name, "physmem") == 0) physmem = kn->value.ul;
454
else if (strcmp(kn->name, "freemem") == 0) freemem = kn->value.ul;
455
else if (strcmp(kn->name, "availrmem") == 0) availrmem = kn->value.ul;
459
if (physmem == 0) // sanity check, this should always be > 0
460
fatal(i18n("Uh uh, there seems to be a problem with my handling\n"
461
"of the `kstat' library: I determined 0 physical memory!\n"
462
"(Free memory is %1, available memory is %2.\n\n"
463
"Please contact me at mueller@kde.org and I will try to sort this out.").arg(freemem).arg(availrmem));
465
sample.mtotal = physmem;
466
sample.free = freemem;
468
sample.cached = physmem - availrmem; // memory used by the kernel
471
if ((swapentries = swapctl(SC_GETNSWP, 0)) == -1)
472
fatal(i18n("Sorry, cannot determine the number of\n"
473
"swap spaces. The diagnostics are `%1'.\n\n"
474
"Please contact me at mueller@kde.org and I will try to sort this out.").arg(strerror(errno)));
476
if (swapentries != 0) {
477
// 2* to get some space for padding??
478
swaptbl_t *stbl = (swaptbl_t *) malloc(2*sizeof(int) + swapentries *
479
sizeof(struct swapent));
481
fatal(i18n("Sorry, I ran out of memory while\n"
482
"trying to determine the swap usage.\n"
483
"I tried to allocate %1 bytes of memory (2 * %2 + %3 * %4).\n\n"
484
"Please contact me at mueller@kde.org and I will try to sort this out.")
485
.arg(2 * sizeof(int) + swapentries * sizeof(struct swapent))
486
.arg(sizeof(int)).arg(swapentries).arg(sizeof(struct swapent)));
489
stbl->swt_n = swapentries;
490
for (i = 0; i < swapentries; i++) stbl->swt_ent[i].ste_path = path;
492
if ((swapentries = swapctl(SC_LIST, stbl)) == -1)
493
fatal(i18n("Sorry, I cannot determine the swap usage.\n"
494
"The diagnostics are `%1'.\n\n"
495
"Please contact me at mueller@kde.org and I will try to sort this out.").arg(strerror(errno)));
498
if (!warned && swapentries != stbl->swt_n) {
500
nonfatal(i18n("Strange, I requested information for\n"
501
"%1 swap spaces, but only got %2 swap entries back.\n"
502
"I am a bit confused now, but I will try to continue.\n\n"
503
"Please contact me at mueller@kde.org and I will try to sort this out.").arg(stbl->swt_n).arg(swapentries));
506
for (i = 0; i < swapentries; i++) {
507
sample.stotal += stbl->swt_ent[i].ste_pages;
508
sample.sfree += stbl->swt_ent[i].ste_free;
516
sample.cputotal = sample.user + sample.nice + sample.kernel + sample.idle;
517
sample.used = sample.mtotal - sample.free - sample.buffers - sample.cached;
518
sample.sused = sample.stotal - sample.sfree;
521
// Read a new sample after copying the old one.
522
void KSample::updateSample()
528
// Convert v to a value representing megabytes.
529
inline void KSample::makeMBytes(unsigned long &v)
532
v /= 1024; // can it be simpler ;-)
533
#elif defined (__osf__) || defined(__sun__)
538
// Return unscaled sample
539
KSample::Sample KSample::getRawSample()
541
Sample diff = sample;
543
diff.cputotal -= oldSample.cputotal;
545
diff.user -= oldSample.user;
546
diff.nice -= oldSample.nice;
547
diff.kernel -= oldSample.kernel;
549
for (int i = 0; i < diff.cpus; i++) {
550
diff.smptotal[i] -= oldSample.smptotal[i];
551
diff.smpbusy[i] -= oldSample.smpbusy[i];
554
diff.pin -= oldSample.pin;
555
diff.pout -= oldSample.pout;
557
diff.swin -= oldSample.swin;
558
diff.swout -= oldSample.swout;
560
diff.cswitches -= oldSample.cswitches;
565
// Better scaling, round according to first decimal
566
inline unsigned long KSample::doScale(unsigned long value, unsigned scale1,
567
unsigned long scale2)
569
if (scale2 == 0) scale2 = (unsigned long)~0; // avoid SEGVs
571
unsigned long v = value * scale1 * 10;
579
// Provide the difference from the last to the current sample, scale it
581
KSample::Sample KSample::getSample(unsigned scale)
583
Sample s = getRawSample();
585
s.user = doScale(s.user, scale, s.cputotal);
586
s.nice = doScale(s.nice, scale, s.cputotal);
587
s.kernel = doScale(s.kernel, scale, s.cputotal);
589
for (int i = 0; i < s.cpus; i++)
590
s.smpbusy[i] = doScale(s.smpbusy[i], scale, s.smptotal[i]);
592
s.cached = doScale(s.cached, scale, s.mtotal);
593
s.buffers = doScale(s.buffers, scale, s.mtotal);
594
s.used = doScale(s.used, scale, s.mtotal);
595
makeMBytes(s.mtotal);
597
s.sused = doScale(s.sused, scale, s.stotal);
598
makeMBytes(s.stotal);
602
unsigned page = (s.pin + s.pout) / 2;
604
if (autoscale && page > 0 && // autoscale
605
(page / pageScale > scale / 2 || page / pageScale < 1)) {
606
pageScale = page / (scale/4);
608
s.pin = doScale(s.pin, 1, pageScale);
609
s.pout = doScale(s.pout, 1, pageScale);
613
unsigned swap = (s.swin + s.swout) / 2;
615
if (autoscale && swap > 0 && // autoscale
616
(swap / swapScale > scale / 2 || swap / swapScale < 1)) {
617
swapScale = swap / (scale/4);
619
s.swin = doScale(s.swin, 1, swapScale);
620
s.swout = doScale(s.swout, 1, swapScale);
622
s.cswitches *= scale;
623
if (autoscale && s.cswitches > 0 && // auto scale
624
(s.cswitches / cxScale > scale || s.cswitches / cxScale < 2)) {
625
cxScale = s.cswitches/(scale/2);
627
s.cswitches = doScale(s.cswitches, 1, cxScale);