From 2ee50d030178cede83eb9d0005fbc19f819d30fe Mon Sep 17 00:00:00 2001 From: Graham Inggs Date: Mon, 5 Feb 2018 14:48:51 +0200 Subject: Imported Upstream version 2.1.0 --- linux/Battery.c | 53 +++---- linux/Battery.h | 7 - linux/IOPriority.c | 2 +- linux/IOPriority.h | 2 +- linux/IOPriorityPanel.c | 4 +- linux/LinuxProcess.c | 85 +++++++++--- linux/LinuxProcess.h | 21 ++- linux/LinuxProcessList.c | 349 +++++++++++++++++++++++++++++++++++++++++------ linux/LinuxProcessList.h | 33 ++++- linux/Platform.c | 6 +- linux/Platform.h | 4 +- 11 files changed, 454 insertions(+), 112 deletions(-) (limited to 'linux') diff --git a/linux/Battery.c b/linux/Battery.c index 1068184..34a2401 100644 --- a/linux/Battery.c +++ b/linux/Battery.c @@ -41,11 +41,9 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short unsigned int nBatteries = 0; memset(batteries, 0, MAX_BATTERIES * sizeof(char*)); - struct dirent result; - struct dirent* dirEntry; while (nBatteries < MAX_BATTERIES) { - int err = readdir_r(batteryDir, &result, &dirEntry); - if (err || !dirEntry) + struct dirent* dirEntry = readdir(batteryDir); + if (!dirEntry) break; char* entryName = dirEntry->d_name; if (strncmp(entryName, "BAT", 3)) @@ -58,7 +56,7 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short unsigned long int total = 0; for (unsigned int i = 0; i < nBatteries; i++) { char infoPath[30]; - snprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName); + xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName); FILE* file = fopen(infoPath, "r"); if (!file) { @@ -74,6 +72,8 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short fclose(file); + if (!line) break; + char *foundNumStr = String_getToken(line, wordNum); const unsigned long int foundNum = atoi(foundNumStr); free(foundNumStr); @@ -97,11 +97,9 @@ static ACPresence procAcpiCheck() { return AC_ERROR; } - struct dirent result; - struct dirent* dirEntry; for (;;) { - int err = readdir_r((DIR *) dir, &result, &dirEntry); - if (err || !dirEntry) + struct dirent* dirEntry = readdir((DIR *) dir); + if (!dirEntry) break; char* entryName = (char *) dirEntry->d_name; @@ -110,7 +108,7 @@ static ACPresence procAcpiCheck() { continue; char statePath[50]; - snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName); + xSnprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName); FILE* file = fopen(statePath, "r"); if (!file) { @@ -179,25 +177,6 @@ static inline ssize_t xread(int fd, void *buf, size_t count) { } } -/** - * Returns a pointer to the suffix of `str` if its beginning matches `prefix`. - * Returns NULL if the prefix does not match. - * Examples: - * match("hello world", "hello "); -> "world" - * match("hello world", "goodbye "); -> NULL - */ -static inline const char* match(const char* str, const char* prefix) { - for (;;) { - if (*prefix == '\0') { - return str; - } - if (*prefix != *str) { - return NULL; - } - prefix++; str++; - } -} - static void Battery_getSysData(double* level, ACPresence* isOnAC) { *level = 0; @@ -210,18 +189,16 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) { unsigned long int totalFull = 0; unsigned long int totalRemain = 0; - struct dirent result; - struct dirent* dirEntry; for (;;) { - int err = readdir_r((DIR *) dir, &result, &dirEntry); - if (err || !dirEntry) + struct dirent* dirEntry = readdir((DIR *) dir); + if (!dirEntry) break; char* entryName = (char *) dirEntry->d_name; const char filePath[50]; if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') { - snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); + xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); int fd = open(filePath, O_RDONLY); if (fd == -1) { closedir(dir); @@ -240,6 +217,8 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) { bool full = false; bool now = false; while ((line = strsep(&buf, "\n")) != NULL) { + #define match(str,prefix) \ + (String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL) const char* ps = match(line, "POWER_SUPPLY_"); if (!ps) { continue; @@ -266,12 +245,13 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) { continue; } } + #undef match } else if (entryName[0] == 'A') { if (*isOnAC != AC_ERROR) { continue; } - snprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName); + xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName); int fd = open(filePath, O_RDONLY); if (fd == -1) { closedir(dir); @@ -326,6 +306,9 @@ void Battery_getData(double* level, ACPresence* isOnAC) { *level = -1; *isOnAC = AC_ERROR; } + if (*level > 100.0) { + *level = 100.0; + } Battery_cacheLevel = *level; Battery_cacheIsOnAC = *isOnAC; Battery_cacheTime = now; diff --git a/linux/Battery.h b/linux/Battery.h index 4cb22a8..cfb6c32 100644 --- a/linux/Battery.h +++ b/linux/Battery.h @@ -29,13 +29,6 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat // READ FROM /sys // ---------------------------------------- -/** - * Returns a pointer to the suffix of `str` if its beginning matches `prefix`. - * Returns NULL if the prefix does not match. - * Examples: - * match("hello world", "hello "); -> "world" - * match("hello world", "goodbye "); -> NULL - */ void Battery_getData(double* level, ACPresence* isOnAC); #endif diff --git a/linux/IOPriority.c b/linux/IOPriority.c index 7b19743..dd7c84a 100644 --- a/linux/IOPriority.c +++ b/linux/IOPriority.c @@ -35,7 +35,7 @@ typedef int IOPriority; #define IOPriority_error 0xffffffff #define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0) -#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 0) +#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7) }*/ diff --git a/linux/IOPriority.h b/linux/IOPriority.h index d69e30d..148e344 100644 --- a/linux/IOPriority.h +++ b/linux/IOPriority.h @@ -36,7 +36,7 @@ typedef int IOPriority; #define IOPriority_error 0xffffffff #define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0) -#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 0) +#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7) diff --git a/linux/IOPriorityPanel.c b/linux/IOPriorityPanel.c index 9e12c75..2b315b8 100644 --- a/linux/IOPriorityPanel.c +++ b/linux/IOPriorityPanel.c @@ -19,7 +19,7 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) { Panel_setHeader(this, "IO Priority:"); Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None)); if (currPrio == IOPriority_None) Panel_setSelected(this, 0); - struct { int klass; const char* name; } classes[] = { + static const struct { int klass; const char* name; } classes[] = { { .klass = IOPRIO_CLASS_RT, .name = "Realtime" }, { .klass = IOPRIO_CLASS_BE, .name = "Best-effort" }, { .klass = 0, .name = NULL } @@ -27,7 +27,7 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) { for (int c = 0; classes[c].name; c++) { for (int i = 0; i < 8; i++) { char name[50]; - snprintf(name, sizeof(name)-1, "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : "")); + xSnprintf(name, sizeof(name)-1, "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : "")); IOPriority ioprio = IOPriority_tuple(classes[c].klass, i); Panel_add(this, (Object*) ListItem_new(name, ioprio)); if (currPrio == ioprio) Panel_setSelected(this, Panel_size(this) - 1); diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 43b5e38..39b5647 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -81,7 +81,12 @@ typedef enum LinuxProcessFields { #endif OOM = 114, IO_PRIORITY = 115, - LAST_PROCESSFIELD = 116, + #ifdef HAVE_DELAYACCT + PERCENT_CPU_DELAY = 116, + PERCENT_IO_DELAY = 117, + PERCENT_SWAP_DELAY = 118, + #endif + LAST_PROCESSFIELD = 119, } LinuxProcessField; #include "IOPriority.h" @@ -124,6 +129,16 @@ typedef struct LinuxProcess_ { char* cgroup; #endif unsigned int oom; + char* ttyDevice; + #ifdef HAVE_DELAYACCT + unsigned long long int delay_read_time; + unsigned long long cpu_delay_total; + unsigned long long blkio_delay_total; + unsigned long long swapin_delay_total; + float cpu_delay_percent; + float blkio_delay_percent; + float swapin_delay_percent; + #endif } LinuxProcess; #ifndef Process_isKernelThread @@ -140,11 +155,11 @@ ProcessFieldData Process_fields[] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, - [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, + [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, }, [PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, [PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, - [SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, }, - [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, + [SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, }, + [TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, [FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, @@ -214,6 +229,11 @@ ProcessFieldData Process_fields[] = { #endif [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, +#ifdef HAVE_DELAYACCT + [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, }, + [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, }, + [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, }, +#endif [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, }; @@ -226,7 +246,7 @@ ProcessPidColumn Process_pidColumns[] = { { .id = TPGID, .label = "TPGID" }, { .id = TGID, .label = "TGID" }, { .id = PGRP, .label = "PGRP" }, - { .id = SESSION, .label = "SESN" }, + { .id = SESSION, .label = "SID" }, { .id = OOM, .label = "OOM" }, { .id = 0, .label = NULL }, }; @@ -254,6 +274,7 @@ void Process_delete(Object* cast) { #ifdef HAVE_CGROUP free(this->cgroup); #endif + free(this->ttyDevice); free(this); } @@ -285,6 +306,16 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) { return (LinuxProcess_updateIOPriority(this) == ioprio); } +#ifdef HAVE_DELAYACCT +void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) { + if (delay_percent == -1LL) { + xSnprintf(buffer, n, " N/A "); + } else { + xSnprintf(buffer, n, "%4.1f ", delay_percent); + } +} +#endif + void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) { LinuxProcess* lp = (LinuxProcess*) this; bool coloring = this->settings->highlightMegabytes; @@ -292,6 +323,15 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; switch ((int)field) { + case TTY_NR: { + if (lp->ttyDevice) { + xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */); + } else { + attr = CRT_colors[PROCESS_SHADOW]; + xSnprintf(buffer, n, "? "); + } + break; + } case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return; case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return; case M_DRS: Process_humanNumber(str, lp->m_drs * PAGE_SIZE_KB, coloring); return; @@ -321,34 +361,39 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) } #endif #ifdef HAVE_OPENVZ - case CTID: snprintf(buffer, n, "%7u ", lp->ctid); break; - case VPID: snprintf(buffer, n, Process_pidFormat, lp->vpid); break; + case CTID: xSnprintf(buffer, n, "%7u ", lp->ctid); break; + case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break; #endif #ifdef HAVE_VSERVER - case VXID: snprintf(buffer, n, "%5u ", lp->vxid); break; + case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; #endif #ifdef HAVE_CGROUP - case CGROUP: snprintf(buffer, n, "%-10s ", lp->cgroup); break; + case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup); break; #endif - case OOM: snprintf(buffer, n, Process_pidFormat, lp->oom); break; + case OOM: xSnprintf(buffer, n, Process_pidFormat, lp->oom); break; case IO_PRIORITY: { int klass = IOPriority_class(lp->ioPriority); if (klass == IOPRIO_CLASS_NONE) { // see note [1] above - snprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5); + xSnprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5); } else if (klass == IOPRIO_CLASS_BE) { - snprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority)); + xSnprintf(buffer, n, "B%1d ", IOPriority_data(lp->ioPriority)); } else if (klass == IOPRIO_CLASS_RT) { attr = CRT_colors[PROCESS_HIGH_PRIORITY]; - snprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority)); - } else if (lp->ioPriority == IOPriority_Idle) { + xSnprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority)); + } else if (klass == IOPRIO_CLASS_IDLE) { attr = CRT_colors[PROCESS_LOW_PRIORITY]; - snprintf(buffer, n, "id "); + xSnprintf(buffer, n, "id "); } else { - snprintf(buffer, n, "?? "); + xSnprintf(buffer, n, "?? "); } break; } + #ifdef HAVE_DELAYACCT + case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break; + case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break; + case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break; + #endif default: Process_writeField((Process*)this, str, field); return; @@ -410,6 +455,14 @@ long LinuxProcess_compare(const void* v1, const void* v2) { #endif case OOM: return (p2->oom - p1->oom); + #ifdef HAVE_DELAYACCT + case PERCENT_CPU_DELAY: + return (p2->cpu_delay_percent > p1->cpu_delay_percent ? 1 : -1); + case PERCENT_IO_DELAY: + return (p2->blkio_delay_percent > p1->blkio_delay_percent ? 1 : -1); + case PERCENT_SWAP_DELAY: + return (p2->swapin_delay_percent > p1->swapin_delay_percent ? 1 : -1); + #endif case IO_PRIORITY: return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2); default: diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index f2d81aa..9400d7b 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -73,7 +73,12 @@ typedef enum LinuxProcessFields { #endif OOM = 114, IO_PRIORITY = 115, - LAST_PROCESSFIELD = 116, + #ifdef HAVE_DELAYACCT + PERCENT_CPU_DELAY = 116, + PERCENT_IO_DELAY = 117, + PERCENT_SWAP_DELAY = 118, + #endif + LAST_PROCESSFIELD = 119, } LinuxProcessField; #include "IOPriority.h" @@ -116,6 +121,16 @@ typedef struct LinuxProcess_ { char* cgroup; #endif unsigned int oom; + char* ttyDevice; + #ifdef HAVE_DELAYACCT + unsigned long long int delay_read_time; + unsigned long long cpu_delay_total; + unsigned long long blkio_delay_total; + unsigned long long swapin_delay_total; + float cpu_delay_percent; + float blkio_delay_percent; + float swapin_delay_percent; + #endif } LinuxProcess; #ifndef Process_isKernelThread @@ -151,6 +166,10 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio); +#ifdef HAVE_DELAYACCT +void LinuxProcess_printDelay(float delay_percent, char* buffer, int n); +#endif + void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field); long LinuxProcess_compare(const void* v1, const void* v2); diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 712baa5..6f2631a 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -27,6 +27,16 @@ in the source distribution for its full text. #include #include +#ifdef HAVE_DELAYACCT +#include +#include +#include +#include +#include +#include +#include +#endif + /*{ #include "ProcessList.h" @@ -59,11 +69,23 @@ typedef struct CPUData_ { unsigned long long int guestPeriod; } CPUData; +typedef struct TtyDriver_ { + char* path; + unsigned int major; + unsigned int minorFrom; + unsigned int minorTo; +} TtyDriver; + typedef struct LinuxProcessList_ { ProcessList super; - + CPUData* cpus; - + TtyDriver* ttyDrivers; + + #ifdef HAVE_DELAYACCT + struct nl_sock *netlink_socket; + int netlink_family; + #endif } LinuxProcessList; #ifndef PROCDIR @@ -78,6 +100,10 @@ typedef struct LinuxProcessList_ { #define PROCMEMINFOFILE PROCDIR "/meminfo" #endif +#ifndef PROCTTYDRIVERSFILE +#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" +#endif + #ifndef PROC_LINE_LENGTH #define PROC_LINE_LENGTH 512 #endif @@ -87,11 +113,124 @@ typedef struct LinuxProcessList_ { #ifndef CLAMP #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #endif - + +static ssize_t xread(int fd, void *buf, size_t count) { + // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested. + size_t alreadyRead = 0; + for(;;) { + ssize_t res = read(fd, buf, count); + if (res == -1 && errno == EINTR) continue; + if (res > 0) { + buf = ((char*)buf)+res; + count -= res; + alreadyRead += res; + } + if (res == -1) return -1; + if (count == 0 || res == 0) return alreadyRead; + } +} + +static int sortTtyDrivers(const void* va, const void* vb) { + TtyDriver* a = (TtyDriver*) va; + TtyDriver* b = (TtyDriver*) vb; + return (a->major == b->major) ? (a->minorFrom - b->minorFrom) : (a->major - b->major); +} + +static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) { + TtyDriver* ttyDrivers; + int fd = open(PROCTTYDRIVERSFILE, O_RDONLY); + if (fd == -1) + return; + char* buf = NULL; + int bufSize = MAX_READ; + int bufLen = 0; + for(;;) { + buf = realloc(buf, bufSize); + int size = xread(fd, buf + bufLen, MAX_READ); + if (size <= 0) { + buf[bufLen] = '\0'; + close(fd); + break; + } + bufLen += size; + bufSize += MAX_READ; + } + if (bufLen == 0) { + free(buf); + return; + } + int numDrivers = 0; + int allocd = 10; + ttyDrivers = malloc(sizeof(TtyDriver) * allocd); + char* at = buf; + while (*at != '\0') { + at = strchr(at, ' '); // skip first token + while (*at == ' ') at++; // skip spaces + char* token = at; // mark beginning of path + at = strchr(at, ' '); // find end of path + *at = '\0'; at++; // clear and skip + ttyDrivers[numDrivers].path = strdup(token); // save + while (*at == ' ') at++; // skip spaces + token = at; // mark beginning of major + at = strchr(at, ' '); // find end of major + *at = '\0'; at++; // clear and skip + ttyDrivers[numDrivers].major = atoi(token); // save + while (*at == ' ') at++; // skip spaces + token = at; // mark beginning of minorFrom + while (*at >= '0' && *at <= '9') at++; //find end of minorFrom + if (*at == '-') { // if has range + *at = '\0'; at++; // clear and skip + ttyDrivers[numDrivers].minorFrom = atoi(token); // save + token = at; // mark beginning of minorTo + at = strchr(at, ' '); // find end of minorTo + *at = '\0'; at++; // clear and skip + ttyDrivers[numDrivers].minorTo = atoi(token); // save + } else { // no range + *at = '\0'; at++; // clear and skip + ttyDrivers[numDrivers].minorFrom = atoi(token); // save + ttyDrivers[numDrivers].minorTo = atoi(token); // save + } + at = strchr(at, '\n'); // go to end of line + at++; // skip + numDrivers++; + if (numDrivers == allocd) { + allocd += 10; + ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * allocd); + } + } + free(buf); + numDrivers++; + ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * numDrivers); + ttyDrivers[numDrivers - 1].path = NULL; + qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers); + this->ttyDrivers = ttyDrivers; +} + +#ifdef HAVE_DELAYACCT + +static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { + this->netlink_socket = nl_socket_alloc(); + if (this->netlink_socket == NULL) { + return; + } + if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { + return; + } + this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); +} + +#endif + ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); ProcessList* pl = &(this->super); ProcessList_init(pl, Class(LinuxProcess), usersTable, pidWhiteList, userId); + + LinuxProcessList_initTtyDrivers(this); + + #ifdef HAVE_DELAYACCT + LinuxProcessList_initNetlinkSocket(this); + #endif // Update CPU count: FILE* file = fopen(PROCSTATFILE, "r"); @@ -122,25 +261,21 @@ void ProcessList_delete(ProcessList* pl) { LinuxProcessList* this = (LinuxProcessList*) pl; ProcessList_done(pl); free(this->cpus); + if (this->ttyDrivers) { + for(int i = 0; this->ttyDrivers[i].path; i++) { + free(this->ttyDrivers[i].path); + } + free(this->ttyDrivers); + } + #ifdef HAVE_DELAYACCT + if (this->netlink_socket) { + nl_close(this->netlink_socket); + nl_socket_free(this->netlink_socket); + } + #endif free(this); } -static ssize_t xread(int fd, void *buf, size_t count) { - // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested. - size_t alreadyRead = 0; - for(;;) { - ssize_t res = read(fd, buf, count); - if (res == -1 && errno == EINTR) continue; - if (res > 0) { - buf = ((char*)buf)+res; - count -= res; - alreadyRead += res; - } - if (res == -1) return -1; - if (count == 0 || res == 0) return alreadyRead; - } -} - static double jiffy = 0.0; static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) { @@ -152,7 +287,7 @@ static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) { static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command, int* commLen) { LinuxProcess* lp = (LinuxProcess*) process; char filename[MAX_NAME+1]; - snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name); int fd = open(filename, O_RDONLY); if (fd == -1) return false; @@ -221,7 +356,7 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, process->processor = strtol(location, &location, 10); process->time = lp->utime + lp->stime; - + return true; } @@ -230,7 +365,7 @@ static bool LinuxProcessList_statProcessDir(Process* process, const char* dirnam char filename[MAX_NAME+1]; filename[MAX_NAME] = '\0'; - snprintf(filename, MAX_NAME, "%s/%s", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s", dirname, name); struct stat sstat; int statok = stat(filename, &sstat); if (statok == -1) @@ -252,11 +387,20 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna char filename[MAX_NAME+1]; filename[MAX_NAME] = '\0'; - snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/io", dirname, name); int fd = open(filename, O_RDONLY); if (fd == -1) { process->io_rate_read_bps = -1; process->io_rate_write_bps = -1; + process->io_rchar = -1LL; + process->io_wchar = -1LL; + process->io_syscr = -1LL; + process->io_syscw = -1LL; + process->io_read_bytes = -1LL; + process->io_write_bytes = -1LL; + process->io_cancelled_write_bytes = -1LL; + process->io_rate_read_time = -1LL; + process->io_rate_write_time = -1LL; return; } @@ -312,7 +456,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) { char filename[MAX_NAME+1]; - snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name); int fd = open(filename, O_RDONLY); if (fd == -1) return false; @@ -342,7 +486,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* d return; } char filename[MAX_NAME+1]; - snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name); FILE* file = fopen(filename, "r"); if (!file) return; @@ -365,7 +509,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* d static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) { char filename[MAX_NAME+1]; - snprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name); FILE* file = fopen(filename, "r"); if (!file) { process->cgroup = xStrdup(""); @@ -400,7 +544,7 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* d static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* dirname, const char* name) { char filename[MAX_NAME+1]; - snprintf(filename, MAX_NAME, "%s/%s/status", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/status", dirname, name); FILE* file = fopen(filename, "r"); if (!file) return; @@ -431,10 +575,11 @@ static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirname, const char* name) { char filename[MAX_NAME+1]; - snprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name); FILE* file = fopen(filename, "r"); - if (!file) + if (!file) { return; + } char buffer[PROC_LINE_LENGTH + 1]; if (fgets(buffer, PROC_LINE_LENGTH, file)) { unsigned int oom; @@ -446,6 +591,75 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirn fclose(file); } +#ifdef HAVE_DELAYACCT + +static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) { + struct nlmsghdr *nlhdr; + struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1]; + struct nlattr *nlattr; + struct taskstats *stats; + int rem; + unsigned long long int timeDelta; + LinuxProcess* lp = (LinuxProcess*) linuxProcess; + + nlhdr = nlmsg_hdr(nlmsg); + + if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { + return NL_SKIP; + } + + if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { + stats = nla_data(nla_next(nla_data(nlattr), &rem)); + assert(lp->super.pid == stats->ac_pid); + timeDelta = (stats->ac_etime*1000 - lp->delay_read_time); + #define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x; + #define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100); + lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total); + lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total); + lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total); + #undef DELTAPERC + #undef BOUNDS + lp->swapin_delay_total = stats->swapin_delay_total; + lp->blkio_delay_total = stats->blkio_delay_total; + lp->cpu_delay_total = stats->cpu_delay_total; + lp->delay_read_time = stats->ac_etime*1000; + } + return NL_OK; +} + +static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) { + struct nl_msg *msg; + + if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) { + return; + } + + if (! (msg = nlmsg_alloc())) { + return; + } + + if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { + nlmsg_free(msg); + } + + if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) { + nlmsg_free(msg); + } + + if (nl_send_sync(this->netlink_socket, msg) < 0) { + process->swapin_delay_percent = -1LL; + process->blkio_delay_percent = -1LL; + process->cpu_delay_percent = -1LL; + return; + } + + if (nl_recvmsgs_default(this->netlink_socket) < 0) { + return; + } +} + +#endif + static void setCommand(Process* process, const char* command, int len) { if (process->comm && process->commLen >= len) { strncpy(process->comm, command, len + 1); @@ -461,7 +675,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna return true; char filename[MAX_NAME+1]; - snprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name); + xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name); int fd = open(filename, O_RDONLY); if (fd == -1) return false; @@ -471,27 +685,71 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna close(fd); int tokenEnd = 0; int lastChar = 0; - if (amtRead > 0) { - for (int i = 0; i < amtRead; i++) - if (command[i] == '\0' || command[i] == '\n') { - if (tokenEnd == 0) { - tokenEnd = i; - } - command[i] = ' '; - } else { - lastChar = i; + if (amtRead <= 0) { + return false; + } + for (int i = 0; i < amtRead; i++) { + if (command[i] == '\0' || command[i] == '\n') { + if (tokenEnd == 0) { + tokenEnd = i; } + command[i] = ' '; + } else { + lastChar = i; + } } if (tokenEnd == 0) { tokenEnd = amtRead; } command[lastChar + 1] = '\0'; process->basenameOffset = tokenEnd; - setCommand(process, command, lastChar + 1); + setCommand(process, command, lastChar); return true; } +static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned int tty_nr) { + unsigned int maj = major(tty_nr); + unsigned int min = minor(tty_nr); + + int i = -1; + for (;;) { + i++; + if ((!ttyDrivers[i].path) || maj < ttyDrivers[i].major) { + break; + } + if (maj > ttyDrivers[i].major) { + continue; + } + if (min < ttyDrivers[i].minorFrom) { + break; + } + if (min > ttyDrivers[i].minorTo) { + continue; + } + unsigned int idx = min - ttyDrivers[i].minorFrom; + struct stat sstat; + char* fullPath; + for(;;) { + asprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx); + int err = stat(fullPath, &sstat); + if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath; + free(fullPath); + asprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx); + err = stat(fullPath, &sstat); + if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath; + free(fullPath); + if (idx == min) break; + idx = min; + } + int err = stat(ttyDrivers[i].path, &sstat); + if (err == 0 && tty_nr == sstat.st_rdev) return strdup(ttyDrivers[i].path); + } + char* out; + asprintf(&out, "/dev/%u:%u", maj, min); + return out; +} + static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) { ProcessList* pl = (ProcessList*) this; DIR* dir; @@ -538,7 +796,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* LinuxProcess* lp = (LinuxProcess*) proc; char subdirname[MAX_NAME+1]; - snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name); + xSnprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name); LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv); #ifdef HAVE_TASKSTATS @@ -554,8 +812,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* char command[MAX_NAME+1]; unsigned long long int lasttimes = (lp->utime + lp->stime); int commLen = 0; + unsigned int tty_nr = proc->tty_nr; if (! LinuxProcessList_readStatFile(proc, dirname, name, command, &commLen)) goto errorReadingProcess; + if (tty_nr != proc->tty_nr && this->ttyDrivers) { + free(lp->ttyDevice); + lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr); + } if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO) LinuxProcess_updateIOPriority(lp); float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0; @@ -595,6 +858,10 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* } } + #ifdef HAVE_DELAYACCT + LinuxProcessList_readDelayAcctData(this, lp); + #endif + #ifdef HAVE_CGROUP if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) LinuxProcessList_readCGroupFile(lp, dirname, name); diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 9772581..5005220 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -9,6 +9,9 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ +#ifdef HAVE_DELAYACCT +#endif + #include "ProcessList.h" @@ -40,11 +43,23 @@ typedef struct CPUData_ { unsigned long long int guestPeriod; } CPUData; +typedef struct TtyDriver_ { + char* path; + unsigned int major; + unsigned int minorFrom; + unsigned int minorTo; +} TtyDriver; + typedef struct LinuxProcessList_ { ProcessList super; - + CPUData* cpus; - + TtyDriver* ttyDrivers; + + #ifdef HAVE_DELAYACCT + struct nl_sock *netlink_socket; + int netlink_family; + #endif } LinuxProcessList; #ifndef PROCDIR @@ -59,6 +74,10 @@ typedef struct LinuxProcessList_ { #define PROCMEMINFOFILE PROCDIR "/meminfo" #endif +#ifndef PROCTTYDRIVERSFILE +#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" +#endif + #ifndef PROC_LINE_LENGTH #define PROC_LINE_LENGTH 512 #endif @@ -67,7 +86,11 @@ typedef struct LinuxProcessList_ { #ifndef CLAMP #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #endif - + +#ifdef HAVE_DELAYACCT + +#endif + ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); void ProcessList_delete(ProcessList* pl); @@ -89,6 +112,10 @@ void ProcessList_delete(ProcessList* pl); #endif +#ifdef HAVE_DELAYACCT + +#endif + void ProcessList_goThroughEntries(ProcessList* super); #endif diff --git a/linux/Platform.c b/linux/Platform.c index 04360ca..025abff 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -47,7 +47,7 @@ ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_R int Platform_numberOfFields = LAST_PROCESSFIELD; -SignalItem Platform_signals[] = { +const SignalItem Platform_signals[] = { { .name = " 0 Cancel", .number = 0 }, { .name = " 1 SIGHUP", .number = 1 }, { .name = " 2 SIGINT", .number = 2 }, @@ -84,7 +84,7 @@ SignalItem Platform_signals[] = { { .name = "31 SIGSYS", .number = 31 }, }; -unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem); +const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem); static Htop_Reaction Platform_actionSetIOPriority(State* st) { Panel* panel = st->panel; @@ -215,7 +215,7 @@ void Platform_setSwapValues(Meter* this) { char* Platform_getProcessEnv(pid_t pid) { char procname[32+1]; - snprintf(procname, 32, "/proc/%d/environ", pid); + xSnprintf(procname, 32, "/proc/%d/environ", pid); FILE* fd = fopen(procname, "r"); char *env = NULL; if (fd) { diff --git a/linux/Platform.h b/linux/Platform.h index b0d69fb..b0456e5 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -23,9 +23,9 @@ extern ProcessField Platform_defaultFields[]; extern int Platform_numberOfFields; -extern SignalItem Platform_signals[]; +extern const SignalItem Platform_signals[]; -extern unsigned int Platform_numberOfSignals; +extern const unsigned int Platform_numberOfSignals; void Platform_setBindings(Htop_Action* keys); -- cgit v1.2.3