From 7e6a371d8a36b250a2edddff9f5d059640b8132e Mon Sep 17 00:00:00 2001 From: Jim Warner Date: Wed, 19 Aug 2015 00:00:00 -0500 Subject: [PATCH] library: implement task/thread support via the new api This commit is the culmination of efforts to modernize the library api. It should be treated as a first blush attempt, especially since I have absolutely no library design experience. But I did have a very strong desire to lessen the new library's impact on the top program. Under this new api, a 'stack' is the equivalent of the old proc_t. It can be seen as a variable length record whose contents & order is under complete user control. That initial stack/record configuration is established at procps_pids_new() time and will probably serve most program needs. But, a dynamic & demanding program like top will later change a stack via procps_pids_reset(). For programs like top & ps, procps_pids_reap() will be the function that will retrieve all tasks and threads. Any program that needs to filter / select only certain processes or users have available other functions that can be used: procps_pids_stacks_alloc, fill & dealloc. This implementation attempts to maximize that existing proven libprocps code base. As we gain more experience such actual code can be migrated into the pids.c file. Signed-off-by: Jim Warner --- Makefile.am | 3 + proc/libprocps.sym | 9 + proc/pids.c | 1283 ++++++++++++++++++++++++++++++++++++++++++++ proc/pids.h | 232 ++++++++ 4 files changed, 1527 insertions(+) create mode 100644 proc/pids.c create mode 100644 proc/pids.h diff --git a/Makefile.am b/Makefile.am index 342a8e3c..fb98c077 100644 --- a/Makefile.am +++ b/Makefile.am @@ -163,6 +163,8 @@ proc_libprocps_la_SOURCES = \ proc/procps-private.h \ proc/meminfo.c \ proc/meminfo.h \ + proc/pids.c \ + proc/pids.h \ proc/procps.h \ proc/pwcache.c \ proc/pwcache.h \ @@ -189,6 +191,7 @@ proc_libprocps_la_include_HEADERS = \ proc/devname.h \ proc/diskstat.h \ proc/escape.h \ + proc/pids.h \ proc/procps.h \ proc/pwcache.h \ proc/readproc.h \ diff --git a/proc/libprocps.sym b/proc/libprocps.sym index b4d19065..b2e1f0c7 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -42,6 +42,15 @@ global: procps_meminfo_getstack; procps_meminfo_stack_fill; procps_meminfo_stack_alloc; + procps_pids_new; + procps_pids_reap; + procps_pids_ref; + procps_pids_reset; + procps_pids_stacks_alloc; + procps_pids_stacks_dealloc; + procps_pids_stacks_fill; + procps_pids_stacks_sort; + procps_pids_unref; procps_slabinfo_new; procps_slabinfo_read; procps_slabinfo_ref; diff --git a/proc/pids.c b/proc/pids.c new file mode 100644 index 00000000..e50f8f67 --- /dev/null +++ b/proc/pids.c @@ -0,0 +1,1283 @@ +/* + * pids.c - task/thread/process related declarations for libproc + * + * Copyright (C) 1998-2005 Albert Cahalan + * Copyright (C) 2015 Craig Small + * Copyright (C) 2015 Jim Warner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +//efine _GNU_SOURCE // for qsort_r +#define OOMEM_ENABLE // we will not disturb the api +#define WITH_SYSTEMD // with optional functionality + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "procps-private.h" + +#include "readproc.h" // and two headers for bridged +#include "wchan.h" // support (temporary include) + +//#define UNREF_RPTHASH // report on hashing, at uref time + +#define FILL_ID_MAX 255 // upper limit for pid/uid fills + +enum pids_item PROCPS_PIDS_logical_end = PROCPS_PIDS_noop + 1; +enum pids_item PROCPS_PIDS_physical_end = PROCPS_PIDS_noop + 2; + + +struct stacks_extent { // callers see a pids_stacks struct + struct pids_stack **stacks; + int ext_numitems; // includes 'physical_end' delimiter + int ext_numstacks; + struct stacks_extent *next; +}; + +struct procps_pidsinfo { + int refcount; + int maxitems; // includes 'physical_end' delimiter + int curitems; // includes 'logical_end' delimiter + enum pids_item *items; // includes 'phy/log_end' delimiters + struct pids_stack **anchor; // reapable stacks (consolidated extents) + int alloc_total; // number of above pointers allocated + int inuse_total; // number of above pointers occupied + struct stacks_extent *extents; // anchor for all allocated extents + int history_yes; // need historical data + struct history_info *hist; // pointer to historical support data + int dirty_stacks; // extents need dynamic storage clean + unsigned pgs2k_shift; // to convert some proc vaules + unsigned flags; // the old library PROC_FILL flagss + PROCTAB *PT; // the old library essential interface + struct pids_counts counts; // counts for 'procps_pids_stacks_fill' + struct pids_reap reap; // counts + stacks for 'procps_pids_reap' +}; + + +// ___ Results 'Set' Support |||||||||||||||||||||||||||||||||||||||||||||||||| + + /* note: the vast majority of these 'set' functions have no need for + the procps_pidsinfo structure, but it's being passed to all + bacause of the CVT_set requirement & for future flexibility */ + +#define setNAME(e) set_results_ ## e +#define setDECL(e) static void setNAME(e) \ + (struct procps_pidsinfo *I, struct pids_result *R, proc_t *P) + + // value the addr member +#define ADR_set(e,t,x) setDECL(e) { \ + (void)I; R->result. t = (void *)P-> x; } + // convert pages to kib +#define CVT_set(e,t,x) setDECL(e) { \ + R->result. t = (unsigned long)(P-> x) << I -> pgs2k_shift; } + // strdup of a static char array +#define DUP_set(e,x) setDECL(e) { \ + (void)I; R->result.str = strdup(P-> x); } + // regular assignment copy +#define REG_set(e,t,x) setDECL(e) { \ + (void)I; R->result. t = P-> x; } + // take ownership of a regular char* string +#define STR_set(e,x) setDECL(e) { \ + (void)I; R->result.str = P-> x; P-> x = NULL; } + // take ownership of a vectorized single string +#define VEC_set(e,x) setDECL(e) { \ + (void)I; R->result.str = (const char *)*P-> x; P-> x = NULL; } + +ADR_set(ADDR_END_CODE, addr, end_code) +ADR_set(ADDR_KSTK_EIP, addr, kstk_eip) +ADR_set(ADDR_KSTK_ESP, addr, kstk_esp) +ADR_set(ADDR_START_CODE, addr, start_code) +ADR_set(ADDR_START_STACK, addr, start_stack) +REG_set(ALARM, sl_int, alarm) +VEC_set(CGROUP, cgroup) +STR_set(CMD, cmd) +VEC_set(CMDLINE, cmdline) +VEC_set(ENVIRON, environ) +REG_set(EXIT_SIGNAL, s_int, exit_signal) +REG_set(FLAGS, ul_int, flags) +REG_set(FLT_MAJ, ul_int, maj_flt) +REG_set(FLT_MAJ_C, ul_int, cmaj_flt) +REG_set(FLT_MAJ_DELTA, ul_int, maj_delta) +REG_set(FLT_MIN, ul_int, min_flt) +REG_set(FLT_MIN_C, ul_int, cmin_flt) +REG_set(FLT_MIN_DELTA, ul_int, min_delta) +REG_set(ID_EGID, u_int, egid) +REG_set(ID_EGROUP, str, egroup) +REG_set(ID_EUID, u_int, euid) +REG_set(ID_EUSER, str, euser) +REG_set(ID_FGID, u_int, fgid) +REG_set(ID_FGROUP, str, fgroup) +REG_set(ID_FUID, u_int, fuid) +REG_set(ID_FUSER, str, fuser) +REG_set(ID_PGRP, s_int, pgrp) +REG_set(ID_PID, s_int, tid) +REG_set(ID_PPID, s_int, ppid) +REG_set(ID_RGID, u_int, rgid) +REG_set(ID_RGROUP, str, rgroup) +REG_set(ID_RUID, u_int, ruid) +REG_set(ID_RUSER, str, ruser) +REG_set(ID_SESSION, s_int, session) +REG_set(ID_SGID, u_int, sgid) +REG_set(ID_SGROUP, str, sgroup) +REG_set(ID_SUID, u_int, suid) +REG_set(ID_SUSER, str, suser) +REG_set(ID_TGID, s_int, tgid) +REG_set(ID_TPGID, s_int, tpgid) +REG_set(LXCNAME, str, lxcname) +REG_set(MEM_CODE, sl_int, trs) +CVT_set(MEM_CODE_KIB, ul_int, trs) +REG_set(MEM_DATA, sl_int, drs) +CVT_set(MEM_DATA_KIB, ul_int, drs) +REG_set(MEM_DT, sl_int, dt) +REG_set(MEM_LRS, sl_int, lrs) +REG_set(MEM_RES, sl_int, resident) +CVT_set(MEM_RES_KIB, ul_int, resident) +REG_set(MEM_SHR, sl_int, share) +CVT_set(MEM_SHR_KIB, ul_int, share) +REG_set(MEM_VIRT, sl_int, size) +CVT_set(MEM_VIRT_KIB, ul_int, size) +REG_set(NICE, sl_int, nice) +REG_set(NLWP, s_int, nlwp) +REG_set(NS_IPC, ul_int, ns[0]) +REG_set(NS_MNT, ul_int, ns[1]) +REG_set(NS_NET, ul_int, ns[2]) +REG_set(NS_PID, ul_int, ns[3]) +REG_set(NS_USER, ul_int, ns[4]) +REG_set(NS_UTS, ul_int, ns[5]) +REG_set(OOM_ADJ, s_int, oom_adj) +REG_set(OOM_SCORE, s_int, oom_score) +REG_set(PRIORITY, s_int, priority) +REG_set(PROCESSOR, u_int, processor) +REG_set(RSS, sl_int, rss) +REG_set(RSS_RLIM, ul_int, rss_rlim) +REG_set(RTPRIO, ul_int, rtprio) +REG_set(SCHED_CLASS, ul_int, sched) +STR_set(SD_MACH, sd_mach) +STR_set(SD_OUID, sd_ouid) +STR_set(SD_SEAT, sd_seat) +STR_set(SD_SESS, sd_sess) +STR_set(SD_SLICE, sd_slice) +STR_set(SD_UNIT, sd_unit) +STR_set(SD_UUNIT, sd_uunit) +DUP_set(SIGBLOCKED, blocked) +DUP_set(SIGCATCH, sigcatch) +DUP_set(SIGIGNORE, sigignore) +DUP_set(SIGNALS, signal) +DUP_set(SIGPENDING, _sigpnd) +REG_set(STATE, s_ch, state) +STR_set(SUPGIDS, supgid) +STR_set(SUPGROUPS, supgrp) +setDECL(TICS_ALL) { (void)I; R->result.ull_int = P->utime + P->stime; } +setDECL(TICS_ALL_C) { (void)I; R->result.ull_int = P->utime + P->stime + P->cutime + P->cstime; } +REG_set(TICS_DELTA, u_int, pcpu) +REG_set(TICS_SYSTEM, ull_int, stime) +REG_set(TICS_SYSTEM_C, ull_int, cstime) +REG_set(TICS_USER, ull_int, utime) +REG_set(TICS_USER_C, ull_int, cutime) +REG_set(TIME_START, ull_int, start_time) +REG_set(TTY, s_int, tty) +REG_set(VM_DATA, ul_int, vm_data) +REG_set(VM_EXE, ul_int, vm_exe) +REG_set(VM_LIB, ul_int, vm_lib) +REG_set(VM_LOCK, ul_int, vm_lock) +REG_set(VM_RSS, ul_int, vm_rss) +REG_set(VM_SIZE, ul_int, vm_size) +REG_set(VM_STACK, ul_int, vm_stack) +REG_set(VM_SWAP, ul_int, vm_swap) +setDECL(VM_USED) { (void)I; R->result.ul_int = P->vm_swap + P->vm_rss; } +REG_set(VSIZE_PGS, ul_int, vsize) +ADR_set(WCHAN_ADDR, addr, wchan) +setDECL(WCHAN_NAME) { (void)I; R->result.str = strdup(lookup_wchan(P->tid)); } +setDECL(noop) { (void)I; (void)R; (void)P; return; } +setDECL(logical_end) { (void)I; (void)R; (void)P; return; } +setDECL(physical_end) { (void)I; (void)R; (void)P; return; } + +#undef setDECL +#undef ADR_set +#undef CVT_set +#undef DUP_set +#undef REG_set +#undef STR_set +#undef VEC_set + + +// ___ Sorting Support |||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +struct sort_parms { + int offset; + enum pids_sort_order order; +}; + +#define srtNAME(e) sort_results_ ## e + +#define NUM_srt(T) static int srtNAME(T) ( \ + const struct pids_stack **A, const struct pids_stack **B, struct sort_parms *P) { \ + const struct pids_result *a = (*A)->head + P->offset; \ + const struct pids_result *b = (*B)->head + P->offset; \ + return P->order * (b->result. T - a->result. T); } + +#define REG_srt(T) static int srtNAME(T) ( \ + const struct pids_stack **A, const struct pids_stack **B, struct sort_parms *P) { \ + const struct pids_result *a = (*A)->head + P->offset; \ + const struct pids_result *b = (*B)->head + P->offset; \ + if ( a->result. T > b->result. T ) return P->order > 0 ? -1 : 1; \ + if ( a->result. T < b->result. T ) return P->order > 0 ? 1 : -1; \ + return 0; } + +NUM_srt(s_ch) +NUM_srt(s_int) +NUM_srt(sl_int) + +REG_srt(u_int) +REG_srt(ul_int) +REG_srt(ull_int) +REG_srt(addr) + +static int srtNAME(str) ( + const struct pids_stack **A, const struct pids_stack **B, struct sort_parms *P) { + const struct pids_result *a = (*A)->head + P->offset; + const struct pids_result *b = (*B)->head + P->offset; + return P->order * strcoll(b->result.str, a->result.str); +} + +static int srtNAME(noop) ( + const struct pids_stack **A, const struct pids_stack **B, enum pids_item *O) { + (void)A; (void)B; (void)O; + return 0; +} + +#undef NUM_srt +#undef REG_srt + + +// ___ Controlling Table |||||||||||||||||||||||||||||||||||||||||||||||||||||| + +#define f_arg PROC_FILLARG // we don't use + // from either 'stat' or 'status' (preferred) +#define f_either PROC_SPARE_1 +#define f_env PROC_FILLENV // we don't use +#define f_grp PROC_FILLGRP +#define f_lxc PROC_FILL_LXC +#define f_ns PROC_FILLNS +#define f_oom PROC_FILLOOM +#define f_stat PROC_FILLSTAT +#define f_statm PROC_FILLMEM +#define f_status PROC_FILLSTATUS +#define f_systemd PROC_FILLSYSTEMD +#define f_usr PROC_FILLUSR + // remaining are compound flags, yielding single string +#define x_cgroup PROC_EDITCGRPCVT | PROC_FILLCGROUP // just 1 str +#define x_cmdline PROC_EDITCMDLCVT | PROC_FILLARG // just 1 str +#define x_environ PROC_EDITENVRCVT | PROC_FILLENV // just 1 str +#define x_ogroup PROC_FILLSTATUS | PROC_FILLGRP +#define x_ouser PROC_FILLSTATUS | PROC_FILLUSR +#define x_supgrp PROC_FILLSTATUS | PROC_FILLSUPGRP + +typedef void (*SET_t)(struct procps_pidsinfo *, struct pids_result *, proc_t *); +typedef int (*QSR_t)(const void *, const void *, void *); + +#define RS(e) (SET_t)setNAME(e) +#define QS(t) (QSR_t)srtNAME(t) + + + /* + * Need it be said? + * This table must be kept in the exact same order as + * those 'enum pids_item' guys ! */ +static struct { + SET_t function; // the actual result setting routine + unsigned oldflags; // PROC_FILLxxxx flags for this item + int mustfree; // free is needed for string storage + QSR_t callback; // sort cmp func for a specific type + int makehist; // a result requires history support +} Item_table[] = { +/* function oldflags mustfree callback makehist + --------------------- ---------- -------- ------------ -------- */ + { RS(ADDR_END_CODE), f_stat, 0, QS(addr), 0 }, + { RS(ADDR_KSTK_EIP), f_stat, 0, QS(addr), 0 }, + { RS(ADDR_KSTK_ESP), f_stat, 0, QS(addr), 0 }, + { RS(ADDR_START_CODE), f_stat, 0, QS(addr), 0 }, + { RS(ADDR_START_STACK), f_stat, 0, QS(addr), 0 }, + { RS(ALARM), f_stat, 0, QS(sl_int), 0 }, + { RS(CGROUP), x_cgroup, -1, QS(str), 0 }, + { RS(CMD), f_either, -1, QS(str), 0 }, + { RS(CMDLINE), x_cmdline, -1, QS(str), 0 }, + { RS(ENVIRON), x_environ, -1, QS(str), 0 }, + { RS(EXIT_SIGNAL), f_stat, 0, QS(s_int), 0 }, + { RS(FLAGS), f_stat, 0, QS(ul_int), 0 }, + { RS(FLT_MAJ), f_stat, 0, QS(ul_int), 0 }, + { RS(FLT_MAJ_C), f_stat, 0, QS(ul_int), 0 }, + { RS(FLT_MAJ_DELTA), f_stat, 0, QS(ul_int), -1 }, + { RS(FLT_MIN), f_stat, 0, QS(ul_int), 0 }, + { RS(FLT_MIN_C), f_stat, 0, QS(ul_int), 0 }, + { RS(FLT_MIN_DELTA), f_stat, 0, QS(ul_int), -1 }, + { RS(ID_EGID), 0, 0, QS(u_int), 0 }, + { RS(ID_EGROUP), f_grp, 0, QS(str), 0 }, + { RS(ID_EUID), 0, 0, QS(u_int), 0 }, + { RS(ID_EUSER), f_usr, 0, QS(str), 0 }, + { RS(ID_FGID), f_status, 0, QS(u_int), 0 }, + { RS(ID_FGROUP), x_ogroup, 0, QS(str), 0 }, + { RS(ID_FUID), f_status, 0, QS(u_int), 0 }, + { RS(ID_FUSER), x_ouser, 0, QS(str), 0 }, + { RS(ID_PGRP), f_stat, 0, QS(s_int), 0 }, + { RS(ID_PID), 0, 0, QS(s_int), 0 }, + { RS(ID_PPID), f_either, 0, QS(s_int), 0 }, + { RS(ID_RGID), f_status, 0, QS(u_int), 0 }, + { RS(ID_RGROUP), x_ogroup, 0, QS(str), 0 }, + { RS(ID_RUID), f_status, 0, QS(u_int), 0 }, + { RS(ID_RUSER), x_ouser, 0, QS(str), 0 }, + { RS(ID_SESSION), f_stat, 0, QS(s_int), 0 }, + { RS(ID_SGID), f_status, 0, QS(u_int), 0 }, + { RS(ID_SGROUP), x_ogroup, 0, QS(str), 0 }, + { RS(ID_SUID), f_status, 0, QS(u_int), 0 }, + { RS(ID_SUSER), x_ouser, 0, QS(str), 0 }, + { RS(ID_TGID), f_status, 0, QS(s_int), 0 }, + { RS(ID_TPGID), f_stat, 0, QS(s_int), 0 }, + { RS(LXCNAME), f_lxc, 0, QS(str), 0 }, + { RS(MEM_CODE), f_statm, 0, QS(sl_int), 0 }, + { RS(MEM_CODE_KIB), f_statm, 0, QS(ul_int), 0 }, + { RS(MEM_DATA), f_statm, 0, QS(sl_int), 0 }, + { RS(MEM_DATA_KIB), f_statm, 0, QS(ul_int), 0 }, + { RS(MEM_DT), f_statm, 0, QS(sl_int), 0 }, + { RS(MEM_LRS), f_statm, 0, QS(sl_int), 0 }, + { RS(MEM_RES), f_statm, 0, QS(sl_int), 0 }, + { RS(MEM_RES_KIB), f_statm, 0, QS(ul_int), 0 }, + { RS(MEM_SHR), f_statm, 0, QS(sl_int), 0 }, + { RS(MEM_SHR_KIB), f_statm, 0, QS(ul_int), 0 }, + { RS(MEM_VIRT), f_statm, 0, QS(sl_int), 0 }, + { RS(MEM_VIRT_KIB), f_statm, 0, QS(ul_int), 0 }, + { RS(NICE), f_stat, 0, QS(sl_int), 0 }, + { RS(NLWP), f_either, 0, QS(s_int), 0 }, + { RS(NS_IPC), f_ns, 0, QS(ul_int), 0 }, + { RS(NS_MNT), f_ns, 0, QS(ul_int), 0 }, + { RS(NS_NET), f_ns, 0, QS(ul_int), 0 }, + { RS(NS_PID), f_ns, 0, QS(ul_int), 0 }, + { RS(NS_USER), f_ns, 0, QS(ul_int), 0 }, + { RS(NS_UTS), f_ns, 0, QS(ul_int), 0 }, + { RS(OOM_ADJ), f_oom, 0, QS(s_int), 0 }, + { RS(OOM_SCORE), f_oom, 0, QS(s_int), 0 }, + { RS(PRIORITY), f_stat, 0, QS(s_int), 0 }, + { RS(PROCESSOR), f_stat, 0, QS(u_int), 0 }, + { RS(RSS), f_stat, 0, QS(sl_int), 0 }, + { RS(RSS_RLIM), f_stat, 0, QS(ul_int), 0 }, + { RS(RTPRIO), f_stat, 0, QS(ul_int), 0 }, + { RS(SCHED_CLASS), f_stat, 0, QS(ul_int), 0 }, + { RS(SD_MACH), f_systemd, -1, QS(str), 0 }, + { RS(SD_OUID), f_systemd, -1, QS(str), 0 }, + { RS(SD_SEAT), f_systemd, -1, QS(str), 0 }, + { RS(SD_SESS), f_systemd, -1, QS(str), 0 }, + { RS(SD_SLICE), f_systemd, -1, QS(str), 0 }, + { RS(SD_UNIT), f_systemd, -1, QS(str), 0 }, + { RS(SD_UUNIT), f_systemd, -1, QS(str), 0 }, + { RS(SIGBLOCKED), f_status, -1, QS(str), 0 }, + { RS(SIGCATCH), f_status, -1, QS(str), 0 }, + { RS(SIGIGNORE), f_status, -1, QS(str), 0 }, + { RS(SIGNALS), f_status, -1, QS(str), 0 }, + { RS(SIGPENDING), f_status, -1, QS(str), 0 }, + { RS(STATE), f_either, 0, QS(s_ch), 0 }, + { RS(SUPGIDS), f_status, -1, QS(str), 0 }, + { RS(SUPGROUPS), x_supgrp, -1, QS(str), 0 }, + { RS(TICS_ALL), f_stat, 0, QS(ull_int), 0 }, + { RS(TICS_ALL_C), f_stat, 0, QS(ull_int), 0 }, + { RS(TICS_DELTA), f_stat, 0, QS(u_int), -1 }, + { RS(TICS_SYSTEM), f_stat, 0, QS(ull_int), 0 }, + { RS(TICS_SYSTEM_C), f_stat, 0, QS(ull_int), 0 }, + { RS(TICS_USER), f_stat, 0, QS(ull_int), 0 }, + { RS(TICS_USER_C), f_stat, 0, QS(ull_int), 0 }, + { RS(TIME_START), f_stat, 0, QS(ull_int), 0 }, + { RS(TTY), f_stat, 0, QS(s_int), 0 }, + { RS(VM_DATA), f_status, 0, QS(ul_int), 0 }, + { RS(VM_EXE), f_status, 0, QS(ul_int), 0 }, + { RS(VM_LIB), f_status, 0, QS(ul_int), 0 }, + { RS(VM_LOCK), f_status, 0, QS(ul_int), 0 }, + { RS(VM_RSS), f_status, 0, QS(ul_int), 0 }, + { RS(VM_SIZE), f_status, 0, QS(ul_int), 0 }, + { RS(VM_STACK), f_status, 0, QS(ul_int), 0 }, + { RS(VM_SWAP), f_status, 0, QS(ul_int), 0 }, + { RS(VM_USED), f_status, 0, QS(ul_int), 0 }, + { RS(VSIZE_PGS), f_stat, 0, QS(ul_int), 0 }, + { RS(WCHAN_ADDR), f_stat, 0, QS(addr), 0 }, + { RS(WCHAN_NAME), 0, -1, QS(str), 0 }, + { RS(noop), 0, 0, QS(noop), 0 }, + { RS(logical_end), 0, 0, QS(noop), 0 }, + { RS(physical_end), 0, 0, QS(noop), 0 } +}; + +#undef RS +#undef QS +#undef srtNAME +#undef setNAME + +#undef f_arg +//#undef f_either // needed later +#undef f_env +#undef f_grp +#undef f_lxc +#undef f_ns +#undef f_oom +//#undef f_stat // needed later +#undef f_statm +//#undef f_status // needed later +#undef f_systemd +#undef f_usr +#undef x_cgroup +#undef x_cmdline +#undef x_environ +#undef x_ogroup +#undef x_ouser +#undef x_supgrp + + +// ___ History Support Private Functions |||||||||||||||||||||||||||||||||||||| +// ( stolen from top when he wasn't looking ) ------------------------------- + +#define HHASH_SIZE 1024 +#define _HASH_PID_(K) (K & (HHASH_SIZE - 1)) + +#define Hr(x) info->hist->x // 'hist ref', minimize stolen impact + +typedef unsigned long long TIC_t; + +typedef struct HST_t { + TIC_t tics; // last frame's tics count + unsigned long maj, min; // last frame's maj/min_flt counts + int pid; // record 'key' + int lnk; // next on hash chain +} HST_t; + + +struct history_info { + int num_tasks; // used as index (tasks tallied) + int HHist_siz; // max number of HST_t structs + HST_t *PHist_sav; // alternating 'old/new' HST_t anchors + HST_t *PHist_new; + int HHash_one [HHASH_SIZE]; // the actual hash tables + int HHash_two [HHASH_SIZE]; // (accessed via PHash_sav/PHash_new) + int HHash_nul [HHASH_SIZE]; // an 'empty' hash table image + int *PHash_sav; // alternating 'old/new' hash tables + int *PHash_new; // (aka. the 'one/two' actual tables) +}; + + +static void config_history ( + struct procps_pidsinfo *info) +{ + int i; + + for (i = 0; i < HHASH_SIZE; i++) // make the 'empty' table image + Hr(HHash_nul[i]) = -1; + memcpy(Hr(HHash_one), Hr(HHash_nul), sizeof(Hr(HHash_nul))); + memcpy(Hr(HHash_two), Hr(HHash_nul), sizeof(Hr(HHash_nul))); + Hr(PHash_sav) = Hr(HHash_one); // alternating 'old/new' hash tables + Hr(PHash_new) = Hr(HHash_two); +} // end: config_history + + +static inline HST_t *histget ( + struct procps_pidsinfo *info, + int pid) +{ + int V = Hr(PHash_sav[_HASH_PID_(pid)]); + + while (-1 < V) { + if (Hr(PHist_sav[V].pid) == pid) + return &Hr(PHist_sav[V]); + V = Hr(PHist_sav[V].lnk); } + return NULL; +} // end: histget + + +static inline void histput ( + struct procps_pidsinfo *info, + unsigned this) +{ + int V = _HASH_PID_(Hr(PHist_new[this].pid)); + + Hr(PHist_new[this].lnk) = Hr(PHash_new[V]); + Hr(PHash_new[V] = this); +} // end: histput + +#undef _HASH_PID_ + + +static int make_hist ( + struct procps_pidsinfo *info, + proc_t *p) +{ + #define slot info->hist->num_tasks + TIC_t tics; + HST_t *h; + + if (slot + 1 >= Hr(HHist_siz)) { + Hr(HHist_siz) = Hr(HHist_siz) * 5 / 4 + 100; + Hr(PHist_sav) = realloc(Hr(PHist_sav), sizeof(HST_t) * Hr(HHist_siz)); + Hr(PHist_new) = realloc(Hr(PHist_new), sizeof(HST_t) * Hr(HHist_siz)); + if (!Hr(PHist_sav || !Hr(PHist_new))) + return -ENOMEM; + } + Hr(PHist_new[slot].pid) = p->tid; + Hr(PHist_new[slot].tics) = tics = (p->utime + p->stime); + Hr(PHist_new[slot].maj) = p->maj_flt; + Hr(PHist_new[slot].min) = p->min_flt; + + histput(info, slot); + + if ((h = histget(info, p->tid))) { + tics -= h->tics; + p->maj_delta = p->maj_flt - h->maj; + p->min_delta = p->min_flt - h->min; + } + p->pcpu = tics; + + slot++; + return 0; + #undef slot +} // end: make_hist + + +static inline void toggle_history ( + struct procps_pidsinfo *info) +{ + void *v; + + v = Hr(PHist_sav); + Hr(PHist_sav) = Hr(PHist_new); + Hr(PHist_new) = v; + + v = Hr(PHash_sav); + Hr(PHash_sav) = Hr(PHash_new); + Hr(PHash_new) = v; + memcpy(Hr(PHash_new), Hr(HHash_nul), sizeof(Hr(HHash_nul))); + + info->hist->num_tasks = 0; +} // end: toggle_history + + +#ifdef UNREF_RPTHASH +static void unref_rpthash ( + struct procps_pidsinfo *info) +{ + int i, j, pop, total_occupied, maxdepth, maxdepth_sav, numdepth + , cross_foot, sz = HHASH_SIZE * (unsigned)sizeof(int); + int depths[HHASH_SIZE]; + + for (i = 0, total_occupied = 0, maxdepth = 0; i < HHASH_SIZE; i++) { + int V = Hr(PHash_new[i]); + j = 0; + if (-1 < V) { + ++total_occupied; + while (-1 < V) { + V = Hr(PHist_new[V].lnk); + if (-1 < V) j++; + } + } + depths[i] = j; + if (maxdepth < j) maxdepth = j; + } + maxdepth_sav = maxdepth; + + fprintf(stderr, + "\n Supplementary HASH report:" + "\n\tTwo Tables providing for %d entries each + 1 extra for 'empty' image" + "\n\t%dk (%d bytes) per table, %d total bytes (including 'empty' image)" + "\n\tResults from latest hash (PHash_new + PHist_new)..." + "\n" + "\n\tTotal hashed = %d" + "\n\tLevel-0 hash entries = %d (%d%% occupied)" + "\n\tMax Depth = %d" + "\n\n" + , HHASH_SIZE, sz / 1024, sz, sz * 3 + , info->hist->num_tasks + , total_occupied, (total_occupied * 100) / HHASH_SIZE + , maxdepth + 1); + + if (total_occupied) { + for (pop = total_occupied, cross_foot = 0; maxdepth; maxdepth--) { + for (i = 0, numdepth = 0; i < HHASH_SIZE; i++) + if (depths[i] == maxdepth) ++numdepth; + fprintf(stderr, + "\t %5d (%3d%%) hash table entries at depth %d\n" + , numdepth, (numdepth * 100) / total_occupied, maxdepth + 1); + pop -= numdepth; + cross_foot += numdepth; + if (0 == pop && cross_foot == total_occupied) break; + } + if (pop) { + fprintf(stderr, "\t %5d (%3d%%) unchained hash table entries\n" + , pop, (pop * 100) / total_occupied); + cross_foot += pop; + } + fprintf(stderr, + "\t -----\n" + "\t %5d total entries occupied\n", cross_foot); + + if (maxdepth_sav) { + fprintf(stderr, "\n PIDs at max depth: "); + for (i = 0; i < HHASH_SIZE; i++) + if (depths[i] == maxdepth_sav) { + j = Hr(PHash_new[i]); + fprintf(stderr, "\n\tpos %4d: %05d", i, Hr(PHist_new[j].pid)); + while (-1 < j) { + j = Hr(PHist_new[j].lnk); + if (-1 < j) fprintf(stderr, ", %05d", Hr(PHist_new[j].pid)); + } + } + fprintf(stderr, "\n"); + } + } +} // end: unref_rpthash +#endif // UNREF_RPTHASH + +#undef HHASH_SIZE + + +// ___ Standard Private Functions ||||||||||||||||||||||||||||||||||||||||||||| + +static inline void assign_results ( + struct procps_pidsinfo *info, + struct pids_stack *stack, + proc_t *p) +{ + struct pids_result *this = stack->head; + + for (;;) { + enum pids_item item = this->item; + if (item >= PROCPS_PIDS_logical_end) + break; + Item_table[item].function(info, this, p); + ++this; + } + return; +} // end: assign_results + + +static inline void cleanup_stack ( + struct pids_result *p, + int depth) +{ + int i; + + for (i = 0; i < depth; i++) { + if (p->item < PROCPS_PIDS_noop) { + if (Item_table[p->item].mustfree && p->result.str) + free((void*)p->result.str); + if (p->item < PROCPS_PIDS_noop) + p->result.ull_int = 0; + } + ++p; + } +} // end: cleanup_stack + + +static inline void cleanup_stacks_all ( + struct procps_pidsinfo *info) +{ + struct stacks_extent *ext = info->extents; + int i; + + while (ext) { + for (i = 0; ext->stacks[i]; i++) + cleanup_stack(ext->stacks[i]->head, info->maxitems); + ext = ext->next; + }; + info->dirty_stacks = 0; +} // end: cleanup_stacks_all + + +static int free_extent ( + struct procps_pidsinfo *info, + struct stacks_extent *ext) +{ + struct stacks_extent *p = info->extents; + + if (ext) { + if (ext == p) { + info->extents = p->next; + free(ext); + return 0; + } + do { + if (ext == p->next) { + p->next = p->next->next; + free(ext); + return 0; + } + p = p->next; + } while (p); + } + return -1; +} // end: free_extent + + +static int items_check_failed ( + int maxitems, + enum pids_item *items) +{ + int i; + + for (i = 0; i < maxitems; i++) { + if (items[i] < 0) + return -1; + if (items[i] > PROCPS_PIDS_noop) { + return -1; + } + } + return 0; +} // end: items_check_failed + + +static inline void libflags_set ( + struct procps_pidsinfo *info) +{ + int i; + + info->flags = info->history_yes = 0; + for (i = 0; i < info->curitems; i++) { + info->flags |= Item_table[info->items[i]].oldflags; + info->history_yes |= Item_table[info->items[i]].makehist; + } + if (info->flags & f_either) { + if (!(info->flags & f_stat)) + info->flags |= f_status; + } + return; +} // end: libflags_set + + +static inline void oldproc_close ( + struct procps_pidsinfo *info) +{ + if (info->PT != NULL) { + closeproc(info->PT); + info->PT = NULL; + } + return; +} // end: oldproc_close + + +static inline int oldproc_open ( + struct procps_pidsinfo *info, + int supp_flgs, + ...) +{ + va_list vl; + int *ids; + + if (info->PT == NULL) { + va_start(vl, supp_flgs); + ids = va_arg(vl, int*); + va_end(vl); + if (NULL == (info->PT = openproc(info->flags | supp_flgs, ids))) + return 0; + } + return 1; +} // end: oldproc_open + + +static inline struct pids_result *stack_itemize ( + struct pids_result *p, + int depth, + enum pids_item *items) +{ + struct pids_result *p_sav = p; + int i; + + for (i = 0; i < depth; i++) { + p->item = items[i]; + p->result.ull_int = 0; + ++p; + } + return p_sav; +} // end: stack_itemize + + +static inline int tally_proc ( + struct procps_pidsinfo *info, + struct pids_counts *counts, + proc_t *p) +{ + switch (p->state) { + case 'R': + ++counts->running; + break; + case 'S': + case 'D': + ++counts->sleeping; + break; + case 'T': + ++counts->stopped; + break; + case 'Z': + ++counts->zombied; + break; + default: // keep gcc happy + break; + } + ++counts->total; + + if (info->history_yes) + return !make_hist(info, p); + return 1; +} // end: tally_proc + + +static void validate_stacks ( + void *stacks, + const char *who) +{ +#if 0 + #include + static int once = 0; + struct stacks_extent *ext = (struct stacks_extent *)stacks; + int i, t, x, n = 0; + + fprintf(stderr, " %s: called by '%s'\n", __func__, who); + fprintf(stderr, " %s: ext_numitems = %d, ext_numstacks = %d, extents = %p, next = %p\n", __func__, ext->ext_numitems, ext->ext_numstacks, ext, ext->next); + fprintf(stderr, " %s: stacks_extent results excluding the end-of-stack element ...\n", __func__); + for (x = 0; NULL != ext->stacks[x]; x++) { + struct pids_stack *h = ext->stacks[x]; + struct pids_result *r = h->head; + fprintf(stderr, " %s: v[%03d] = %p, h = %p, fill_id #%-5u", __func__, x, h, r, (unsigned)h->fill_id); + for (i = 0; r->item < PROCPS_PIDS_logical_end; i++, r++) + ; + t = i + 1; + fprintf(stderr, " - found %d elements for stack %d\n", i, n); + ++n; + } + if (!once) { + fprintf(stderr, " %s: found %d total stack(s), each %d bytes (including eos)\n", __func__, x, (int)(sizeof(struct pids_stack) + (sizeof(struct pids_result) * t))); + fprintf(stderr, " %s: sizeof(struct pids_stack) = %d\n", __func__, (int)sizeof(struct pids_stack)); + fprintf(stderr, " %s: sizeof(struct pids_result) = %d\n", __func__, (int)sizeof(struct pids_result)); + fprintf(stderr, " %s: sizeof(struct stacks_extent) = %d\n", __func__, (int)sizeof(struct stacks_extent)); + once = 1; + } + fputc('\n', stderr); + return; +#endif +} // end: validate_stacks + + +// ___ Public Functions ||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +/* + * procps_pids_new(): + * + * @info: location of returned new structure + * + * Returns: 0 on success <0 on failure + */ +PROCPS_EXPORT int procps_pids_new ( + struct procps_pidsinfo **info, + int maxitems, + enum pids_item *items) +{ + struct procps_pidsinfo *p; + int pgsz; + + if (info == NULL || *info != NULL) + return -EINVAL; + if (items_check_failed(maxitems, items)) + return -EINVAL; + + if (!(p = calloc(1, sizeof(struct procps_pidsinfo)))) + return -ENOMEM; + // allow for our PROCPS_PIDS_physical_end + if (!(p->items = calloc((maxitems + 1), sizeof(enum pids_item)))) { + free(p); + return -ENOMEM; + } + if (!(p->hist = calloc((maxitems + 1), sizeof(struct history_info)))) { + free(p->items); + free(p); + return -ENOMEM; + } + + memcpy(p->items, items, sizeof(enum pids_item) * maxitems); + p->items[maxitems] = PROCPS_PIDS_physical_end; + p->curitems = p->maxitems = maxitems + 1; + libflags_set(p); + + pgsz = getpagesize(); + while (pgsz > 1024) { pgsz >>= 1; p->pgs2k_shift++; } + + config_history(p); + + p->refcount = 1; + *info = p; + return 0; +} // end: procps_pids_new + + +/* procps_pids_reap(): + * + * Harvest all the available tasks/threads and provide the result + * stacks along with a summary of the information gathered. + * + * Returns: pointer to a pids_reap struct on success, NULL on error. + */ +PROCPS_EXPORT struct pids_reap *procps_pids_reap ( + struct procps_pidsinfo *info, + enum pids_reap_type which) +{ + #define amtGROW 256 + #define n_alloc info->alloc_total + #define n_inuse info->inuse_total + static proc_t task; // static for initial zeroes + later dynamic free(s) + proc_t*(*read_something)(PROCTAB*, proc_t*); + struct pids_stacks *ext; + int n_save = n_alloc; + + if (!info->anchor) { + if (!(info->anchor = calloc(sizeof(void*), amtGROW))) + return NULL; + if (!(ext = procps_pids_stacks_alloc(info, amtGROW))) + return NULL; + memcpy(info->anchor, ext->stacks, sizeof(void*) * amtGROW); + if (!(info->reap.reaped.stacks = calloc(sizeof(void*), amtGROW))) + return NULL; + n_save = info->alloc_total = amtGROW; + } + + if (info->dirty_stacks) + cleanup_stacks_all(info); + + memset(&info->reap.counts, 0, sizeof(struct pids_counts)); + read_something = which ? readeither : readproc; + + if (!oldproc_open(info, 0)) + return NULL; + toggle_history(info); + + for (n_inuse = 0; ; n_inuse++) { + if (n_inuse == n_alloc) { + n_alloc += amtGROW; + if (!(info->anchor = realloc(info->anchor, sizeof(void*) * n_alloc))) + return NULL; + if (!(ext = procps_pids_stacks_alloc(info, amtGROW))) + return NULL; + memcpy(info->anchor + n_inuse, ext->stacks, sizeof(void*) * amtGROW); + } + if (NULL == read_something(info->PT, &task)) + break; + if (!tally_proc(info, &info->reap.counts, &task)) + return NULL; + assign_results(info, info->anchor[n_inuse], &task); + } + + oldproc_close(info); + if (n_save != n_alloc + && !(info->reap.reaped.stacks = realloc(info->reap.reaped.stacks, sizeof(void*) * n_alloc))) + return NULL; + memcpy(info->reap.reaped.stacks, info->anchor, sizeof(void*) * n_alloc); + info->dirty_stacks = 1; + return &info->reap; + #undef n_alloc + #undef n_inuse + #undef amtGROW +} // end: procps_pids_reap + + +PROCPS_EXPORT int procps_pids_ref ( + struct procps_pidsinfo *info) +{ + if (info == NULL) + return -EINVAL; + + info->refcount++; + return info->refcount; +} // end: procps_pids_ref + + +PROCPS_EXPORT int procps_pids_reset ( + struct procps_pidsinfo *info, + int newmaxitems, + enum pids_item *newitems) +{ + struct stacks_extent *ext; + int i; + + if (info == NULL) + return -EINVAL; + /* disallow (for now?) absolute increases in stacks size + ( users must 'unref' and then 'new' to achieve that ) */ + if (newmaxitems + 1 > info->maxitems) + return -EINVAL; + if (items_check_failed(newmaxitems, newitems)) + return -EINVAL; + + /* shame on this caller, they didn't change anything - but they might have + shortened their stacks. yet we cannot reposition their logical_end enum + lest we overlay some string result that would never be freed, let alone + all those strings that could follow it. so here's the deal, this caller + will just have to suffer the additional overhead of retrieving unneeded + results until they offer us a real, properly formatted 'reset' request! */ + if (info->curitems == newmaxitems + 1 + && !memcmp(info->items, newitems, sizeof(enum pids_item) * newmaxitems)) + return 0; + + if (info->dirty_stacks) + cleanup_stacks_all(info); + + memcpy(info->items, newitems, sizeof(enum pids_item) * newmaxitems); + info->items[newmaxitems] = PROCPS_PIDS_logical_end; + // account for above PROCPS_PIDS_logical_end + info->curitems = newmaxitems + 1; + + ext = info->extents; + while (ext) { + for (i = 0; ext->stacks[i]; i++) + stack_itemize(ext->stacks[i]->head, info->curitems, info->items); + validate_stacks(ext, __func__); + ext = ext->next; + }; + + libflags_set(info); + return 0; +} // end: procps_pids_reset + + +/* + * procps_pids_stacks_alloc(): + * + * Allocate and initialize one or more stacks each of which is anchored in an + * associated pids_stack structure (which may include extra user space). + * + * All such stacks will will have their result structures properly primed with + * 'items', while the result itself will be zeroed. + * + * Returns an array of pointers representing the 'heads' of each new stack. + */ +PROCPS_EXPORT struct pids_stacks *procps_pids_stacks_alloc ( + struct procps_pidsinfo *info, + int maxstacks) +{ + struct stacks_extent *p_blob; + struct pids_stack **p_vect; + struct pids_stack *p_head; + size_t vect_size, head_size, list_size, blob_size; + void *v_head, *v_list; + int i; + + if (info == NULL || info->items == NULL) + return NULL; + if (maxstacks < 1) + return NULL; + + vect_size = sizeof(void *) * maxstacks; // address vectors themselves + vect_size += sizeof(void *); // plus NULL delimiter + head_size = sizeof(struct pids_stack); // a head struct + list_size = sizeof(struct pids_result) * info->maxitems; // a results stack + blob_size = sizeof(struct stacks_extent); // the extent anchor itself + blob_size += vect_size; // all vectors + delim + blob_size += head_size * maxstacks; // all head structs + blob_size += list_size * maxstacks; // all results stacks + + /* note: all memory is allocated in a single blob, facilitating a later free(). + as a minimum, it's important that the result structures themselves always be + contiguous for any given stack (just as they are when defined statically). */ + if (NULL == (p_blob = calloc(1, blob_size))) + return NULL; + + p_blob->next = info->extents; + info->extents = p_blob; + p_blob->stacks = (void *)p_blob + sizeof(struct stacks_extent); + p_vect = p_blob->stacks; + v_head = (void *)p_vect + vect_size; + v_list = v_head + (head_size * maxstacks); + + for (i = 0; i < maxstacks; i++) { + p_head = (struct pids_stack *)v_head; + p_head->head = stack_itemize((struct pids_result *)v_list, info->curitems, info->items); + p_blob->stacks[i] = p_head; + v_list += list_size; + v_head += head_size; + } + p_blob->ext_numitems = info->maxitems; + p_blob->ext_numstacks = maxstacks; + validate_stacks(p_blob, __func__); + return (struct pids_stacks *)p_blob; +} // end: procps_pids_stacks_alloc + + +PROCPS_EXPORT int procps_pids_stacks_dealloc ( + struct procps_pidsinfo *info, + struct pids_stacks **these) +{ + struct stacks_extent *ext; + + if (info == NULL || these == NULL) + return -EINVAL; + if ((*these)->stacks == NULL || (*these)->stacks[0] == NULL) + return -EINVAL; + + ext = (struct stacks_extent *)(*these); + int rc = free_extent(info, ext); + *these = NULL; + return rc; +} // end: procps_pids_stacks_dealloc + + +PROCPS_EXPORT struct pids_counts *procps_pids_stacks_fill ( + struct procps_pidsinfo *info, + struct pids_stacks *these, + int maxstacks, + enum pids_fill_type which) +{ + static proc_t task; // static for initial zeroes + later dynamic free(s) + unsigned ids[FILL_ID_MAX + 1]; + int i; + + if (info == NULL || these == NULL) + return NULL; + if (these->stacks == NULL || these->stacks[0] == NULL) + return NULL; + if (which != PROCPS_FILL_PID && which != PROCPS_FILL_UID) + return NULL; + if (maxstacks < 1 || maxstacks > FILL_ID_MAX) + return NULL; + + for (i = 0; i < maxstacks; i++) { + if (these->stacks[i] == NULL) + break; + ids[i] = these->stacks[i]->fill_id; + } + ids[i] = 0; + + if (info->dirty_stacks) + cleanup_stacks_all(info); + memset(&info->counts, 0, sizeof(struct pids_counts)); + + if (!oldproc_open(info, which, ids, i)) + return NULL; + toggle_history(info); + + for (i = 0; i < maxstacks; i++) { + if (these->stacks[i] == NULL) + break; + if (!readproc(info->PT, &task)) + break; + if (!tally_proc(info, &info->counts, &task)) { + oldproc_close(info); + return NULL; + } + assign_results(info, these->stacks[i], &task); + } + + oldproc_close(info); + info->dirty_stacks = 1; + validate_stacks(these, __func__); + return &info->counts; +} // end: procps_pids_stacks_fill + + +/* + * procps_pids_stacks_sort(): + * + * Sort stacks anchored in the passed pids_stack pointers array + * based on the designated sort enumerator and specified order. + * + * Returns those same addresses sorted. + * + * Note: all of the stacks must be homogeneous (of equal length and content). + */ +PROCPS_EXPORT struct pids_stack **procps_pids_stacks_sort ( + struct procps_pidsinfo *info, + struct pids_stack **stacks, + int numstacked, + enum pids_item sort, + enum pids_sort_order order) +{ + struct sort_parms parms; + struct pids_result *p; + int offset; + + if (info == NULL || stacks == NULL) + return NULL; + if (sort < 0 || sort > PROCPS_PIDS_noop) + return NULL; + if (order < -1 || order > +1) + return NULL; + if (numstacked < 2) + return stacks; + + offset = 0; + p = stacks[0]->head; + for (;;) { + if (p->item == sort) + break; + ++offset; + if (offset >= info->curitems) + return NULL; + if (p->item > PROCPS_PIDS_noop) + return NULL; + ++p; + } + + parms.offset = offset; + parms.order = order; + + qsort_r(stacks, numstacked, sizeof(void *), (QSR_t)Item_table[p->item].callback, &parms); + return stacks; +} // end: procps_pids_stacks_sort + + +PROCPS_EXPORT int procps_pids_unref ( + struct procps_pidsinfo **info) +{ + if (info == NULL || *info == NULL) + return -EINVAL; + + (*info)->refcount--; + if ((*info)->refcount == 0) { +#ifdef UNREF_RPTHASH + unref_rpthash(*info); +#endif + if ((*info)->extents) { + cleanup_stacks_all(*info); + do { + struct stacks_extent *p = (*info)->extents; + (*info)->extents = (*info)->extents->next; + free(p); + } while ((*info)->extents); + } + if ((*info)->reap.reaped.stacks) + free((*info)->reap.reaped.stacks); + if ((*info)->anchor) + free((*info)->anchor); + if ((*info)->items) + free((*info)->items); + if ((*info)->hist) { + free((*info)->hist->PHist_sav); + free((*info)->hist->PHist_new); + free((*info)->hist); + } + free(*info); + *info = NULL; + return 0; + } + return (*info)->refcount; +} // end: procps_pids_unref diff --git a/proc/pids.h b/proc/pids.h new file mode 100644 index 00000000..2d0bb3cd --- /dev/null +++ b/proc/pids.h @@ -0,0 +1,232 @@ +/* + * pids.h - task/thread/process related declarations for libproc + * + * Copyright (C) 1998-2005 Albert Cahalan + * Copyright (C) 2015 Craig Small + * Copyright (C) 2015 Jim Warner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PROC_PIDS_H +#define _PROC_PIDS_H + +__BEGIN_DECLS + +enum pids_item { + PROCPS_PIDS_ADDR_END_CODE, // addr + PROCPS_PIDS_ADDR_KSTK_EIP, // addr + PROCPS_PIDS_ADDR_KSTK_ESP, // addr + PROCPS_PIDS_ADDR_START_CODE, // addr + PROCPS_PIDS_ADDR_START_STACK, // addr + PROCPS_PIDS_ALARM, // sl_int + PROCPS_PIDS_CGROUP, // str + PROCPS_PIDS_CMD, // str + PROCPS_PIDS_CMDLINE, // str + PROCPS_PIDS_ENVIRON, // str + PROCPS_PIDS_EXIT_SIGNAL, // s_int + PROCPS_PIDS_FLAGS, // ul_int + PROCPS_PIDS_FLT_MAJ, // ul_int + PROCPS_PIDS_FLT_MAJ_C, // ul_int + PROCPS_PIDS_FLT_MAJ_DELTA, // ul_int + PROCPS_PIDS_FLT_MIN, // ul_int + PROCPS_PIDS_FLT_MIN_C, // ul_int + PROCPS_PIDS_FLT_MIN_DELTA, // ul_int + PROCPS_PIDS_ID_EGID, // u_int + PROCPS_PIDS_ID_EGROUP, // str + PROCPS_PIDS_ID_EUID, // u_int + PROCPS_PIDS_ID_EUSER, // str + PROCPS_PIDS_ID_FGID, // u_int + PROCPS_PIDS_ID_FGROUP, // str + PROCPS_PIDS_ID_FUID, // u_int + PROCPS_PIDS_ID_FUSER, // str + PROCPS_PIDS_ID_PGRP, // s_int + PROCPS_PIDS_ID_PID, // s_int + PROCPS_PIDS_ID_PPID, // s_int + PROCPS_PIDS_ID_RGID, // u_int + PROCPS_PIDS_ID_RGROUP, // str + PROCPS_PIDS_ID_RUID, // u_int + PROCPS_PIDS_ID_RUSER, // str + PROCPS_PIDS_ID_SESSION, // s_int + PROCPS_PIDS_ID_SGID, // u_int + PROCPS_PIDS_ID_SGROUP, // str + PROCPS_PIDS_ID_SUID, // u_int + PROCPS_PIDS_ID_SUSER, // str + PROCPS_PIDS_ID_TGID, // s_int + PROCPS_PIDS_ID_TPGID, // s_int + PROCPS_PIDS_LXCNAME, // str + PROCPS_PIDS_MEM_CODE, // sl_int + PROCPS_PIDS_MEM_CODE_KIB, // ul_int + PROCPS_PIDS_MEM_DATA, // sl_int + PROCPS_PIDS_MEM_DATA_KIB, // ul_int + PROCPS_PIDS_MEM_DT, // sl_int + PROCPS_PIDS_MEM_LRS, // sl_int + PROCPS_PIDS_MEM_RES, // sl_int + PROCPS_PIDS_MEM_RES_KIB, // ul_int + PROCPS_PIDS_MEM_SHR, // sl_int + PROCPS_PIDS_MEM_SHR_KIB, // ul_int + PROCPS_PIDS_MEM_VIRT, // sl_int + PROCPS_PIDS_MEM_VIRT_KIB, // ul_int + PROCPS_PIDS_NICE, // sl_int + PROCPS_PIDS_NLWP, // s_int + PROCPS_PIDS_NS_IPC, // ul_int + PROCPS_PIDS_NS_MNT, // ul_int + PROCPS_PIDS_NS_NET, // ul_int + PROCPS_PIDS_NS_PID, // ul_int + PROCPS_PIDS_NS_USER, // ul_int + PROCPS_PIDS_NS_UTS, // ul_int + PROCPS_PIDS_OOM_ADJ, // s_int + PROCPS_PIDS_OOM_SCORE, // s_int + PROCPS_PIDS_PRIORITY, // s_int + PROCPS_PIDS_PROCESSOR, // u_int + PROCPS_PIDS_RSS, // sl_int + PROCPS_PIDS_RSS_RLIM, // ul_int + PROCPS_PIDS_RTPRIO, // ul_int + PROCPS_PIDS_SCHED_CLASS, // ul_int + PROCPS_PIDS_SD_MACH, // str + PROCPS_PIDS_SD_OUID, // str + PROCPS_PIDS_SD_SEAT, // str + PROCPS_PIDS_SD_SESS, // str + PROCPS_PIDS_SD_SLICE, // str + PROCPS_PIDS_SD_UNIT, // str + PROCPS_PIDS_SD_UUNIT, // str + PROCPS_PIDS_SIGBLOCKED, // str + PROCPS_PIDS_SIGCATCH, // str + PROCPS_PIDS_SIGIGNORE, // str + PROCPS_PIDS_SIGNALS, // str + PROCPS_PIDS_SIGPENDING, // str + PROCPS_PIDS_STATE, // s_ch + PROCPS_PIDS_SUPGIDS, // str + PROCPS_PIDS_SUPGROUPS, // str + PROCPS_PIDS_TICS_ALL, // ull_int + PROCPS_PIDS_TICS_ALL_C, // ull_int + PROCPS_PIDS_TICS_DELTA, // u_int + PROCPS_PIDS_TICS_SYSTEM, // ull_int + PROCPS_PIDS_TICS_SYSTEM_C, // ull_int + PROCPS_PIDS_TICS_USER, // ull_int + PROCPS_PIDS_TICS_USER_C, // ull_int + PROCPS_PIDS_TIME_START, // ull_int + PROCPS_PIDS_TTY, // s_int + PROCPS_PIDS_VM_DATA, // ul_int + PROCPS_PIDS_VM_EXE, // ul_int + PROCPS_PIDS_VM_LIB, // ul_int + PROCPS_PIDS_VM_LOCK, // ul_int + PROCPS_PIDS_VM_RSS, // ul_int + PROCPS_PIDS_VM_SIZE, // ul_int + PROCPS_PIDS_VM_STACK, // ul_int + PROCPS_PIDS_VM_SWAP, // ul_int + PROCPS_PIDS_VM_USED, // ul_int + PROCPS_PIDS_VSIZE_PGS, // ul_int + PROCPS_PIDS_WCHAN_ADDR, // addr + PROCPS_PIDS_WCHAN_NAME, // str + PROCPS_PIDS_noop // n/a +}; + +enum pids_fill_type { + PROCPS_FILL_PID = 0x1000, + PROCPS_FILL_UID = 0x4000 +}; + +enum pids_reap_type { + PROCPS_REAP_TASKS_ONLY = 0, + PROCPS_REAP_THREADS_TOO = 1 +}; + +enum pids_sort_order { + PROCPS_SORT_ASCEND = -1, + PROCPS_SORT_DESCEND = +1 +}; + + +struct procps_pidsinfo; + +struct pids_result { + enum pids_item item; + union { + char s_ch; + int s_int; + unsigned int u_int; + long sl_int; + unsigned long ul_int; + unsigned long long ull_int; + void * addr; + const char * str; + } result; +}; + +struct pids_stack { + struct pids_result *head; + unsigned fill_id; +}; + +struct pids_stacks { + struct pids_stack **stacks; +}; + +struct pids_counts { + int total; + int running, sleeping, stopped, zombied; +}; + +struct pids_reap { + struct pids_stacks reaped; + struct pids_counts counts; +}; + + +int procps_pids_new ( + struct procps_pidsinfo **info, + int maxitems, + enum pids_item *items); + +struct pids_reap *procps_pids_reap ( + struct procps_pidsinfo *info, + enum pids_reap_type which); + +int procps_pids_ref ( + struct procps_pidsinfo *info); + +int procps_pids_reset ( + struct procps_pidsinfo *info, + int newmaxitems, + enum pids_item *newitems); + +struct pids_stacks *procps_pids_stacks_alloc ( + struct procps_pidsinfo *info, + int maxstacks); + +int procps_pids_stacks_dealloc ( + struct procps_pidsinfo *info, + struct pids_stacks **these); + +struct pids_counts *procps_pids_stacks_fill ( + struct procps_pidsinfo *info, + struct pids_stacks *these, + int maxstacks, + enum pids_fill_type which); + +struct pids_stack **procps_pids_stacks_sort ( + struct procps_pidsinfo *info, + struct pids_stack **stacks, + int numstacked, + enum pids_item sort, + enum pids_sort_order order); + +int procps_pids_unref ( + struct procps_pidsinfo **info); + +__END_DECLS + +#endif /* _PROC_PIDS_H */