1 /*
2 htop - PCPProcessList.c
3 (C) 2014 Hisham H. Muhammad
4 (C) 2020-2021 htop dev team
5 (C) 2020-2021 Red Hat, Inc.
6 Released under the GNU GPLv2+, see the COPYING file
7 in the source distribution for its full text.
8 */
9 
10 #include "config.h" // IWYU pragma: keep
11 
12 #include "pcp/PCPProcessList.h"
13 
14 #include <assert.h>
15 #include <limits.h>
16 #include <math.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/time.h>
20 
21 #include "Macros.h"
22 #include "Object.h"
23 #include "Platform.h"
24 #include "Process.h"
25 #include "Settings.h"
26 #include "XUtils.h"
27 
28 #include "pcp/PCPMetric.h"
29 #include "pcp/PCPProcess.h"
30 
31 
PCPProcessList_updateCPUcount(PCPProcessList * this)32 static void PCPProcessList_updateCPUcount(PCPProcessList* this) {
33    ProcessList* pl = &(this->super);
34    pl->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM);
35    unsigned int cpus = Platform_getMaxCPU();
36    if (cpus == pl->existingCPUs)
37       return;
38    if (cpus == 0)
39       cpus = pl->activeCPUs;
40    if (cpus <= 1)
41       cpus = pl->activeCPUs = 1;
42    pl->existingCPUs = cpus;
43 
44    free(this->percpu);
45    free(this->values);
46 
47    this->percpu = xCalloc(cpus, sizeof(pmAtomValue *));
48    for (unsigned int i = 0; i < cpus; i++)
49       this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
50    this->values = xCalloc(cpus, sizeof(pmAtomValue));
51 }
52 
setUser(UsersTable * this,unsigned int uid,int pid,int offset)53 static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) {
54    char* name = Hashtable_get(this->users, uid);
55    if (name)
56       return name;
57 
58    pmAtomValue value;
59    if (PCPMetric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) {
60       Hashtable_put(this->users, uid, value.cp);
61       name = value.cp;
62    }
63    return name;
64 }
65 
ProcessList_new(UsersTable * usersTable,Hashtable * dynamicMeters,Hashtable * dynamicColumns,Hashtable * pidMatchList,uid_t userId)66 ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
67    PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList));
68    ProcessList* super = &(this->super);
69 
70    ProcessList_init(super, Class(PCPProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId);
71 
72    struct timeval timestamp;
73    gettimeofday(&timestamp, NULL);
74    this->timestamp = pmtimevalToReal(&timestamp);
75 
76    this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
77    PCPProcessList_updateCPUcount(this);
78 
79    return super;
80 }
81 
ProcessList_delete(ProcessList * pl)82 void ProcessList_delete(ProcessList* pl) {
83    PCPProcessList* this = (PCPProcessList*) pl;
84    ProcessList_done(pl);
85    free(this->values);
86    for (unsigned int i = 0; i < pl->existingCPUs; i++)
87       free(this->percpu[i]);
88    free(this->percpu);
89    free(this->cpu);
90    free(this);
91 }
92 
Metric_instance_s32(int metric,int pid,int offset,long fallback)93 static inline long Metric_instance_s32(int metric, int pid, int offset, long fallback) {
94    pmAtomValue value;
95    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_32))
96       return value.l;
97    return fallback;
98 }
99 
Metric_instance_s64(int metric,int pid,int offset,long long fallback)100 static inline long long Metric_instance_s64(int metric, int pid, int offset, long long fallback) {
101    pmAtomValue value;
102    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_64))
103       return value.l;
104    return fallback;
105 }
106 
Metric_instance_u32(int metric,int pid,int offset,unsigned long fallback)107 static inline unsigned long Metric_instance_u32(int metric, int pid, int offset, unsigned long fallback) {
108    pmAtomValue value;
109    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U32))
110       return value.ul;
111    return fallback;
112 }
113 
Metric_instance_u64(int metric,int pid,int offset,unsigned long long fallback)114 static inline unsigned long long Metric_instance_u64(int metric, int pid, int offset, unsigned long long fallback) {
115    pmAtomValue value;
116    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U64))
117       return value.ull;
118    return fallback;
119 }
120 
Metric_instance_time(int metric,int pid,int offset)121 static inline unsigned long long Metric_instance_time(int metric, int pid, int offset) {
122    pmAtomValue value;
123    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U64))
124       return value.ull / 10;
125    return 0;
126 }
127 
Metric_instance_ONE_K(int metric,int pid,int offset)128 static inline unsigned long long Metric_instance_ONE_K(int metric, int pid, int offset) {
129    pmAtomValue value;
130    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_U64))
131       return value.ull / ONE_K;
132    return ULLONG_MAX;
133 }
134 
Metric_instance_char(int metric,int pid,int offset,char fallback)135 static inline char Metric_instance_char(int metric, int pid, int offset, char fallback) {
136    pmAtomValue value;
137    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_STRING)) {
138       char uchar = value.cp[0];
139       free(value.cp);
140       return uchar;
141    }
142    return fallback;
143 }
144 
PCPProcessList_getProcessState(char state)145 static inline ProcessState PCPProcessList_getProcessState(char state) {
146    switch (state) {
147       case '?': return UNKNOWN;
148       case 'R': return RUNNING;
149       case 'W': return WAITING;
150       case 'D': return UNINTERRUPTIBLE_WAIT;
151       case 'P': return PAGING;
152       case 'T': return STOPPED;
153       case 't': return TRACED;
154       case 'Z': return ZOMBIE;
155       case 'X': return DEFUNCT;
156       case 'I': return IDLE;
157       case 'S': return SLEEPING;
158       default: return UNKNOWN;
159    }
160 }
161 
PCPProcessList_updateID(Process * process,int pid,int offset)162 static void PCPProcessList_updateID(Process* process, int pid, int offset) {
163    process->tgid = Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1);
164    process->ppid = Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1);
165    process->state = PCPProcessList_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?'));
166 }
167 
PCPProcessList_updateInfo(Process * process,int pid,int offset,char * command,size_t commLen)168 static void PCPProcessList_updateInfo(Process* process, int pid, int offset, char* command, size_t commLen) {
169    PCPProcess* pp = (PCPProcess*) process;
170    pmAtomValue value;
171 
172    if (!PCPMetric_instance(PCP_PROC_CMD, pid, offset, &value, PM_TYPE_STRING))
173       value.cp = xStrdup("<unknown>");
174    String_safeStrncpy(command, value.cp, commLen);
175    free(value.cp);
176 
177    process->pgrp = Metric_instance_u32(PCP_PROC_PGRP, pid, offset, 0);
178    process->session = Metric_instance_u32(PCP_PROC_SESSION, pid, offset, 0);
179    process->tty_nr = Metric_instance_u32(PCP_PROC_TTY, pid, offset, 0);
180    process->tpgid = Metric_instance_u32(PCP_PROC_TTYPGRP, pid, offset, 0);
181    process->minflt = Metric_instance_u32(PCP_PROC_MINFLT, pid, offset, 0);
182    pp->cminflt = Metric_instance_u32(PCP_PROC_CMINFLT, pid, offset, 0);
183    process->majflt = Metric_instance_u32(PCP_PROC_MAJFLT, pid, offset, 0);
184    pp->cmajflt = Metric_instance_u32(PCP_PROC_CMAJFLT, pid, offset, 0);
185    pp->utime = Metric_instance_time(PCP_PROC_UTIME, pid, offset);
186    pp->stime = Metric_instance_time(PCP_PROC_STIME, pid, offset);
187    pp->cutime = Metric_instance_time(PCP_PROC_CUTIME, pid, offset);
188    pp->cstime = Metric_instance_time(PCP_PROC_CSTIME, pid, offset);
189    process->priority = Metric_instance_u32(PCP_PROC_PRIORITY, pid, offset, 0);
190    process->nice = Metric_instance_s32(PCP_PROC_NICE, pid, offset, 0);
191    process->nlwp = Metric_instance_u32(PCP_PROC_THREADS, pid, offset, 0);
192    process->starttime_ctime = Metric_instance_time(PCP_PROC_STARTTIME, pid, offset);
193    process->processor = Metric_instance_u32(PCP_PROC_PROCESSOR, pid, offset, 0);
194 
195    process->time = pp->utime + pp->stime;
196 }
197 
PCPProcessList_updateIO(PCPProcess * pp,int pid,int offset,unsigned long long now)198 static void PCPProcessList_updateIO(PCPProcess* pp, int pid, int offset, unsigned long long now) {
199    pmAtomValue value;
200 
201    pp->io_rchar = Metric_instance_ONE_K(PCP_PROC_IO_RCHAR, pid, offset);
202    pp->io_wchar = Metric_instance_ONE_K(PCP_PROC_IO_WCHAR, pid, offset);
203    pp->io_syscr = Metric_instance_u64(PCP_PROC_IO_SYSCR, pid, offset, ULLONG_MAX);
204    pp->io_syscw = Metric_instance_u64(PCP_PROC_IO_SYSCW, pid, offset, ULLONG_MAX);
205    pp->io_cancelled_write_bytes = Metric_instance_ONE_K(PCP_PROC_IO_CANCELLED, pid, offset);
206 
207    if (PCPMetric_instance(PCP_PROC_IO_READB, pid, offset, &value, PM_TYPE_U64)) {
208       unsigned long long last_read = pp->io_read_bytes;
209       pp->io_read_bytes = value.ull / ONE_K;
210       pp->io_rate_read_bps = ONE_K * (pp->io_read_bytes - last_read) /
211                                      (now - pp->io_last_scan_time);
212    } else {
213       pp->io_read_bytes = ULLONG_MAX;
214       pp->io_rate_read_bps = NAN;
215    }
216 
217    if (PCPMetric_instance(PCP_PROC_IO_WRITEB, pid, offset, &value, PM_TYPE_U64)) {
218       unsigned long long last_write = pp->io_write_bytes;
219       pp->io_write_bytes = value.ull;
220       pp->io_rate_write_bps = ONE_K * (pp->io_write_bytes - last_write) /
221                                       (now - pp->io_last_scan_time);
222    } else {
223       pp->io_write_bytes = ULLONG_MAX;
224       pp->io_rate_write_bps = NAN;
225    }
226 
227    pp->io_last_scan_time = now;
228 }
229 
PCPProcessList_updateMemory(PCPProcess * pp,int pid,int offset)230 static void PCPProcessList_updateMemory(PCPProcess* pp, int pid, int offset) {
231    pp->super.m_virt = Metric_instance_u32(PCP_PROC_MEM_SIZE, pid, offset, 0);
232    pp->super.m_resident = Metric_instance_u32(PCP_PROC_MEM_RSS, pid, offset, 0);
233    pp->m_share = Metric_instance_u32(PCP_PROC_MEM_SHARE, pid, offset, 0);
234    pp->m_trs = Metric_instance_u32(PCP_PROC_MEM_TEXTRS, pid, offset, 0);
235    pp->m_lrs = Metric_instance_u32(PCP_PROC_MEM_LIBRS, pid, offset, 0);
236    pp->m_drs = Metric_instance_u32(PCP_PROC_MEM_DATRS, pid, offset, 0);
237    pp->m_dt = Metric_instance_u32(PCP_PROC_MEM_DIRTY, pid, offset, 0);
238 }
239 
PCPProcessList_updateSmaps(PCPProcess * pp,pid_t pid,int offset)240 static void PCPProcessList_updateSmaps(PCPProcess* pp, pid_t pid, int offset) {
241    pp->m_pss = Metric_instance_u64(PCP_PROC_SMAPS_PSS, pid, offset, 0);
242    pp->m_swap = Metric_instance_u64(PCP_PROC_SMAPS_SWAP, pid, offset, 0);
243    pp->m_psswp = Metric_instance_u64(PCP_PROC_SMAPS_SWAPPSS, pid, offset, 0);
244 }
245 
PCPProcessList_readOomData(PCPProcess * pp,int pid,int offset)246 static void PCPProcessList_readOomData(PCPProcess* pp, int pid, int offset) {
247    pp->oom = Metric_instance_u32(PCP_PROC_OOMSCORE, pid, offset, 0);
248 }
249 
PCPProcessList_readAutogroup(PCPProcess * pp,int pid,int offset)250 static void PCPProcessList_readAutogroup(PCPProcess* pp, int pid, int offset) {
251    pp->autogroup_id = Metric_instance_s64(PCP_PROC_AUTOGROUP_ID, pid, offset, -1);
252    pp->autogroup_nice = Metric_instance_s32(PCP_PROC_AUTOGROUP_NICE, pid, offset, 0);
253 }
254 
PCPProcessList_readCtxtData(PCPProcess * pp,int pid,int offset)255 static void PCPProcessList_readCtxtData(PCPProcess* pp, int pid, int offset) {
256    pmAtomValue value;
257    unsigned long ctxt = 0;
258 
259    if (PCPMetric_instance(PCP_PROC_VCTXSW, pid, offset, &value, PM_TYPE_U32))
260       ctxt += value.ul;
261    if (PCPMetric_instance(PCP_PROC_NVCTXSW, pid, offset, &value, PM_TYPE_U32))
262       ctxt += value.ul;
263 
264    pp->ctxt_diff = ctxt > pp->ctxt_total ? ctxt - pp->ctxt_total : 0;
265    pp->ctxt_total = ctxt;
266 }
267 
setString(PCPMetric metric,int pid,int offset,char * string)268 static char* setString(PCPMetric metric, int pid, int offset, char* string) {
269    if (string)
270       free(string);
271    pmAtomValue value;
272    if (PCPMetric_instance(metric, pid, offset, &value, PM_TYPE_STRING))
273       string = value.cp;
274    else
275       string = NULL;
276    return string;
277 }
278 
PCPProcessList_updateTTY(Process * process,int pid,int offset)279 static void PCPProcessList_updateTTY(Process* process, int pid, int offset) {
280    process->tty_name = setString(PCP_PROC_TTYNAME, pid, offset, process->tty_name);
281 }
282 
PCPProcessList_readCGroups(PCPProcess * pp,int pid,int offset)283 static void PCPProcessList_readCGroups(PCPProcess* pp, int pid, int offset) {
284    pp->cgroup = setString(PCP_PROC_CGROUPS, pid, offset, pp->cgroup);
285 }
286 
PCPProcessList_readSecattrData(PCPProcess * pp,int pid,int offset)287 static void PCPProcessList_readSecattrData(PCPProcess* pp, int pid, int offset) {
288    pp->secattr = setString(PCP_PROC_LABELS, pid, offset, pp->secattr);
289 }
290 
PCPProcessList_readCwd(PCPProcess * pp,int pid,int offset)291 static void PCPProcessList_readCwd(PCPProcess* pp, int pid, int offset) {
292    pp->super.procCwd = setString(PCP_PROC_CWD, pid, offset, pp->super.procCwd);
293 }
294 
PCPProcessList_updateUsername(Process * process,int pid,int offset,UsersTable * users)295 static void PCPProcessList_updateUsername(Process* process, int pid, int offset, UsersTable* users) {
296    process->st_uid = Metric_instance_u32(PCP_PROC_ID_UID, pid, offset, 0);
297    process->user = setUser(users, process->st_uid, pid, offset);
298 }
299 
PCPProcessList_updateCmdline(Process * process,int pid,int offset,const char * comm)300 static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, const char* comm) {
301    pmAtomValue value;
302    if (!PCPMetric_instance(PCP_PROC_PSARGS, pid, offset, &value, PM_TYPE_STRING)) {
303       if (process->state != ZOMBIE)
304          process->isKernelThread = true;
305       Process_updateCmdline(process, NULL, 0, 0);
306       return;
307    }
308 
309    char* command = value.cp;
310    int length = strlen(command);
311    if (command[0] != '(') {
312       process->isKernelThread = false;
313    } else {
314       ++command;
315       --length;
316       if (command[length - 1] == ')')
317          command[--length] = '\0';
318       process->isKernelThread = true;
319    }
320 
321    int tokenStart = 0;
322    for (int i = 0; i < length; i++) {
323       /* htop considers the next character after the last / that is before
324        * basenameOffset, as the start of the basename in cmdline - see
325        * Process_writeCommand */
326       if (command[i] == '/')
327          tokenStart = i + 1;
328    }
329    int tokenEnd = length;
330 
331    Process_updateCmdline(process, command, tokenStart, tokenEnd);
332    free(value.cp);
333 
334    Process_updateComm(process, comm);
335 
336    if (PCPMetric_instance(PCP_PROC_EXE, pid, offset, &value, PM_TYPE_STRING)) {
337       Process_updateExe(process, value.cp[0] ? value.cp : NULL);
338       free(value.cp);
339    }
340 }
341 
PCPProcessList_updateProcesses(PCPProcessList * this,double period,struct timeval * tv)342 static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, struct timeval* tv) {
343    ProcessList* pl = (ProcessList*) this;
344    const Settings* settings = pl->settings;
345 
346    bool hideKernelThreads = settings->hideKernelThreads;
347    bool hideUserlandThreads = settings->hideUserlandThreads;
348 
349    unsigned long long now = tv->tv_sec * 1000LL + tv->tv_usec / 1000LL;
350    int pid = -1, offset = -1;
351 
352    /* for every process ... */
353    while (PCPMetric_iterate(PCP_PROC_PID, &pid, &offset)) {
354 
355       bool preExisting;
356       Process* proc = ProcessList_getProcess(pl, pid, &preExisting, PCPProcess_new);
357       PCPProcess* pp = (PCPProcess*) proc;
358       PCPProcessList_updateID(proc, pid, offset);
359       proc->isUserlandThread = proc->pid != proc->tgid;
360       pp->offset = offset >= 0 ? offset : 0;
361 
362       /*
363        * These conditions will not trigger on first occurrence, cause we need to
364        * add the process to the ProcessList and do all one time scans
365        * (e.g. parsing the cmdline to detect a kernel thread)
366        * But it will short-circuit subsequent scans.
367        */
368       if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) {
369          proc->updated = true;
370          proc->show = false;
371          if (proc->state == RUNNING)
372             pl->runningTasks++;
373          pl->kernelThreads++;
374          pl->totalTasks++;
375          continue;
376       }
377       if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
378          proc->updated = true;
379          proc->show = false;
380          if (proc->state == RUNNING)
381             pl->runningTasks++;
382          pl->userlandThreads++;
383          pl->totalTasks++;
384          continue;
385       }
386 
387       if (settings->flags & PROCESS_FLAG_IO)
388          PCPProcessList_updateIO(pp, pid, offset, now);
389 
390       PCPProcessList_updateMemory(pp, pid, offset);
391 
392       if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) &&
393           (Process_isKernelThread(proc) == false)) {
394          if (PCPMetric_enabled(PCP_PROC_SMAPS_PSS))
395             PCPProcessList_updateSmaps(pp, pid, offset);
396       }
397 
398       char command[MAX_NAME + 1];
399       unsigned int tty_nr = proc->tty_nr;
400       unsigned long long int lasttimes = pp->utime + pp->stime;
401 
402       PCPProcessList_updateInfo(proc, pid, offset, command, sizeof(command));
403       proc->starttime_ctime += Platform_getBootTime();
404       if (tty_nr != proc->tty_nr)
405          PCPProcessList_updateTTY(proc, pid, offset);
406 
407       float percent_cpu = (pp->utime + pp->stime - lasttimes) / period * 100.0;
408       proc->percent_cpu = isnan(percent_cpu) ?
409                           0.0 : CLAMP(percent_cpu, 0.0, pl->activeCPUs * 100.0);
410       proc->percent_mem = proc->m_resident / (double)pl->totalMem * 100.0;
411 
412       PCPProcessList_updateUsername(proc, pid, offset, pl->usersTable);
413 
414       if (!preExisting) {
415          PCPProcessList_updateCmdline(proc, pid, offset, command);
416          Process_fillStarttimeBuffer(proc);
417          ProcessList_add(pl, proc);
418       } else if (settings->updateProcessNames && proc->state != ZOMBIE) {
419          PCPProcessList_updateCmdline(proc, pid, offset, command);
420       }
421 
422       if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
423          PCPProcessList_readCGroups(pp, pid, offset);
424 
425       if (settings->flags & PROCESS_FLAG_LINUX_OOM)
426          PCPProcessList_readOomData(pp, pid, offset);
427 
428       if (settings->flags & PROCESS_FLAG_LINUX_CTXT)
429          PCPProcessList_readCtxtData(pp, pid, offset);
430 
431       if (settings->flags & PROCESS_FLAG_LINUX_SECATTR)
432          PCPProcessList_readSecattrData(pp, pid, offset);
433 
434       if (settings->flags & PROCESS_FLAG_CWD)
435          PCPProcessList_readCwd(pp, pid, offset);
436 
437       if (settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP)
438          PCPProcessList_readAutogroup(pp, pid, offset);
439 
440       if (proc->state == ZOMBIE && !proc->cmdline && command[0]) {
441          Process_updateCmdline(proc, command, 0, strlen(command));
442       } else if (Process_isThread(proc)) {
443          if ((settings->showThreadNames || Process_isKernelThread(proc)) && command[0]) {
444             Process_updateCmdline(proc, command, 0, strlen(command));
445          }
446 
447          if (Process_isKernelThread(proc)) {
448             pl->kernelThreads++;
449          } else {
450             pl->userlandThreads++;
451          }
452       }
453 
454       /* Set at the end when we know if a new entry is a thread */
455       proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) ||
456                       (hideUserlandThreads && Process_isUserlandThread(proc)));
457 
458       pl->totalTasks++;
459       if (proc->state == RUNNING)
460          pl->runningTasks++;
461       proc->updated = true;
462    }
463    return true;
464 }
465 
PCPProcessList_updateMemoryInfo(ProcessList * super)466 static void PCPProcessList_updateMemoryInfo(ProcessList* super) {
467    unsigned long long int freeMem = 0;
468    unsigned long long int swapFreeMem = 0;
469    unsigned long long int sreclaimableMem = 0;
470    super->totalMem = super->usedMem = super->cachedMem = 0;
471    super->usedSwap = super->totalSwap = super->sharedMem = 0;
472 
473    pmAtomValue value;
474    if (PCPMetric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL)
475       super->totalMem = value.ull;
476    if (PCPMetric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL)
477       freeMem = value.ull;
478    if (PCPMetric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL)
479       super->buffersMem = value.ull;
480    if (PCPMetric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL)
481       sreclaimableMem = value.ull;
482    if (PCPMetric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL)
483       super->sharedMem = value.ull;
484    if (PCPMetric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL)
485       super->cachedMem = value.ull + sreclaimableMem - super->sharedMem;
486    const memory_t usedDiff = freeMem + super->cachedMem + sreclaimableMem + super->buffersMem;
487    super->usedMem = (super->totalMem >= usedDiff) ?
488            super->totalMem - usedDiff : super->totalMem - freeMem;
489    if (PCPMetric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL)
490       super->availableMem = MINIMUM(value.ull, super->totalMem);
491    else
492       super->availableMem = freeMem;
493    if (PCPMetric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL)
494       swapFreeMem = value.ull;
495    if (PCPMetric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL)
496       super->totalSwap = value.ull;
497    if (PCPMetric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL)
498       super->cachedSwap = value.ull;
499    super->usedSwap = super->totalSwap - swapFreeMem - super->cachedSwap;
500 }
501 
502 /* make copies of previously sampled values to avoid overwrite */
PCPProcessList_backupCPUTime(pmAtomValue * values)503 static inline void PCPProcessList_backupCPUTime(pmAtomValue* values) {
504    /* the PERIOD fields (must) mirror the TIME fields */
505    for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) {
506       values[metric + CPU_TOTAL_PERIOD] = values[metric];
507    }
508 }
509 
PCPProcessList_saveCPUTimePeriod(pmAtomValue * values,CPUMetric previous,pmAtomValue * latest)510 static inline void PCPProcessList_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) {
511    pmAtomValue* value;
512 
513    /* new value for period */
514    value = &values[previous];
515    if (latest->ull > value->ull)
516       value->ull = latest->ull - value->ull;
517    else
518       value->ull = 0;
519 
520    /* new value for time */
521    value = &values[previous - CPU_TOTAL_PERIOD];
522    value->ull = latest->ull;
523 }
524 
525 /* using copied sampled values and new values, calculate derivations */
PCPProcessList_deriveCPUTime(pmAtomValue * values)526 static void PCPProcessList_deriveCPUTime(pmAtomValue* values) {
527 
528    pmAtomValue* usertime = &values[CPU_USER_TIME];
529    pmAtomValue* guesttime = &values[CPU_GUEST_TIME];
530    usertime->ull -= guesttime->ull;
531 
532    pmAtomValue* nicetime = &values[CPU_NICE_TIME];
533    pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME];
534    nicetime->ull -= guestnicetime->ull;
535 
536    pmAtomValue* idletime = &values[CPU_IDLE_TIME];
537    pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME];
538    pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME];
539    idlealltime->ull = idletime->ull + iowaittime->ull;
540 
541    pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME];
542    pmAtomValue* irqtime = &values[CPU_IRQ_TIME];
543    pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME];
544    pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME];
545    systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull;
546 
547    pmAtomValue* virtalltime = &values[CPU_GUEST_TIME];
548    virtalltime->ull = guesttime->ull + guestnicetime->ull;
549 
550    pmAtomValue* stealtime = &values[CPU_STEAL_TIME];
551    pmAtomValue* totaltime = &values[CPU_TOTAL_TIME];
552    totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull +
553                     idlealltime->ull + stealtime->ull + virtalltime->ull;
554 
555    PCPProcessList_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime);
556    PCPProcessList_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime);
557    PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime);
558    PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime);
559    PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime);
560    PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime);
561    PCPProcessList_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime);
562    PCPProcessList_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime);
563    PCPProcessList_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime);
564    PCPProcessList_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime);
565    PCPProcessList_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime);
566    PCPProcessList_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime);
567 }
568 
PCPProcessList_updateAllCPUTime(PCPProcessList * this,PCPMetric metric,CPUMetric cpumetric)569 static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric)
570 {
571    pmAtomValue* value = &this->cpu[cpumetric];
572    if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL)
573       memset(&value, 0, sizeof(pmAtomValue));
574 }
575 
PCPProcessList_updatePerCPUTime(PCPProcessList * this,PCPMetric metric,CPUMetric cpumetric)576 static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric)
577 {
578    int cpus = this->super.existingCPUs;
579    if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL)
580       memset(this->values, 0, cpus * sizeof(pmAtomValue));
581    for (int i = 0; i < cpus; i++)
582       this->percpu[i][cpumetric].ull = this->values[i].ull;
583 }
584 
PCPProcessList_updatePerCPUReal(PCPProcessList * this,PCPMetric metric,CPUMetric cpumetric)585 static void PCPProcessList_updatePerCPUReal(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric)
586 {
587    int cpus = this->super.existingCPUs;
588    if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL)
589       memset(this->values, 0, cpus * sizeof(pmAtomValue));
590    for (int i = 0; i < cpus; i++)
591       this->percpu[i][cpumetric].d = this->values[i].d;
592 }
593 
PCPProcessList_scanZfsArcstats(PCPProcessList * this)594 static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) {
595    unsigned long long int dbufSize = 0;
596    unsigned long long int dnodeSize = 0;
597    unsigned long long int bonusSize = 0;
598    pmAtomValue value;
599 
600    memset(&this->zfs, 0, sizeof(ZfsArcStats));
601    if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
602       this->zfs.anon = value.ull / ONE_K;
603    if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64))
604       this->zfs.max = value.ull / ONE_K;
605    if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))
606       bonusSize = value.ull / ONE_K;
607    if (PCPMetric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64))
608       dbufSize = value.ull / ONE_K;
609    if (PCPMetric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64))
610       dnodeSize = value.ull / ONE_K;
611    if (PCPMetric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
612       this->zfs.compressed = value.ull / ONE_K;
613    if (PCPMetric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
614       this->zfs.uncompressed = value.ull / ONE_K;
615    if (PCPMetric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64))
616       this->zfs.header = value.ull / ONE_K;
617    if (PCPMetric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64))
618       this->zfs.MFU = value.ull / ONE_K;
619    if (PCPMetric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64))
620       this->zfs.MRU = value.ull / ONE_K;
621    if (PCPMetric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64))
622       this->zfs.size = value.ull / ONE_K;
623 
624    this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K;
625    this->zfs.enabled = (this->zfs.size > 0);
626    this->zfs.isCompressed = (this->zfs.compressed > 0);
627 }
628 
PCPProcessList_updateHeader(ProcessList * super,const Settings * settings)629 static void PCPProcessList_updateHeader(ProcessList* super, const Settings* settings) {
630    PCPProcessList_updateMemoryInfo(super);
631 
632    PCPProcessList* this = (PCPProcessList*) super;
633    PCPProcessList_updateCPUcount(this);
634 
635    PCPProcessList_backupCPUTime(this->cpu);
636    PCPProcessList_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME);
637    PCPProcessList_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME);
638    PCPProcessList_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME);
639    PCPProcessList_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME);
640    PCPProcessList_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME);
641    PCPProcessList_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME);
642    PCPProcessList_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
643    PCPProcessList_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME);
644    PCPProcessList_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME);
645    PCPProcessList_deriveCPUTime(this->cpu);
646 
647    for (unsigned int i = 0; i < super->existingCPUs; i++)
648       PCPProcessList_backupCPUTime(this->percpu[i]);
649    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME);
650    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME);
651    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME);
652    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME);
653    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME);
654    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME);
655    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
656    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME);
657    PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME);
658    for (unsigned int i = 0; i < super->existingCPUs; i++)
659       PCPProcessList_deriveCPUTime(this->percpu[i]);
660 
661    if (settings->showCPUFrequency)
662       PCPProcessList_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY);
663 
664    PCPProcessList_scanZfsArcstats(this);
665 }
666 
ProcessList_goThroughEntries(ProcessList * super,bool pauseProcessUpdate)667 void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
668    PCPProcessList* this = (PCPProcessList*) super;
669    const Settings* settings = super->settings;
670    bool enabled = !pauseProcessUpdate;
671 
672    bool flagged = settings->showCPUFrequency;
673    PCPMetric_enable(PCP_HINV_CPUCLOCK, flagged);
674 
675    /* In pause mode do not sample per-process metric values at all */
676    for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++)
677       PCPMetric_enable(metric, enabled);
678 
679    flagged = settings->flags & PROCESS_FLAG_LINUX_CGROUP;
680    PCPMetric_enable(PCP_PROC_CGROUPS, flagged && enabled);
681    flagged = settings->flags & PROCESS_FLAG_LINUX_OOM;
682    PCPMetric_enable(PCP_PROC_OOMSCORE, flagged && enabled);
683    flagged = settings->flags & PROCESS_FLAG_LINUX_CTXT;
684    PCPMetric_enable(PCP_PROC_VCTXSW, flagged && enabled);
685    PCPMetric_enable(PCP_PROC_NVCTXSW, flagged && enabled);
686    flagged = settings->flags & PROCESS_FLAG_LINUX_SECATTR;
687    PCPMetric_enable(PCP_PROC_LABELS, flagged && enabled);
688    flagged = settings->flags & PROCESS_FLAG_LINUX_AUTOGROUP;
689    PCPMetric_enable(PCP_PROC_AUTOGROUP_ID, flagged && enabled);
690    PCPMetric_enable(PCP_PROC_AUTOGROUP_NICE, flagged && enabled);
691 
692    /* Sample smaps metrics on every second pass to improve performance */
693    static int smaps_flag;
694    smaps_flag = !!smaps_flag;
695    PCPMetric_enable(PCP_PROC_SMAPS_PSS, smaps_flag && enabled);
696    PCPMetric_enable(PCP_PROC_SMAPS_SWAP, smaps_flag && enabled);
697    PCPMetric_enable(PCP_PROC_SMAPS_SWAPPSS, smaps_flag && enabled);
698 
699    struct timeval timestamp;
700    if (PCPMetric_fetch(&timestamp) != true)
701       return;
702 
703    double sample = this->timestamp;
704    this->timestamp = pmtimevalToReal(&timestamp);
705 
706    PCPProcessList_updateHeader(super, settings);
707 
708    /* In pause mode only update global data for meters (CPU, memory, etc) */
709    if (pauseProcessUpdate)
710       return;
711 
712    double period = (this->timestamp - sample) * 100;
713    PCPProcessList_updateProcesses(this, period, &timestamp);
714 }
715 
ProcessList_isCPUonline(const ProcessList * super,unsigned int id)716 bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) {
717    assert(id < super->existingCPUs);
718    (void) super;
719 
720    pmAtomValue value;
721    if (PCPMetric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32))
722       return true;
723    return false;
724 }
725