diff options
author | André Carvalho <asantostc@gmail.com> | 2017-12-04 00:15:29 -0200 |
---|---|---|
committer | Hisham Muhammad <hisham@gobolinux.org> | 2017-12-04 00:15:29 -0200 |
commit | b7b66b76a5f01ac570d537d27a7e156af248d6f4 (patch) | |
tree | 66f8094a5fc284eb135538020b78b6287dc0df52 /linux/LinuxProcessList.c | |
parent | 52831955c759ecfa5d5f527bdf54f20545fdbcff (diff) |
Adds support for linux delay accounting (#667)
Adds support for showing columns with linux delay accounting.
This information can be read from the netlink interface, and thus we set up a socket to read from that when initializing the LinuxProcessList (LinuxProcessList_initNetlinkSocket). After that, for each process we call LinuxProcessList_readDelayAcctData, which sends a message thru the socket after setting up a callback to get the answer from the Kernel. That callback sets the process total delay time attribute. We then set the delay percent as the percentage of time process cpu time since last scan.
Diffstat (limited to 'linux/LinuxProcessList.c')
-rw-r--r-- | linux/LinuxProcessList.c | 112 |
1 files changed, 112 insertions, 0 deletions
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); |