From f3147ea2d1598914c2db53e8cfb34c8ff81e2ff4 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Thu, 27 Aug 2020 07:48:10 +0200 Subject: New upstream version 3.0.0 --- linux/Battery.c | 38 +++-- linux/Battery.h | 2 +- linux/IOPriority.c | 3 +- linux/IOPriority.h | 3 +- linux/IOPriorityPanel.c | 1 - linux/IOPriorityPanel.h | 5 +- linux/LinuxCRT.c | 2 +- linux/LinuxCRT.h | 2 +- linux/LinuxProcess.c | 55 ++++++-- linux/LinuxProcess.h | 33 +++-- linux/LinuxProcessList.c | 344 +++++++++++++++++++++++++++++++++++++-------- linux/LinuxProcessList.h | 33 +++-- linux/Platform.c | 57 +++++++- linux/Platform.h | 21 +-- linux/PressureStallMeter.c | 133 ++++++++++++++++++ linux/PressureStallMeter.h | 25 ++++ 16 files changed, 632 insertions(+), 125 deletions(-) create mode 100644 linux/PressureStallMeter.c create mode 100644 linux/PressureStallMeter.h (limited to 'linux') diff --git a/linux/Battery.c b/linux/Battery.c index aedacab..4014a50 100644 --- a/linux/Battery.c +++ b/linux/Battery.c @@ -64,7 +64,7 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short } char* line = NULL; - for (unsigned short int i = 0; i < lineNum; i++) { + for (unsigned short int j = 0; j < lineNum; j++) { free(line); line = String_readLine(file); if (!line) break; @@ -78,7 +78,7 @@ static unsigned long int parseBatInfo(const char *fileName, const unsigned short const unsigned long int foundNum = atoi(foundNumStr); free(foundNumStr); free(line); - + total += foundNum; } @@ -175,7 +175,7 @@ static inline ssize_t xread(int fd, void *buf, size_t count) { } static void Battery_getSysData(double* level, ACPresence* isOnAC) { - + *level = 0; *isOnAC = AC_ERROR; @@ -193,17 +193,27 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) { char* entryName = (char *) dirEntry->d_name; const char filePath[50]; - if (entryName[0] == 'B' && entryName[1] == 'A' && entryName[2] == 'T') { - + xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName); + int fd1 = open(filePath, O_RDONLY); + if (fd1 == -1) + continue; + + char type[8]; + ssize_t typelen = xread(fd1, type, 7); + close(fd1); + if (typelen < 1) + continue; + + if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') { xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); - int fd = open(filePath, O_RDONLY); - if (fd == -1) { + int fd2 = open(filePath, O_RDONLY); + if (fd2 == -1) { closedir(dir); return; } char buffer[1024]; - ssize_t buflen = xread(fd, buffer, 1023); - close(fd); + ssize_t buflen = xread(fd2, buffer, 1023); + close(fd2); if (buflen < 1) { closedir(dir); return; @@ -247,20 +257,20 @@ static void Battery_getSysData(double* level, ACPresence* isOnAC) { if (*isOnAC != AC_ERROR) { continue; } - + xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName); - int fd = open(filePath, O_RDONLY); - if (fd == -1) { + int fd3 = open(filePath, O_RDONLY); + if (fd3 == -1) { closedir(dir); return; } char buffer[2] = ""; for(;;) { - ssize_t res = read(fd, buffer, 1); + ssize_t res = read(fd3, buffer, 1); if (res == -1 && errno == EINTR) continue; break; } - close(fd); + close(fd3); if (buffer[0] == '0') { *isOnAC = AC_ABSENT; } else if (buffer[0] == '1') { diff --git a/linux/Battery.h b/linux/Battery.h index cfb6c32..261cff0 100644 --- a/linux/Battery.h +++ b/linux/Battery.h @@ -29,6 +29,6 @@ Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat // READ FROM /sys // ---------------------------------------- -void Battery_getData(double* level, ACPresence* isOnAC); +extern void Battery_getData(double* level, ACPresence* isOnAC); #endif diff --git a/linux/IOPriority.c b/linux/IOPriority.c index dd7c84a..087f7f9 100644 --- a/linux/IOPriority.c +++ b/linux/IOPriority.c @@ -4,7 +4,7 @@ htop - IOPriority.c Released under the GNU GPL, see the COPYING file in the source distribution for its full text. -Based on ionice, +Based on ionice, Copyright (C) 2005 Jens Axboe Released under the terms of the GNU General Public License version 2 */ @@ -38,4 +38,3 @@ typedef int IOPriority; #define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7) }*/ - diff --git a/linux/IOPriority.h b/linux/IOPriority.h index 148e344..a4733e8 100644 --- a/linux/IOPriority.h +++ b/linux/IOPriority.h @@ -8,7 +8,7 @@ htop - IOPriority.h Released under the GNU GPL, see the COPYING file in the source distribution for its full text. -Based on ionice, +Based on ionice, Copyright (C) 2005 Jens Axboe Released under the terms of the GNU General Public License version 2 */ @@ -39,5 +39,4 @@ typedef int IOPriority; #define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7) - #endif diff --git a/linux/IOPriorityPanel.c b/linux/IOPriorityPanel.c index 2b315b8..22735bb 100644 --- a/linux/IOPriorityPanel.c +++ b/linux/IOPriorityPanel.c @@ -41,4 +41,3 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) { IOPriority IOPriorityPanel_getIOPriority(Panel* this) { return (IOPriority) ( ((ListItem*) Panel_getSelected(this))->key ); } - diff --git a/linux/IOPriorityPanel.h b/linux/IOPriorityPanel.h index 9f77a4d..906296e 100644 --- a/linux/IOPriorityPanel.h +++ b/linux/IOPriorityPanel.h @@ -13,9 +13,8 @@ in the source distribution for its full text. #include "IOPriority.h" #include "ListItem.h" -Panel* IOPriorityPanel_new(IOPriority currPrio); - -IOPriority IOPriorityPanel_getIOPriority(Panel* this); +extern Panel* IOPriorityPanel_new(IOPriority currPrio); +extern IOPriority IOPriorityPanel_getIOPriority(Panel* this); #endif diff --git a/linux/LinuxCRT.c b/linux/LinuxCRT.c index 5b2a21f..c65b782 100644 --- a/linux/LinuxCRT.c +++ b/linux/LinuxCRT.c @@ -17,7 +17,7 @@ void CRT_handleSIGSEGV(int sgn) { (void) sgn; CRT_done(); #ifdef __linux - fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n"); + fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at https://htop.dev\n"); #ifdef HAVE_EXECINFO_H size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *)); fprintf(stderr, "\n Please include in your report the following backtrace: \n"); diff --git a/linux/LinuxCRT.h b/linux/LinuxCRT.h index f8c43e4..966dfec 100644 --- a/linux/LinuxCRT.h +++ b/linux/LinuxCRT.h @@ -12,6 +12,6 @@ in the source distribution for its full text. #ifdef HAVE_EXECINFO_H #endif -void CRT_handleSIGSEGV(int sgn); +extern void CRT_handleSIGSEGV(int sgn); #endif diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 09ccbe1..3a15049 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -1,6 +1,7 @@ /* htop - LinuxProcess.c (C) 2014 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -15,6 +16,7 @@ in the source distribution for its full text. #include #include #include +#include /*{ @@ -23,6 +25,7 @@ in the source distribution for its full text. #define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OOM 0x1000 +#define PROCESS_FLAG_LINUX_SMAPS 0x2000 typedef enum UnsupportedProcessFields { FLAGS = 9, @@ -86,7 +89,10 @@ typedef enum LinuxProcessFields { PERCENT_IO_DELAY = 117, PERCENT_SWAP_DELAY = 118, #endif - LAST_PROCESSFIELD = 119, + M_PSS = 119, + M_SWAP = 120, + M_PSSWP = 121, + LAST_PROCESSFIELD = 122, } LinuxProcessField; #include "IOPriority.h" @@ -102,10 +108,14 @@ typedef struct LinuxProcess_ { unsigned long long int cutime; unsigned long long int cstime; long m_share; + long m_pss; + long m_swap; + long m_psswp; long m_trs; long m_drs; long m_lrs; long m_dt; + unsigned long long starttime; #ifdef HAVE_TASKSTATS unsigned long long io_rchar; unsigned long long io_wchar; @@ -115,7 +125,7 @@ typedef struct LinuxProcess_ { unsigned long long io_write_bytes; unsigned long long io_cancelled_write_bytes; unsigned long long io_rate_read_time; - unsigned long long io_rate_write_time; + unsigned long long io_rate_write_time; double io_rate_read_bps; double io_rate_write_bps; #endif @@ -143,7 +153,7 @@ typedef struct LinuxProcess_ { } LinuxProcess; #ifndef Process_isKernelThread -#define Process_isKernelThread(_process) ((LinuxProcess*)(_process)->isKernelThread) +#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread) #endif #ifndef Process_isUserlandThread @@ -152,6 +162,9 @@ typedef struct LinuxProcess_ { }*/ +/* semi-global */ +long long btime; + ProcessFieldData Process_fields[] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, @@ -199,7 +212,7 @@ ProcessFieldData Process_fields[] = { [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, }, [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process", .flags = 0, }, [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process", .flags = 0, }, - [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, + [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, }, [PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, }, [PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, @@ -235,6 +248,9 @@ ProcessFieldData Process_fields[] = { [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 + [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, }, + [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, }, + [M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, }, [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, }; @@ -299,12 +315,12 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) { return ioprio; } -bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) { +bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio) { // Other OSes masquerading as Linux (NetBSD?) don't have this syscall #ifdef SYS_ioprio_set - syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio); + syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio.i); #endif - return (LinuxProcess_updateIOPriority(this) == ioprio); + return (LinuxProcess_updateIOPriority(this) == ioprio.i); } #ifdef HAVE_DELAYACCT @@ -340,10 +356,20 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) case M_LRS: Process_humanNumber(str, lp->m_lrs * PAGE_SIZE_KB, coloring); return; case M_TRS: Process_humanNumber(str, lp->m_trs * PAGE_SIZE_KB, coloring); return; case M_SHARE: Process_humanNumber(str, lp->m_share * PAGE_SIZE_KB, coloring); return; + case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return; + case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return; + case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return; case UTIME: Process_printTime(str, lp->utime); return; case STIME: Process_printTime(str, lp->stime); return; case CUTIME: Process_printTime(str, lp->cutime); return; case CSTIME: Process_printTime(str, lp->cstime); return; + case STARTTIME: { + struct tm date; + time_t starttimewall = btime + (lp->starttime / sysconf(_SC_CLK_TCK)); + (void) localtime_r(&starttimewall, &date); + strftime(buffer, n, ((starttimewall > time(NULL) - 86400) ? "%R " : "%b%d "), &date); + break; + } #ifdef HAVE_TASKSTATS case RCHAR: Process_colorNumber(str, lp->io_rchar, coloring); return; case WCHAR: Process_colorNumber(str, lp->io_wchar, coloring); return; @@ -383,7 +409,7 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) attr = CRT_colors[PROCESS_HIGH_PRIORITY]; xSnprintf(buffer, n, "R%1d ", IOPriority_data(lp->ioPriority)); } else if (klass == IOPRIO_CLASS_IDLE) { - attr = CRT_colors[PROCESS_LOW_PRIORITY]; + attr = CRT_colors[PROCESS_LOW_PRIORITY]; xSnprintf(buffer, n, "id "); } else { xSnprintf(buffer, n, "?? "); @@ -424,10 +450,22 @@ long LinuxProcess_compare(const void* v1, const void* v2) { return (p2->m_trs - p1->m_trs); case M_SHARE: return (p2->m_share - p1->m_share); + case M_PSS: + return (p2->m_pss - p1->m_pss); + case M_SWAP: + return (p2->m_swap - p1->m_swap); + case M_PSSWP: + return (p2->m_psswp - p1->m_psswp); case UTIME: diff = p2->utime - p1->utime; goto test_diff; case CUTIME: diff = p2->cutime - p1->cutime; goto test_diff; case STIME: diff = p2->stime - p1->stime; goto test_diff; case CSTIME: diff = p2->cstime - p1->cstime; goto test_diff; + case STARTTIME: { + if (p1->starttime == p2->starttime) + return (p1->super.pid - p2->super.pid); + else + return (p1->starttime - p2->starttime); + } #ifdef HAVE_TASKSTATS case RCHAR: diff = p2->io_rchar - p1->io_rchar; goto test_diff; case WCHAR: diff = p2->io_wchar - p1->io_wchar; goto test_diff; @@ -476,4 +514,3 @@ long LinuxProcess_compare(const void* v1, const void* v2) { bool Process_isThread(Process* this) { return (Process_isUserlandThread(this) || Process_isKernelThread(this)); } - diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index d75fe89..7c0a11f 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -5,6 +5,7 @@ /* htop - LinuxProcess.h (C) 2014 Hisham H. Muhammad +(C) 2020 Red Hat, Inc. All Rights Reserved. Released under the GNU GPL, see the COPYING file in the source distribution for its full text. */ @@ -15,6 +16,7 @@ in the source distribution for its full text. #define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OOM 0x1000 +#define PROCESS_FLAG_LINUX_SMAPS 0x2000 typedef enum UnsupportedProcessFields { FLAGS = 9, @@ -78,7 +80,10 @@ typedef enum LinuxProcessFields { PERCENT_IO_DELAY = 117, PERCENT_SWAP_DELAY = 118, #endif - LAST_PROCESSFIELD = 119, + M_PSS = 119, + M_SWAP = 120, + M_PSSWP = 121, + LAST_PROCESSFIELD = 122, } LinuxProcessField; #include "IOPriority.h" @@ -94,10 +99,14 @@ typedef struct LinuxProcess_ { unsigned long long int cutime; unsigned long long int cstime; long m_share; + long m_pss; + long m_swap; + long m_psswp; long m_trs; long m_drs; long m_lrs; long m_dt; + unsigned long long starttime; #ifdef HAVE_TASKSTATS unsigned long long io_rchar; unsigned long long io_wchar; @@ -107,7 +116,7 @@ typedef struct LinuxProcess_ { unsigned long long io_write_bytes; unsigned long long io_cancelled_write_bytes; unsigned long long io_rate_read_time; - unsigned long long io_rate_write_time; + unsigned long long io_rate_write_time; double io_rate_read_bps; double io_rate_write_bps; #endif @@ -143,15 +152,18 @@ typedef struct LinuxProcess_ { #endif +/* semi-global */ +extern long long btime; + extern ProcessFieldData Process_fields[]; extern ProcessPidColumn Process_pidColumns[]; extern ProcessClass LinuxProcess_class; -LinuxProcess* LinuxProcess_new(Settings* settings); +extern LinuxProcess* LinuxProcess_new(Settings* settings); -void Process_delete(Object* cast); +extern void Process_delete(Object* cast); /* [1] Note that before kernel 2.6.26 a process that has not asked for @@ -163,19 +175,18 @@ extern io_priority; */ #define LinuxProcess_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->super.nice + 20) / 5) : p_->ioPriority) -IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); +extern IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this); -bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio); +extern bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio); #ifdef HAVE_DELAYACCT -void LinuxProcess_printDelay(float delay_percent, char* buffer, int n); +extern 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); +extern void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field); -bool Process_isThread(Process* this); +extern long LinuxProcess_compare(const void* v1, const void* v2); +extern bool Process_isThread(Process* this); #endif diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 2edd042..6d3d034 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -28,8 +28,7 @@ in the source distribution for its full text. #include #ifdef MAJOR_IN_MKDEV #include -#elif defined(MAJOR_IN_SYSMACROS) || \ - (defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H) +#elif defined(MAJOR_IN_SYSMACROS) #include #endif @@ -46,6 +45,9 @@ in the source distribution for its full text. /*{ #include "ProcessList.h" +#include "zfs/ZfsArcStats.h" + +extern long long btime; typedef struct CPUData_ { unsigned long long int totalTime; @@ -60,7 +62,7 @@ typedef struct CPUData_ { unsigned long long int softIrqTime; unsigned long long int stealTime; unsigned long long int guestTime; - + unsigned long long int totalPeriod; unsigned long long int userPeriod; unsigned long long int systemPeriod; @@ -73,6 +75,8 @@ typedef struct CPUData_ { unsigned long long int softIrqPeriod; unsigned long long int stealPeriod; unsigned long long int guestPeriod; + + double frequency; } CPUData; typedef struct TtyDriver_ { @@ -84,20 +88,27 @@ typedef struct TtyDriver_ { typedef struct LinuxProcessList_ { ProcessList super; - + CPUData* cpus; TtyDriver* ttyDrivers; - + bool haveSmapsRollup; + #ifdef HAVE_DELAYACCT struct nl_sock *netlink_socket; int netlink_family; #endif + + ZfsArcStats zfs; } LinuxProcessList; #ifndef PROCDIR #define PROCDIR "/proc" #endif +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + #ifndef PROCSTATFILE #define PROCSTATFILE PROCDIR "/stat" #endif @@ -106,12 +117,16 @@ typedef struct LinuxProcessList_ { #define PROCMEMINFOFILE PROCDIR "/meminfo" #endif +#ifndef PROCARCSTATSFILE +#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats" +#endif + #ifndef PROCTTYDRIVERSFILE #define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" #endif #ifndef PROC_LINE_LENGTH -#define PROC_LINE_LENGTH 512 +#define PROC_LINE_LENGTH 4096 #endif }*/ @@ -230,26 +245,41 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { 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 + // Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+) + FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r"); + if(file != NULL) { + this->haveSmapsRollup = true; + fclose(file); + } else { + this->haveSmapsRollup = false; + } + // Update CPU count: - FILE* file = fopen(PROCSTATFILE, "r"); + file = fopen(PROCSTATFILE, "r"); if (file == NULL) { CRT_fatalError("Cannot open " PROCSTATFILE); } - char buffer[PROC_LINE_LENGTH + 1]; - int cpus = -1; + int cpus = 0; do { - cpus++; - char * s = fgets(buffer, PROC_LINE_LENGTH, file); - (void) s; - } while (String_startsWith(buffer, "cpu")); + char buffer[PROC_LINE_LENGTH + 1]; + if (fgets(buffer, PROC_LINE_LENGTH + 1, file) == NULL) { + CRT_fatalError("No btime in " PROCSTATFILE); + } else if (String_startsWith(buffer, "cpu")) { + cpus++; + } else if (String_startsWith(buffer, "btime ")) { + sscanf(buffer, "btime %lld\n", &btime); + break; + } + } while(true); + fclose(file); pl->cpuCount = MAX(cpus - 1, 1); @@ -259,7 +289,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui this->cpus[i].totalTime = 1; this->cpus[i].totalPeriod = 1; } - return pl; } @@ -312,7 +341,7 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, location += 2; char *end = strrchr(location, ')'); if (!end) return false; - + int commsize = end - location; memcpy(command, location, commsize); command[commsize] = '\0'; @@ -355,19 +384,22 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, location += 1; process->nlwp = strtol(location, &location, 10); location += 1; - for (int i=0; i<17; i++) location = strchr(location, ' ')+1; + location = strchr(location, ' ')+1; + lp->starttime = strtoll(location, &location, 10); + location += 1; + for (int i=0; i<15; i++) location = strchr(location, ' ')+1; process->exit_signal = strtol(location, &location, 10); location += 1; assert(location != NULL); process->processor = strtol(location, &location, 10); - + process->time = lp->utime + lp->stime; - + return true; } -static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name, time_t curTime) { +static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name) { char filename[MAX_NAME+1]; filename[MAX_NAME] = '\0'; @@ -377,13 +409,6 @@ static bool LinuxProcessList_statProcessDir(Process* process, const char* dirnam if (statok == -1) return false; process->st_uid = sstat.st_uid; - - struct tm date; - time_t ctime = sstat.st_ctime; - process->starttime_ctime = ctime; - (void) localtime_r((time_t*) &ctime, &date); - strftime(process->starttime_show, 7, ((ctime > curTime - 86400) ? "%R " : "%b%d "), &date); - return true; } @@ -409,7 +434,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna process->io_rate_write_time = -1LL; return; } - + char buffer[1024]; ssize_t buflen = xread(fd, buffer, 1023); close(fd); @@ -426,7 +451,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna process->io_rchar = strtoull(line+7, NULL, 10); else if (strncmp(line+1, "ead_bytes: ", 11) == 0) { process->io_read_bytes = strtoull(line+12, NULL, 10); - process->io_rate_read_bps = + process->io_rate_read_bps = ((double)(process->io_read_bytes - last_read))/(((double)(now - process->io_rate_read_time))/1000); process->io_rate_read_time = now; } @@ -436,7 +461,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna process->io_wchar = strtoull(line+7, NULL, 10); else if (strncmp(line+1, "rite_bytes: ", 12) == 0) { process->io_write_bytes = strtoull(line+13, NULL, 10); - process->io_rate_write_bps = + process->io_rate_write_bps = ((double)(process->io_write_bytes - last_write))/(((double)(now - process->io_rate_write_time))/1000); process->io_rate_write_time = now; } @@ -483,6 +508,62 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* di return (errno == 0); } +static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* dirname, const char* name, bool haveSmapsRollup) { + //http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719 + //kernel will return data in chunks of size PAGE_SIZE or less. + + char buffer[PAGE_SIZE];// 4k + char *start,*end; + ssize_t nread=0; + int tmp=0; + if(haveSmapsRollup) {// only available in Linux 4.14+ + snprintf(buffer, PAGE_SIZE-1, "%s/%s/smaps_rollup", dirname, name); + } else { + snprintf(buffer, PAGE_SIZE-1, "%s/%s/smaps", dirname, name); + } + int fd = open(buffer, O_RDONLY); + if (fd == -1) + return false; + + process->m_pss = 0; + process->m_swap = 0; + process->m_psswp = 0; + + while ( ( nread = read(fd,buffer, sizeof(buffer)) ) > 0 ){ + start = (char *)&buffer; + end = start + nread; + do{//parse 4k block + + if( (tmp = (end - start)) > 0 && + (start = memmem(start,tmp,"\nPss:",5)) != NULL ) + { + process->m_pss += strtol(start+5, &start, 10); + start += 3;//now we must be at the end of line "Pss: 0 kB" + }else + break; //read next 4k block + + if( (tmp = (end - start)) > 0 && + (start = memmem(start,tmp,"\nSwap:",6)) != NULL ) + { + process->m_swap += strtol(start+6, &start, 10); + start += 3; + }else + break; + + if( (tmp = (end - start)) > 0 && + (start = memmem(start,tmp,"\nSwapPss:",9)) != NULL ) + { + process->m_psswp += strtol(start+9, &start, 10); + start += 3; + }else + break; + + }while(1); + }//while read + close(fd); + return true; +} + #ifdef HAVE_OPENVZ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* dirname, const char* name) { @@ -658,7 +739,7 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc process->cpu_delay_percent = -1LL; return; } - + if (nl_recvmsgs_default(this->netlink_socket) < 0) { return; } @@ -682,14 +763,18 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna int fd = open(filename, O_RDONLY); if (fd == -1) return false; - + char command[4096+1]; // max cmdline length on Linux int amtRead = xread(fd, command, sizeof(command) - 1); close(fd); - int tokenEnd = 0; + int tokenEnd = 0; int lastChar = 0; if (amtRead == 0) { - ((LinuxProcess*)process)->isKernelThread = true; + if (process->state == 'Z') { + process->basenameOffset = 0; + } else { + ((LinuxProcess*)process)->isKernelThread = true; + } return true; } else if (amtRead < 0) { return false; @@ -709,7 +794,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna } command[lastChar + 1] = '\0'; process->basenameOffset = tokenEnd; - setCommand(process, command, lastChar); + setCommand(process, command, lastChar + 1); return true; } @@ -723,13 +808,13 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned in 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; } @@ -737,11 +822,11 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned in struct stat sstat; char* fullPath; for(;;) { - asprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx); + xAsprintf(&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); + xAsprintf(&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); @@ -752,7 +837,7 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned in if (err == 0 && tty_nr == sstat.st_rdev) return strdup(ttyDrivers[i].path); } char* out; - asprintf(&out, "/dev/%u:%u", maj, min); + xAsprintf(&out, "/dev/%u:%u", maj, min); return out; } @@ -762,7 +847,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* struct dirent* entry; Settings* settings = pl->settings; - time_t curTime = tv.tv_sec; #ifdef HAVE_TASKSTATS unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL; #endif @@ -788,17 +872,17 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* // filename is a number: process directory int pid = atoi(name); - + if (parent && pid == parent->pid) continue; - if (pid <= 0) + if (pid <= 0) continue; bool preExisting = false; Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_New) LinuxProcess_new); proc->tgid = parent ? parent->pid : pid; - + LinuxProcess* lp = (LinuxProcess*) proc; char subdirname[MAX_NAME+1]; @@ -813,6 +897,21 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* if (! LinuxProcessList_readStatmFile(lp, dirname, name)) goto errorReadingProcess; + if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)){ + if (!parent){ + // Read smaps file of each process only every second pass to improve performance + static int smaps_flag = 0; + if ((pid & 1) == smaps_flag){ + LinuxProcessList_readSmapsFile(lp, dirname, name, this->haveSmapsRollup); + } + if (pid == 1) { + smaps_flag = !smaps_flag; + } + } else { + lp->m_pss = ((LinuxProcess*)parent)->m_pss; + } + } + proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); char command[MAX_NAME+1]; @@ -834,7 +933,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* if(!preExisting) { - if (! LinuxProcessList_statProcessDir(proc, dirname, name, curTime)) + if (! LinuxProcessList_statProcessDir(proc, dirname, name)) goto errorReadingProcess; proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid); @@ -844,7 +943,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* LinuxProcessList_readOpenVZData(lp, dirname, name); } #endif - + #ifdef HAVE_VSERVER if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) { LinuxProcessList_readVServerData(lp, dirname, name); @@ -872,7 +971,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) LinuxProcessList_readCGroupFile(lp, dirname, name); #endif - + if (settings->flags & PROCESS_FLAG_LINUX_OOM) LinuxProcessList_readOomData(lp, dirname, name); @@ -925,30 +1024,30 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { char buffer[128]; while (fgets(buffer, 128, file)) { - #define tryRead(label, variable) (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %32llu kB", variable)) + #define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %32llu kB", variable)) { break; } } while(0) switch (buffer[0]) { case 'M': - if (tryRead("MemTotal:", &this->totalMem)) {} - else if (tryRead("MemFree:", &this->freeMem)) {} - else if (tryRead("MemShared:", &this->sharedMem)) {} + tryRead("MemTotal:", &this->totalMem); + tryRead("MemFree:", &this->freeMem); + tryRead("MemShared:", &this->sharedMem); break; case 'B': - if (tryRead("Buffers:", &this->buffersMem)) {} + tryRead("Buffers:", &this->buffersMem); break; case 'C': - if (tryRead("Cached:", &this->cachedMem)) {} + tryRead("Cached:", &this->cachedMem); break; case 'S': switch (buffer[1]) { case 'w': - if (tryRead("SwapTotal:", &this->totalSwap)) {} - else if (tryRead("SwapFree:", &swapFree)) {} + tryRead("SwapTotal:", &this->totalSwap); + tryRead("SwapFree:", &swapFree); break; case 'h': - if (tryRead("Shmem:", &shmem)) {} + tryRead("Shmem:", &shmem); break; case 'R': - if (tryRead("SReclaimable:", &sreclaimable)) {} + tryRead("SReclaimable:", &sreclaimable); break; } break; @@ -962,6 +1061,68 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { fclose(file); } +static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { + unsigned long long int dbufSize; + unsigned long long int dnodeSize; + unsigned long long int bonusSize; + + FILE* file = fopen(PROCARCSTATSFILE, "r"); + if (file == NULL) { + lpl->zfs.enabled = 0; + return; + } + char buffer[128]; + while (fgets(buffer, 128, file)) { + #define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { break; } } while(0) + #define tryReadFlag(label, variable, flag) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { flag = 1; break; } else { flag = 0; } } while(0) + switch (buffer[0]) { + case 'c': + tryRead("c_max", &lpl->zfs.max); + tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed); + break; + case 'u': + tryRead("uncompressed_size", &lpl->zfs.uncompressed); + break; + case 's': + tryRead("size", &lpl->zfs.size); + break; + case 'h': + tryRead("hdr_size", &lpl->zfs.header); + break; + case 'd': + tryRead("dbuf_size", &dbufSize); + tryRead("dnode_size", &dnodeSize); + break; + case 'b': + tryRead("bonus_size", &bonusSize); + break; + case 'a': + tryRead("anon_size", &lpl->zfs.anon); + break; + case 'm': + tryRead("mfu_size", &lpl->zfs.MFU); + tryRead("mru_size", &lpl->zfs.MRU); + break; + } + #undef tryRead + #undef tryReadFlag + } + fclose(file); + + lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0); + lpl->zfs.size /= 1024; + lpl->zfs.max /= 1024; + lpl->zfs.MFU /= 1024; + lpl->zfs.MRU /= 1024; + lpl->zfs.anon /= 1024; + lpl->zfs.header /= 1024; + lpl->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024; + if ( lpl->zfs.isCompressed ) { + lpl->zfs.compressed /= 1024; + lpl->zfs.uncompressed /= 1024; + } +} + static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) { FILE* file = fopen(PROCSTATFILE, "r"); @@ -1026,18 +1187,87 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) { cpuData->stealTime = steal; cpuData->guestTime = virtalltime; cpuData->totalTime = totaltime; + } double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file); return period; } +static inline double LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { + ProcessList* pl = (ProcessList*) this; + Settings* settings = pl->settings; + + int cpus = this->super.cpuCount; + assert(cpus > 0); + + for (int i = 0; i <= cpus; i++) { + CPUData* cpuData = &(this->cpus[i]); + cpuData->frequency = -1; + } + + int numCPUsWithFrequency = 0; + double totalFrequency = 0; + + if (settings->showCPUFrequency) { + FILE* file = fopen(PROCCPUINFOFILE, "r"); + if (file == NULL) { + CRT_fatalError("Cannot open " PROCCPUINFOFILE); + } + + int cpuid = -1; + double frequency; + while (!feof(file)) { + char buffer[PROC_LINE_LENGTH]; + char *ok = fgets(buffer, PROC_LINE_LENGTH, file); + if (!ok) break; + + if ( + (sscanf(buffer, "processor : %d", &cpuid) == 1) || + (sscanf(buffer, "processor: %d", &cpuid) == 1) + ) { + if (cpuid < 0 || cpuid > (cpus - 1)) { + char tmpbuffer[64]; + xSnprintf(tmpbuffer, sizeof(tmpbuffer), PROCCPUINFOFILE " contains out-of-range CPU number %d", cpuid); + CRT_fatalError(tmpbuffer); + } + } else if ( + (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || + (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) + ) { + if (cpuid < 0) { + CRT_fatalError(PROCCPUINFOFILE " is malformed: cpu MHz line without corresponding processor line"); + } + + int cpu = cpuid + 1; + CPUData* cpuData = &(this->cpus[cpu]); + cpuData->frequency = frequency; + numCPUsWithFrequency++; + totalFrequency += frequency; + } else if (buffer[0] == '\n') { + cpuid = -1; + } + } + fclose(file); + + if (numCPUsWithFrequency > 0) { + this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency; + } + } + + double period = (double)this->cpus[0].totalPeriod / cpus; + return period; +} + void ProcessList_goThroughEntries(ProcessList* super) { LinuxProcessList* this = (LinuxProcessList*) super; LinuxProcessList_scanMemoryInfo(super); + LinuxProcessList_scanZfsArcstats(this); double period = LinuxProcessList_scanCPUTime(this); + LinuxProcessList_scanCPUFrequency(this); + struct timeval tv; gettimeofday(&tv, NULL); LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv); diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 0851a10..86e9d3f 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -10,8 +10,7 @@ in the source distribution for its full text. */ #ifdef MAJOR_IN_MKDEV -#elif defined(MAJOR_IN_SYSMACROS) || \ - (defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H) +#elif defined(MAJOR_IN_SYSMACROS) #endif #ifdef HAVE_DELAYACCT @@ -19,6 +18,9 @@ in the source distribution for its full text. #include "ProcessList.h" +#include "zfs/ZfsArcStats.h" + +extern long long btime; typedef struct CPUData_ { unsigned long long int totalTime; @@ -33,7 +35,7 @@ typedef struct CPUData_ { unsigned long long int softIrqTime; unsigned long long int stealTime; unsigned long long int guestTime; - + unsigned long long int totalPeriod; unsigned long long int userPeriod; unsigned long long int systemPeriod; @@ -46,6 +48,8 @@ typedef struct CPUData_ { unsigned long long int softIrqPeriod; unsigned long long int stealPeriod; unsigned long long int guestPeriod; + + double frequency; } CPUData; typedef struct TtyDriver_ { @@ -57,20 +61,27 @@ typedef struct TtyDriver_ { typedef struct LinuxProcessList_ { ProcessList super; - + CPUData* cpus; TtyDriver* ttyDrivers; - + bool haveSmapsRollup; + #ifdef HAVE_DELAYACCT struct nl_sock *netlink_socket; int netlink_family; #endif + + ZfsArcStats zfs; } LinuxProcessList; #ifndef PROCDIR #define PROCDIR "/proc" #endif +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + #ifndef PROCSTATFILE #define PROCSTATFILE PROCDIR "/stat" #endif @@ -79,12 +90,16 @@ typedef struct LinuxProcessList_ { #define PROCMEMINFOFILE PROCDIR "/meminfo" #endif +#ifndef PROCARCSTATSFILE +#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats" +#endif + #ifndef PROCTTYDRIVERSFILE #define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" #endif #ifndef PROC_LINE_LENGTH -#define PROC_LINE_LENGTH 512 +#define PROC_LINE_LENGTH 4096 #endif @@ -96,9 +111,9 @@ typedef struct LinuxProcessList_ { #endif -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); +extern ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); -void ProcessList_delete(ProcessList* pl); +extern void ProcessList_delete(ProcessList* pl); #ifdef HAVE_TASKSTATS @@ -121,6 +136,6 @@ void ProcessList_delete(ProcessList* pl); #endif -void ProcessList_goThroughEntries(ProcessList* super); +extern void ProcessList_goThroughEntries(ProcessList* super); #endif diff --git a/linux/Platform.c b/linux/Platform.c index ab90ca7..0f59fed 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -19,8 +19,11 @@ in the source distribution for its full text. #include "TasksMeter.h" #include "LoadAverageMeter.h" #include "UptimeMeter.h" +#include "PressureStallMeter.h" #include "ClockMeter.h" #include "HostnameMeter.h" +#include "zfs/ZfsArcMeter.h" +#include "zfs/ZfsCompressedArcMeter.h" #include "LinuxProcess.h" #include @@ -41,7 +44,7 @@ in the source distribution for its full text. #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) #endif -ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; +ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; //static ProcessField defaultIoFields[] = { PID, IO_PRIORITY, USER, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, COMM, 0 }; @@ -91,12 +94,12 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) { LinuxProcess* p = (LinuxProcess*) Panel_getSelected(panel); if (!p) return HTOP_OK; - IOPriority ioprio = p->ioPriority; - Panel* ioprioPanel = IOPriorityPanel_new(ioprio); - void* set = Action_pickFromVector(st, ioprioPanel, 21); + IOPriority ioprio1 = p->ioPriority; + Panel* ioprioPanel = IOPriorityPanel_new(ioprio1); + void* set = Action_pickFromVector(st, ioprioPanel, 21, true); if (set) { - IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel); - bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (Arg){ .i = ioprio }, NULL); + IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel); + bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (Arg){ .i = ioprio2 }, NULL); if (!ok) beep(); } @@ -121,11 +124,21 @@ MeterClass* Platform_meterTypes[] = { &HostnameMeter_class, &AllCPUsMeter_class, &AllCPUs2Meter_class, + &AllCPUs4Meter_class, &LeftCPUsMeter_class, &RightCPUsMeter_class, &LeftCPUs2Meter_class, &RightCPUs2Meter_class, + &LeftCPUs4Meter_class, + &RightCPUs4Meter_class, &BlankMeter_class, + &PressureStallCPUSomeMeter_class, + &PressureStallIOSomeMeter_class, + &PressureStallIOFullMeter_class, + &PressureStallMemorySomeMeter_class, + &PressureStallMemoryFullMeter_class, + &ZfsArcMeter_class, + &ZfsCompressedArcMeter_class, NULL }; @@ -192,6 +205,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { } percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = cpuData->frequency; + return percent; } @@ -213,6 +229,17 @@ void Platform_setSwapValues(Meter* this) { this->values[0] = pl->usedSwap; } +void Platform_setZfsArcValues(Meter* this) { + LinuxProcessList* lpl = (LinuxProcessList*) this->pl; + + ZfsArcMeter_readStats(this, &(lpl->zfs)); +} + +void Platform_setZfsCompressedArcValues(Meter* this) { + LinuxProcessList* lpl = (LinuxProcessList*) this->pl; + + ZfsCompressedArcMeter_readStats(this, &(lpl->zfs)); +} char* Platform_getProcessEnv(pid_t pid) { char procname[32+1]; xSnprintf(procname, 32, "/proc/%d/environ", pid); @@ -237,3 +264,21 @@ char* Platform_getProcessEnv(pid_t pid) { } return env; } + +void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred) { + *ten = *sixty = *threehundred = 0; + char procname[128+1]; + xSnprintf(procname, 128, PROCDIR "/pressure/%s", file); + FILE *fd = fopen(procname, "r"); + if (!fd) { + *ten = *sixty = *threehundred = NAN; + return; + } + int total = fscanf(fd, "some avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred); + if (!some) { + total = fscanf(fd, "full avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred); + } + (void) total; + assert(total == 3); + fclose(fd); +} diff --git a/linux/Platform.h b/linux/Platform.h index b0456e5..bf163a5 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -27,22 +27,27 @@ extern const SignalItem Platform_signals[]; extern const unsigned int Platform_numberOfSignals; -void Platform_setBindings(Htop_Action* keys); +extern void Platform_setBindings(Htop_Action* keys); extern MeterClass* Platform_meterTypes[]; -int Platform_getUptime(); +extern int Platform_getUptime(); -void Platform_getLoadAverage(double* one, double* five, double* fifteen); +extern void Platform_getLoadAverage(double* one, double* five, double* fifteen); -int Platform_getMaxPid(); +extern int Platform_getMaxPid(); -double Platform_setCPUValues(Meter* this, int cpu); +extern double Platform_setCPUValues(Meter* this, int cpu); -void Platform_setMemoryValues(Meter* this); +extern void Platform_setMemoryValues(Meter* this); -void Platform_setSwapValues(Meter* this); +extern void Platform_setSwapValues(Meter* this); -char* Platform_getProcessEnv(pid_t pid); +extern void Platform_setZfsArcValues(Meter* this); + +extern void Platform_setZfsCompressedArcValues(Meter* this); +extern char* Platform_getProcessEnv(pid_t pid); + +extern void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred); #endif diff --git a/linux/PressureStallMeter.c b/linux/PressureStallMeter.c new file mode 100644 index 0000000..56055bf --- /dev/null +++ b/linux/PressureStallMeter.c @@ -0,0 +1,133 @@ +/* +htop - PressureStallMeter.c +(C) 2004-2011 Hisham H. Muhammad +(C) 2019 Ran Benita +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "PressureStallMeter.h" +#include "Platform.h" +#include "CRT.h" + +#include + +/*{ +#include "Meter.h" +}*/ + +static int PressureStallMeter_attributes[] = { + PRESSURE_STALL_TEN, PRESSURE_STALL_SIXTY, PRESSURE_STALL_THREEHUNDRED +}; + +static void PressureStallMeter_updateValues(Meter* this, char* buffer, int len) { + const char *file; + if (strstr(Meter_name(this), "CPU")) { + file = "cpu"; + } else if (strstr(Meter_name(this), "IO")) { + file = "io"; + } else { + file = "memory"; + } + + bool some; + if (strstr(Meter_name(this), "Some")) { + some = true; + } else { + some = false; + } + + Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]); + xSnprintf(buffer, len, "xxxx %.2lf%% %.2lf%% %.2lf%%", this->values[0], this->values[1], this->values[2]); +} + +static void PressureStallMeter_display(Object* cast, RichString* out) { + Meter* this = (Meter*)cast; + char buffer[20]; + xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[0]); + RichString_write(out, CRT_colors[PRESSURE_STALL_TEN], buffer); + xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[1]); + RichString_append(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer); + xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[2]); + RichString_append(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer); +} + +MeterClass PressureStallCPUSomeMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallCPUSome", + .uiName = "Pressure Stall Information, some CPU", + .caption = "Some CPU pressure: " +}; + +MeterClass PressureStallIOSomeMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallIOSome", + .uiName = "Pressure Stall Information, some IO", + .caption = "Some IO pressure: " +}; + +MeterClass PressureStallIOFullMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallIOFull", + .uiName = "Pressure Stall Information, full IO", + .caption = "Full IO pressure: " +}; + +MeterClass PressureStallMemorySomeMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallMemorySome", + .uiName = "Pressure Stall Information, some memory", + .caption = "Some Mem pressure: " +}; + +MeterClass PressureStallMemoryFullMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallMemoryFull", + .uiName = "Pressure Stall Information, full memory", + .caption = "Full Mem pressure: " +}; diff --git a/linux/PressureStallMeter.h b/linux/PressureStallMeter.h new file mode 100644 index 0000000..22b8b97 --- /dev/null +++ b/linux/PressureStallMeter.h @@ -0,0 +1,25 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_PressureStallMeter +#define HEADER_PressureStallMeter +/* +htop - PressureStallMeter.h +(C) 2004-2011 Hisham H. Muhammad +(C) 2019 Ran Benita +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Meter.h" + +extern MeterClass PressureStallCPUSomeMeter_class; + +extern MeterClass PressureStallIOSomeMeter_class; + +extern MeterClass PressureStallIOFullMeter_class; + +extern MeterClass PressureStallMemorySomeMeter_class; + +extern MeterClass PressureStallMemoryFullMeter_class; + +#endif -- cgit v1.2.3