diff options
-rw-r--r-- | configure.ac | 11 | ||||
-rw-r--r-- | htop.1.in | 9 | ||||
-rw-r--r-- | linux/LinuxProcess.c | 44 | ||||
-rw-r--r-- | linux/LinuxProcess.h | 20 | ||||
-rw-r--r-- | linux/LinuxProcessList.c | 112 | ||||
-rw-r--r-- | linux/LinuxProcessList.h | 15 |
6 files changed, 209 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac index f8af8a96..4b266535 100644 --- a/configure.ac +++ b/configure.ac @@ -256,6 +256,17 @@ then AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) fi +AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no") +if test "x$enable_delayacct" = xyes +then + PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"]) + PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"]) + CFLAGS+=" $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" + LIBS+=" $LIBNL3_LIBS $LIBNL3GENL_LIBS" + AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.]) +fi + + # Bail out on errors. # ---------------------------------------------------------------------- if test ! -z "$missing_libraries"; then @@ -370,6 +370,15 @@ The I/O scheduling class followed by the priority if the class supports it: \fBB\fR for Best-effort \fBid\fR for Idle .TP +.B PERCENT_CPU_DELAY (CPUD%) +The percentage of time spent waiting for a CPU (while runnable). Requires CAP_NET_ADMIN. +.TP +.B PERCENT_IO_DELAY (IOD%) +The percentage of time spent waiting for the completion of synchronous block I/O. Requires CAP_NET_ADMIN. +.TP +.B PERCENT_SWAP_DELAY (SWAPD%) +The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN. +.TP .B All other flags Currently unsupported (always displays '-'). diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index bb9e99bb..72408cfa 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" @@ -125,6 +130,15 @@ typedef struct LinuxProcess_ { #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 @@ -215,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, }, }; @@ -287,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; @@ -360,6 +389,11 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) } 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; @@ -421,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 b42808e1..9400d7be 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" @@ -117,6 +122,15 @@ typedef struct LinuxProcess_ { #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 @@ -152,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 4b585e30..6f2631af 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -27,6 +27,16 @@ in the source distribution for its full text. #include <sys/types.h> #include <fcntl.h> +#ifdef HAVE_DELAYACCT +#include <netlink/attr.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <netlink/socket.h> +#include <netlink/msg.h> +#include <linux/taskstats.h> +#endif + /*{ #include "ProcessList.h" @@ -72,6 +82,10 @@ typedef struct LinuxProcessList_ { CPUData* cpus; TtyDriver* ttyDrivers; + #ifdef HAVE_DELAYACCT + struct nl_sock *netlink_socket; + int netlink_family; + #endif } LinuxProcessList; #ifndef PROCDIR @@ -192,6 +206,21 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) { 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); @@ -199,6 +228,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui LinuxProcessList_initTtyDrivers(this); + #ifdef HAVE_DELAYACCT + LinuxProcessList_initNetlinkSocket(this); + #endif + // Update CPU count: FILE* file = fopen(PROCSTATFILE, "r"); if (file == NULL) { @@ -234,6 +267,12 @@ void ProcessList_delete(ProcessList* pl) { } free(this->ttyDrivers); } + #ifdef HAVE_DELAYACCT + if (this->netlink_socket) { + nl_close(this->netlink_socket); + nl_socket_free(this->netlink_socket); + } + #endif free(this); } @@ -552,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); @@ -750,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 ce39f805..5005220a 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" @@ -53,6 +56,10 @@ typedef struct LinuxProcessList_ { CPUData* cpus; TtyDriver* ttyDrivers; + #ifdef HAVE_DELAYACCT + struct nl_sock *netlink_socket; + int netlink_family; + #endif } LinuxProcessList; #ifndef PROCDIR @@ -80,6 +87,10 @@ typedef struct LinuxProcessList_ { #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); @@ -101,6 +112,10 @@ void ProcessList_delete(ProcessList* pl); #endif +#ifdef HAVE_DELAYACCT + +#endif + void ProcessList_goThroughEntries(ProcessList* super); #endif |