From 61e94c1b5b32a8c4c6db1c8037412ac1ac727833 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 29 Jan 2018 12:08:56 -0200 Subject: [PATCH] Add IPC performance counter for Linux --- Makefile.am | 6 ++++-- configure.ac | 4 +++- linux/LinuxProcess.c | 34 +++++++++++++++++++++++++++++++++- linux/LinuxProcess.h | 13 ++++++++++++- linux/LinuxProcessList.c | 31 +++++++++++++++++++++++++++++++ linux/LinuxProcessList.h | 4 ++++ 6 files changed, 87 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index b2ed1b38..1aa70c27 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,10 +39,12 @@ EnvScreen.h InfoScreen.h XAlloc.h if HTOP_LINUX htop_CFLAGS += -rdynamic myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \ -linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c +linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \ +linux/PerfCounter.c myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \ -linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h +linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h \ +linux/PerfCounter.h endif if HTOP_FREEBSD diff --git a/configure.ac b/configure.ac index 5c333d59..32559745 100644 --- a/configure.ac +++ b/configure.ac @@ -273,8 +273,10 @@ then fi AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes") -if test "x$enable_perfcounters_$my_htop_platform" = xyes_linux +if test "x$enable_perfcounters" = "xyes" -a "$my_htop_platform" = "linux" then + AC_DEFINE(HAVE_PERFCOUNTERS, 1, [Define if hardware performance counter support should be enabled.]) + AC_CHECK_HEADERS([linux/perf_counter.h], [have_perf_counter=yes], [have_perf_counter=no]) diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index c646cdc7..68a5943a 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -18,11 +18,14 @@ in the source distribution for its full text. /*{ +#include "PerfCounter.h" + #define PROCESS_FLAG_LINUX_IOPRIO 0x0100 #define PROCESS_FLAG_LINUX_OPENVZ 0x0200 #define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OOM 0x1000 +#define PROCESS_FLAG_LINUX_HPC 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, + #ifdef HAVE_PERFCOUNTERS + IPC = 119, + #endif + LAST_PROCESSFIELD = 120, } LinuxProcessField; #include "IOPriority.h" @@ -139,6 +145,11 @@ typedef struct LinuxProcess_ { float blkio_delay_percent; float swapin_delay_percent; #endif + #ifdef HAVE_PERFCOUNTERS + PerfCounter* cycleCounter; + PerfCounter* insnCounter; + double ipc; + #endif } LinuxProcess; #ifndef Process_isKernelThread @@ -233,6 +244,9 @@ ProcessFieldData Process_fields[] = { [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 +#ifdef HAVE_PERFCOUNTERS + [IPC] = { .name = "IPC", .title = " IPC ", .description = "Executed instructions per cycle", .flags = PROCESS_FLAG_LINUX_HPC, }, #endif [LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, }, }; @@ -273,6 +287,10 @@ void Process_delete(Object* cast) { Process_done((Process*)cast); #ifdef HAVE_CGROUP free(this->cgroup); +#endif +#ifdef HAVE_PERFCOUNTERS + PerfCounter_delete(this->cycleCounter); + PerfCounter_delete(this->insnCounter); #endif free(this->ttyDevice); free(this); @@ -394,6 +412,16 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) 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 + #ifdef HAVE_PERFCOUNTERS + case IPC: { + if (lp->ipc == -1) { + attr = CRT_colors[PROCESS_SHADOW]; + xSnprintf(buffer, n, " N/A "); break; + } else { + xSnprintf(buffer, n, "%5.2f ", lp->ipc); break; + } + } + #endif default: Process_writeField((Process*)this, str, field); return; @@ -463,6 +491,10 @@ long LinuxProcess_compare(const void* v1, const void* v2) { case PERCENT_SWAP_DELAY: return (p2->swapin_delay_percent > p1->swapin_delay_percent ? 1 : -1); #endif + #ifdef HAVE_PERFCOUNTERS + case IPC: + return (p2->ipc > p1->ipc ? 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 5995dafb..ca22bdda 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -10,11 +10,14 @@ in the source distribution for its full text. */ +#include "PerfCounter.h" + #define PROCESS_FLAG_LINUX_IOPRIO 0x0100 #define PROCESS_FLAG_LINUX_OPENVZ 0x0200 #define PROCESS_FLAG_LINUX_VSERVER 0x0400 #define PROCESS_FLAG_LINUX_CGROUP 0x0800 #define PROCESS_FLAG_LINUX_OOM 0x1000 +#define PROCESS_FLAG_LINUX_HPC 0x2000 typedef enum UnsupportedProcessFields { FLAGS = 9, @@ -78,7 +81,10 @@ typedef enum LinuxProcessFields { PERCENT_IO_DELAY = 117, PERCENT_SWAP_DELAY = 118, #endif - LAST_PROCESSFIELD = 119, + #ifdef HAVE_PERFCOUNTERS + IPC = 119, + #endif + LAST_PROCESSFIELD = 120, } LinuxProcessField; #include "IOPriority.h" @@ -131,6 +137,11 @@ typedef struct LinuxProcess_ { float blkio_delay_percent; float swapin_delay_percent; #endif + #ifdef HAVE_PERFCOUNTERS + PerfCounter* cycleCounter; + PerfCounter* insnCounter; + double ipc; + #endif } LinuxProcess; #ifndef Process_isKernelThread diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index e0aaeb06..6a4486cc 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -666,6 +666,32 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc #endif +#ifdef HAVE_PERFCOUNTERS + +static void LinuxProcessList_readPerfCounters(LinuxProcess* lp) { + if (!lp->cycleCounter) { + lp->cycleCounter = PerfCounter_new(lp->super.pid, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); + } + if (!lp->insnCounter) { + lp->insnCounter = PerfCounter_new(lp->super.pid, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); + } + bool cOk = PerfCounter_read(lp->cycleCounter); + bool iOk = PerfCounter_read(lp->insnCounter); + if (cOk && iOk) { + uint64_t i = PerfCounter_delta(lp->insnCounter); + uint64_t c = PerfCounter_delta(lp->cycleCounter); + if (c > 0) { + lp->ipc = (double)i / c; + } else { + lp->ipc = 0; + } + } else { + lp->ipc = -1; + } +} + +#endif + static void setCommand(Process* process, const char* command, int len) { if (process->comm && process->commLen >= len) { strncpy(process->comm, command, len + 1); @@ -877,6 +903,11 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* if (ss->flags & PROCESS_FLAG_LINUX_OOM) LinuxProcessList_readOomData(lp, dirname, name); + #ifdef HAVE_PERFCOUNTERS + if (ss->flags & PROCESS_FLAG_LINUX_HPC) + LinuxProcessList_readPerfCounters(lp); + #endif + if (proc->state == 'Z' && (proc->basenameOffset == 0)) { proc->basenameOffset = -1; setCommand(proc, command, commLen); diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 0851a102..273d427a 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -121,6 +121,10 @@ void ProcessList_delete(ProcessList* pl); #endif +#ifdef HAVE_PERFCOUNTERS + +#endif + void ProcessList_goThroughEntries(ProcessList* super); #endif