From 84da08d7b7fcda12c85caeb5a10b4903770a6f69 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeaddons@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kicker-applets/ktimemon/sample.cc | 508 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 kicker-applets/ktimemon/sample.cc (limited to 'kicker-applets/ktimemon/sample.cc') diff --git a/kicker-applets/ktimemon/sample.cc b/kicker-applets/ktimemon/sample.cc new file mode 100644 index 0000000..25f25a3 --- /dev/null +++ b/kicker-applets/ktimemon/sample.cc @@ -0,0 +1,508 @@ +/**********************************************************************/ +/* TimeMon (c) 1994 Helmut Maierhofer */ +/* KDE-ified M. Maierhofer 1998 */ +/**********************************************************************/ + +/* + * sample.cc + * + * Definitions for the system dependent sampling class. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include + +#ifdef __osf__ +#include +#elif defined(USE_SOLARIS) +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include "timemon.h" +#include "sample.h" + +// -- global definitions ------------------------------------------------- + +#if defined(__osf__) || defined(USE_SOLARIS) +extern "C" int getpagesize(); // argh, have to define prototype! +#endif + +#ifdef __linux__ +// -- global constants --------------------------------------------------- +#define STAT_NAME "/proc/stat" +#define MEMINFO_NAME "/proc/meminfo" +#endif + +// -- KSample::Sample definition ----------------------------------------- + +// Fill sample with some default values (e.g. used in preview widget +// in configuration) +void KSample::Sample::fill(unsigned scale) +{ + user = scale * 40; user /= 100; + nice = scale * 25; user /= 100; + kernel = scale * 10; kernel /= 100; + iowait = scale * 5; iowait /= 100; + cpus = 1; + buffers = scale * 20; buffers /= 100; + used = scale * 30; used /= 100; + cached = scale * 20; cached /= 100; + sused = scale * 25; sused /= 100; +} + +// -- KSample definition ------------------------------------------------- + +// Initialise the member variables and try to open the standard files in +// the proc filesystem; for other platforms perform equivalent initialisation +KSample::KSample(KTimeMon *t, bool a, unsigned p, unsigned s, unsigned c) : + timemon(t), +#ifdef __linux__ + memFD(-1), statFD(-1), +#elif defined (USE_SOLARIS) + kc(0), warned(false), +#endif + pageScale(p), swapScale(s), cxScale(c), autoscale(a) +{ +#ifdef __linux__ + memstats[0].name = "SwapTotal:"; + memstats[0].stat = &sample.stotal; + memstats[1].name = "MemTotal:"; + memstats[1].stat = &sample.mtotal; + memstats[2].name = "MemFree:"; + memstats[2].stat = &sample.free; + memstats[3].name = "Buffers:"; + memstats[3].stat = &sample.buffers; + memstats[4].name = "Cached:"; + memstats[4].stat = &sample.cached; + memstats[5].name = "SwapFree:"; + memstats[5].stat = &sample.sfree; + memstats[6].name = 0; + memstats[6].stat = 0; + + if ((memFD = open(MEMINFO_NAME, O_RDONLY)) == -1) { + KMessageBox::error(timemon, + i18n("Unable to open the file '%1'. The diagnostics are:\n%2.\n" + "This file is required to determine current memory usage.\n" + "Maybe your proc filesystem is non-Linux standard?").arg(MEMINFO_NAME).arg(strerror(errno))); + exit(1); + } + + fcntl( memFD,F_SETFD, FD_CLOEXEC ); + + if ((statFD = open(STAT_NAME, O_RDONLY)) == -1) { + KMessageBox::error(timemon, + i18n("Unable to open the file '%1'. The diagnostics are:\n%2.\n" + "This file is required to determine current system info. " + "Maybe your proc filesystem is non-Linux standard?").arg(MEMINFO_NAME).arg(strerror(errno))); + exit(1); + } + + fcntl( statFD,F_SETFD, FD_CLOEXEC ); + +#elif defined (USE_SOLARIS) + if ((kc = kstat_open()) == 0) { + KMessageBox::error(timemon, i18n("Unable to initialize the 'kstat' library. " + "This library is used for accessing kernel information. " + "The diagnostics are:\n%1.\n" + "Are you really running Solaris? " + "Please contact the maintainer at mueller@kde.org " + "who will try to figure out what went wrong.").arg(strerror(errno))); + exit(1); + } +#endif + +#if defined(USE_SOLARIS) || defined(__osf__) + pagesPerMB = (1024*1024) / getpagesize(); + if (pagesPerMB == 0) pagesPerMB = 1; // paranoia sanity check +#endif + + readSample(); + updateSample(); +} + +// Get rid of the resources we acquired in the constructor. +KSample::~KSample() +{ +#ifdef __linux__ + close(memFD); + close(statFD); +#elif defined (USE_SOLARIS) + if (kc != 0) kstat_close(kc); +#endif +} + +// Set the appropriate scaling parameters +void KSample::setScaling(bool a, unsigned p, unsigned s, unsigned c) +{ + autoscale = a; + pageScale = p; + swapScale = s; + cxScale = c; +} + +// ----------------------------------------------------------------------------- +// Show a message box with the given message and terminate the application. + +void KSample::fatal(const QString& msg) +{ + timemon->stop(); + + KMessageBox::error(timemon, msg); +// exit(1); +} + + +// ----------------------------------------------------------------------------- +// Show a message box with the given message and don't terminate the app ;-) + +void KSample::nonfatal(const QString& msg) +{ + timemon->stop(); + + KMessageBox::sorry(timemon, msg); + timemon->cont(); +} + + +// ----------------------------------------------------------------------------- +// Read a new sample from the files or whatever resource the OS implements + +/* For 2.5 kernels */ +static inline void +scan_one(const char* buff, const char *key, unsigned long int* val) +{ + const char *b = strstr(buff, key); + if (b) { + b = strstr(b, " "); + if (b) + sscanf(b, " %lu", val); + } +} + +void KSample::readSample() +{ + sample.cpus = 0; // just to make sure... + +#ifdef __linux__ // linux makes it simple: use the /proc if + int l; + char buffer[4096]; + + lseek(memFD, 0, 0); + if ((l = read(memFD, buffer, sizeof(buffer) - 1)) < 0) + { + fatal(i18n("Unable to read the memory usage file '%1'.\n" + "The diagnostics are: %2").arg(MEMINFO_NAME).arg(strerror(errno))); + } + buffer[l] = '\0'; + l = 0; + char *p; + while (memstats[l].name != 0) { + p = strstr(buffer, memstats[l].name); + if (p == 0 || + sscanf(p + strlen(memstats[l].name), "%lu kB", memstats[l].stat) < 1) + fatal(i18n("The memory usage file '%1' seems to use a " + "different file format than expected.\n" + "Maybe your version of the proc filesystem is " + "incompatible with supported versions. " + "Please contact the developer at http://bugs.kde.org/ who will try to sort this out.").arg(MEMINFO_NAME)); + l++; + } + + if ( ( p = strstr(buffer, "Slab:") ) ) { + unsigned long slabs; + sscanf(p + 5, "%lu kB", &slabs); + sample.mkernel = slabs; + } + + /* read the data for the cpu stats */ + lseek(statFD, 0, 0); + if ((l = read(statFD, buffer, sizeof(buffer)-1)) < 0) + fatal(i18n("Unable to read the system usage file '%1'.\n" + "The diagnostics are: %2").arg(STAT_NAME).arg(strerror(errno))); + + buffer[l] = '\0'; + + bool ok = (sscanf(buffer, "cpu %lu %lu %lu %lu %lu", &sample.user, + &sample.nice, &sample.kernel, &sample.idle, &sample.iowait) == 5); + + if (ok) { + for (l = 0; l < MAX_CPU; l++) { // get individual stat for SMP machines + char cpuname[10]; + sprintf(cpuname, "cpu%d", l); + + if ((p = strstr(buffer, cpuname)) == NULL) break; + + unsigned long u, n, k, i; + ok = sscanf(p, "cpu%*d %lu %lu %lu %lu", &u, &n, &k, &i); + if (!ok) break; + + sample.smptotal[l] = u+n+k+i; + sample.smpbusy[l] = sample.smptotal[l] - i; + } + } + sample.cpus = l; + +#elif defined(__osf__) // in OSF/2, we can use table() + + QString msg = i18n("Unable to obtain system information.\n" + "The table(2) system call returned an error " + "for table %1.\n" + "Please contact the maintainer at mueller@kde.org " + "who will try to figure out what went wrong."); + + struct tbl_sysinfo sysinfo; + if (table(TBL_SYSINFO, 0, &sysinfo, 1, sizeof(sysinfo)) != 1) + fatal(msg.arg("TBL_SYSINFO")); + + sample.user = sysinfo.si_user; + sample.nice = sysinfo.si_nice; + sample.kernel = sysinfo.si_sys; + sample.iowait = sysinfo.wait; + sample.idle = sysinfo.si_idle; + + struct tbl_vmstats vmstats; + if (table(TBL_VMSTATS, 0, &vmstats, 1, sizeof(vmstats)) != 1) + fatal(msg.arg("TBL_VMSTATS")); + + sample.mtotal = vmstats.free_count + vmstats.active_count + + vmstats.inactive_count + vmstats.wire_count; + sample.free = vmstats.free_count; + sample.buffers = vmstats.inactive_count; // pages not used for some time + sample.cached = vmstats.wire_count; // kernel/driver memory + + struct tbl_swapinfo swapinfo; + if (table(TBL_SWAPINFO, -1, &swapinfo, 1, sizeof(swapinfo)) != 1) + fatal(msg.arg("TBL_SWAPINFO")); + + sample.stotal = swapinfo.size; + sample.sfree = swapinfo.free; + +#elif defined(USE_SOLARIS) + kstat_t *ksp; + + sample.cpus = 0; + for (ksp = kc->kc_chain; ksp != 0; ksp = ksp->ks_next) { + if (strncmp(ksp->ks_name, "cpu_stat", 8) != 0) continue; + sample.cpus++; + } + + if (sample.cpus == 0) + fatal(i18n("Unable to find any entries for CPU statistics " + "in the 'kstat' library. Are you running a non-standard " + "version of Solaris?\n" + "Please contact the maintainer via http://bugs.kde.org/ who will try to sort this out.")); + + sample.user = sample.nice = sample.kernel = sample.iowait = sample.idle = 0; + sample.stotal = sample.sfree = 0; + + int cpus = 0; + for (ksp = kc->kc_chain; ksp != 0; ksp = ksp->ks_next) { + if (strncmp(ksp->ks_name, "cpu_stat", 8) != 0) continue; + cpus++; + + cpu_stat_t cstat; + if (kstat_read(kc, ksp, 0) == -1 || // update from kernel + kstat_read(kc, ksp, &cstat) == -1) // and read into buffer + fatal(i18n("Unable to read the CPU statistics entry " + "from the 'kstat' library. The diagnostics are '%1'.\n" + "Please contact the maintainer via http://bugs.kde.org/ who will try to sort this out.").arg(strerror(errno))); + + // fields are: idle user kernel iowait (no nice info?) + sample.user += cstat.cpu_sysinfo.cpu[1] / sample.cpus; + sample.nice += 0; + sample.kernel += cstat.cpu_sysinfo.cpu[2] / sample.cpus; + sample.iowait += cstat.cpu_sysinfo.cpu[3] / sample.cpus; + sample.idle += cstat.cpu_sysinfo.cpu[0] / sample.cpus; + } + + if (cpus != sample.cpus) + fatal(i18n("The number of CPUs appears to have changed at " + "very short notice, or the 'kstat' library returns " + "inconsistent results (%1 vs. %2 CPUs).\n" + "Please contact the maintainer via http://bugs.kde.org/ who will try to sort this out.").arg(sample.cpus).arg(cpus)); + + // availrmem = pages of core for user-proc ( == physmem - kernelmem) + // freemem = no of free pages + // physmem == total mem in 4KB blocks + + errno = 0; + if ((ksp = kstat_lookup(kc, "unix", -1, "system_pages")) == 0 || + kstat_read(kc, ksp, 0) == -1) + fatal(i18n("Unable to read the memory statistics entry " + "from the 'kstat' library. The diagnostics are '%1'\n" + "You might want to contact the maintainer at " + "http://bugs.kde.org/ who will try to sort this out.").arg(strerror(errno))); + + int i; + unsigned long physmem = 0, freemem = 0, availrmem = 0; + + kstat_named_t *kn = (kstat_named_t *)ksp->ks_data; + for (i = 0; i < (int) ksp->ks_ndata; i++) { + if (strcmp(kn->name, "physmem") == 0) physmem = kn->value.ul; + else if (strcmp(kn->name, "freemem") == 0) freemem = kn->value.ul; + else if (strcmp(kn->name, "availrmem") == 0) availrmem = kn->value.ul; + kn++; + } + + if (physmem == 0) // sanity check, this should always be > 0 + fatal(i18n("There seems to be a problem with KTimeMon's handling " + "of the 'kstat' library: 0 bytes of physical memory determined!\n" + "Free memory is %1, available memory is %2.\n" + "Please contact the maintainer at mueller@kde.org who will try to sort this out.").arg(freemem).arg(availrmem)); + + sample.mtotal = physmem; + sample.free = freemem; + sample.buffers = 0; + sample.cached = physmem - availrmem; // memory used by the kernel + + int swapentries; + if ((swapentries = swapctl(SC_GETNSWP, 0)) == -1) + fatal(i18n("Unable to determine the number of " + "swap spaces. The diagnostics are '%1'.\n" + "Please contact the maintainer at http://bugs.kde.org/ who will try to sort this out.").arg(strerror(errno))); + + if (swapentries != 0) { + // 2* to get some space for padding?? + swaptbl_t *stbl = (swaptbl_t *) malloc(2*sizeof(int) + swapentries * + sizeof(struct swapent)); + if (stbl == 0) + fatal(i18n("KTimeMon ran out of memory while " + "trying to determine the swap usage.\n" + "Attempted to allocate %1 bytes of memory (2 * %2 + %3 * %4).\n" + "Please contact the maintainer at http://bugs.kde.org/ who will try to sort this out.") + .arg(2 * sizeof(int) + swapentries * sizeof(struct swapent)) + .arg(sizeof(int)).arg(swapentries).arg(sizeof(struct swapent))); + + char path[1024]; + stbl->swt_n = swapentries; + for (i = 0; i < swapentries; i++) stbl->swt_ent[i].ste_path = path; + + if ((swapentries = swapctl(SC_LIST, stbl)) == -1) + fatal(i18n("Unable to determine the swap usage.\n" + "The diagnostics are '%1'.\n" + "Please contact the maintainer at http://bugs.kde.org/ who will try to sort this out.").arg(strerror(errno))); + + + if (!warned && swapentries != stbl->swt_n) { + warned = true; + nonfatal(i18n("Information was requested for " + "%1 swap spaces, but only %2 swap entries were returned.\n" + "KTimeMon will attempt to continue.\n" + "Please contact the maintainer at http://bugs.kde.org/ who will try to sort this out.").arg(stbl->swt_n).arg(swapentries)); + } + + for (i = 0; i < swapentries; i++) { + sample.stotal += stbl->swt_ent[i].ste_pages; + sample.sfree += stbl->swt_ent[i].ste_free; + } + + free(stbl); + } + +#else +#warning This type of system is not supported + sample.stotal = sample.sfree = 0; +#endif + + sample.cputotal = + sample.user + sample.nice + sample.kernel + sample.iowait + sample.idle; + sample.used = sample.mtotal - sample.mkernel - sample.free - sample.buffers - sample.cached; + sample.sused = sample.stotal - sample.sfree; +} + +// Read a new sample after copying the old one. +void KSample::updateSample() +{ + oldSample = sample; + readSample(); +} + +// Convert v to a value representing megabytes. +inline void KSample::makeMBytes(unsigned long &v) +{ +#ifdef __linux__ + v /= 1024; // can it be simpler ;-) +#elif defined (__osf__) || defined(USE_SOLARIS) + v /= pagesPerMB; +#endif +} + +// Return unscaled sample +KSample::Sample KSample::getRawSample() +{ + Sample diff = sample; + + diff.cputotal -= oldSample.cputotal; + + diff.user -= oldSample.user; + diff.nice -= oldSample.nice; + diff.kernel -= oldSample.kernel; + diff.iowait -= oldSample.iowait; + + for (int i = 0; i < diff.cpus; i++) { + diff.smptotal[i] -= oldSample.smptotal[i]; + diff.smpbusy[i] -= oldSample.smpbusy[i]; + } + + return diff; +} + +// Better scaling, round according to first decimal +inline unsigned long KSample::doScale(unsigned long value, unsigned scale1, + unsigned long scale2) +{ + if (scale2 == 0) scale2 = (unsigned long)~0; // avoid SEGVs + + unsigned long v = value * scale1 * 10; + v /= scale2; + unsigned r = v % 10; + v /= 10; + if (r > 4) v++; + return v; +} + +// Provide the difference from the last to the current sample, scale it +// and return it. +KSample::Sample KSample::getSample(unsigned scale) +{ + Sample s = getRawSample(); + + s.user = doScale(s.user, scale, s.cputotal); + s.nice = doScale(s.nice, scale, s.cputotal); + s.kernel = doScale(s.kernel, scale, s.cputotal); + s.iowait = doScale(s.iowait, scale, s.cputotal); + + for (int i = 0; i < s.cpus; i++) + s.smpbusy[i] = doScale(s.smpbusy[i], scale, s.smptotal[i]); + + s.cached = doScale(s.cached, scale, s.mtotal); + s.buffers = doScale(s.buffers, scale, s.mtotal); + s.used = doScale(s.used, scale, s.mtotal); + s.mkernel = doScale(s.mkernel, scale, s.mtotal); + makeMBytes(s.mtotal); + + s.sused = doScale(s.sused, scale, s.stotal); + makeMBytes(s.stotal); + + return s; +} + -- cgit v1.2.1