/* * 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 #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 FPRINT_STACKS // enable validate_stacks output #define FILL_ID_MAX 255 // upper limit for pid/uid fills #define MEMORY_INCR 128 // amt by which allocations grow #define READS_BEGUN (info->read) // a read is in progress 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 struct stacks_extent *read; // an extent used for active reads proc_t*(*read_something)(PROCTAB*, proc_t*); // readproc/readeither via which 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 reaped; // 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 STV_set(e,x) setDECL(e) { \ (void)I; R->result.str = *P-> x; P-> x = NULL; } // take ownership of true vectorized strings #define VEC_set(e,x) setDECL(e) { \ (void)I; R->result.strv = 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) STV_set(CGROUP, cgroup) VEC_set(CGROUP_V, cgroup) STR_set(CMD, cmd) STV_set(CMDLINE, cmdline) VEC_set(CMDLINE_V, cmdline) STV_set(ENVIRON, environ) VEC_set(ENVIRON_V, 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) setDECL(LXCNAME) { (void)I; R->result.str = (char *)P->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 STV_set #undef VEC_set // ___ Free Storage Support ||||||||||||||||||||||||||||||||||||||||||||||||||| #define freNAME(e) free_results_ ## e static void freNAME(str) (struct pids_result *R) { if (R->result.str) free((void *)R->result.str); } static void freNAME(strv) (struct pids_result *R) { if (R->result.str && *R->result.strv) free((void *)*R->result.strv); } // ___ 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(strv) ( 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.strv || !b->result.strv) return 0; return P->order * strcoll((*b->result.strv), (*a->result.strv)); } 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 |||||||||||||||||||||||||||||||||||||||||||||||||||||| // from either 'stat' or 'status' (preferred) #define f_either PROC_SPARE_1 #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 // these next three will yield true verctorized strings #define v_arg PROC_FILLARG #define v_cgroup PROC_FILLCGROUP #define v_env PROC_FILLENV // remaining are compound flags, yielding a single string (maybe vectorized) #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 void (*FRE_t)(struct pids_result *); typedef int (*QSR_t)(const void *, const void *, void *); #define RS(e) (SET_t)setNAME(e) #define FF(e) (FRE_t)freNAME(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 setsfunc; // the actual result setting routine unsigned oldflags; // PROC_FILLxxxx flags for this item FRE_t freefunc; // free function for strings storage QSR_t sortfunc; // sort cmp func for a specific type int needhist; // a result requires history support } Item_table[] = { /* setsfunc oldflags freefunc sortfunc needhist --------------------- ---------- --------- ------------ -------- */ { RS(ADDR_END_CODE), f_stat, NULL, QS(addr), 0 }, { RS(ADDR_KSTK_EIP), f_stat, NULL, QS(addr), 0 }, { RS(ADDR_KSTK_ESP), f_stat, NULL, QS(addr), 0 }, { RS(ADDR_START_CODE), f_stat, NULL, QS(addr), 0 }, { RS(ADDR_START_STACK), f_stat, NULL, QS(addr), 0 }, { RS(ALARM), f_stat, NULL, QS(sl_int), 0 }, { RS(CGROUP), x_cgroup, FF(str), QS(str), 0 }, { RS(CGROUP_V), v_cgroup, FF(strv), QS(strv), 0 }, { RS(CMD), f_either, FF(str), QS(str), 0 }, { RS(CMDLINE), x_cmdline, FF(str), QS(str), 0 }, { RS(CMDLINE_V), v_arg, FF(strv), QS(strv), 0 }, { RS(ENVIRON), x_environ, FF(str), QS(str), 0 }, { RS(ENVIRON_V), v_env, FF(strv), QS(strv), 0 }, { RS(EXIT_SIGNAL), f_stat, NULL, QS(s_int), 0 }, { RS(FLAGS), f_stat, NULL, QS(ul_int), 0 }, { RS(FLT_MAJ), f_stat, NULL, QS(ul_int), 0 }, { RS(FLT_MAJ_C), f_stat, NULL, QS(ul_int), 0 }, { RS(FLT_MAJ_DELTA), f_stat, NULL, QS(ul_int), +1 }, { RS(FLT_MIN), f_stat, NULL, QS(ul_int), 0 }, { RS(FLT_MIN_C), f_stat, NULL, QS(ul_int), 0 }, { RS(FLT_MIN_DELTA), f_stat, NULL, QS(ul_int), +1 }, { RS(ID_EGID), 0, NULL, QS(u_int), 0 }, { RS(ID_EGROUP), f_grp, NULL, QS(str), 0 }, { RS(ID_EUID), 0, NULL, QS(u_int), 0 }, { RS(ID_EUSER), f_usr, NULL, QS(str), 0 }, { RS(ID_FGID), f_status, NULL, QS(u_int), 0 }, { RS(ID_FGROUP), x_ogroup, NULL, QS(str), 0 }, { RS(ID_FUID), f_status, NULL, QS(u_int), 0 }, { RS(ID_FUSER), x_ouser, NULL, QS(str), 0 }, { RS(ID_PGRP), f_stat, NULL, QS(s_int), 0 }, { RS(ID_PID), 0, NULL, QS(s_int), 0 }, { RS(ID_PPID), f_either, NULL, QS(s_int), 0 }, { RS(ID_RGID), f_status, NULL, QS(u_int), 0 }, { RS(ID_RGROUP), x_ogroup, NULL, QS(str), 0 }, { RS(ID_RUID), f_status, NULL, QS(u_int), 0 }, { RS(ID_RUSER), x_ouser, NULL, QS(str), 0 }, { RS(ID_SESSION), f_stat, NULL, QS(s_int), 0 }, { RS(ID_SGID), f_status, NULL, QS(u_int), 0 }, { RS(ID_SGROUP), x_ogroup, NULL, QS(str), 0 }, { RS(ID_SUID), f_status, NULL, QS(u_int), 0 }, { RS(ID_SUSER), x_ouser, NULL, QS(str), 0 }, { RS(ID_TGID), f_status, NULL, QS(s_int), 0 }, { RS(ID_TPGID), f_stat, NULL, QS(s_int), 0 }, { RS(LXCNAME), f_lxc, NULL, QS(str), 0 }, { RS(MEM_CODE), f_statm, NULL, QS(sl_int), 0 }, { RS(MEM_CODE_KIB), f_statm, NULL, QS(ul_int), 0 }, { RS(MEM_DATA), f_statm, NULL, QS(sl_int), 0 }, { RS(MEM_DATA_KIB), f_statm, NULL, QS(ul_int), 0 }, { RS(MEM_DT), f_statm, NULL, QS(sl_int), 0 }, { RS(MEM_LRS), f_statm, NULL, QS(sl_int), 0 }, { RS(MEM_RES), f_statm, NULL, QS(sl_int), 0 }, { RS(MEM_RES_KIB), f_statm, NULL, QS(ul_int), 0 }, { RS(MEM_SHR), f_statm, NULL, QS(sl_int), 0 }, { RS(MEM_SHR_KIB), f_statm, NULL, QS(ul_int), 0 }, { RS(MEM_VIRT), f_statm, NULL, QS(sl_int), 0 }, { RS(MEM_VIRT_KIB), f_statm, NULL, QS(ul_int), 0 }, { RS(NICE), f_stat, NULL, QS(sl_int), 0 }, { RS(NLWP), f_either, NULL, QS(s_int), 0 }, { RS(NS_IPC), f_ns, NULL, QS(ul_int), 0 }, { RS(NS_MNT), f_ns, NULL, QS(ul_int), 0 }, { RS(NS_NET), f_ns, NULL, QS(ul_int), 0 }, { RS(NS_PID), f_ns, NULL, QS(ul_int), 0 }, { RS(NS_USER), f_ns, NULL, QS(ul_int), 0 }, { RS(NS_UTS), f_ns, NULL, QS(ul_int), 0 }, { RS(OOM_ADJ), f_oom, NULL, QS(s_int), 0 }, { RS(OOM_SCORE), f_oom, NULL, QS(s_int), 0 }, { RS(PRIORITY), f_stat, NULL, QS(s_int), 0 }, { RS(PROCESSOR), f_stat, NULL, QS(u_int), 0 }, { RS(RSS), f_stat, NULL, QS(sl_int), 0 }, { RS(RSS_RLIM), f_stat, NULL, QS(ul_int), 0 }, { RS(RTPRIO), f_stat, NULL, QS(ul_int), 0 }, { RS(SCHED_CLASS), f_stat, NULL, QS(ul_int), 0 }, { RS(SD_MACH), f_systemd, FF(str), QS(str), 0 }, { RS(SD_OUID), f_systemd, FF(str), QS(str), 0 }, { RS(SD_SEAT), f_systemd, FF(str), QS(str), 0 }, { RS(SD_SESS), f_systemd, FF(str), QS(str), 0 }, { RS(SD_SLICE), f_systemd, FF(str), QS(str), 0 }, { RS(SD_UNIT), f_systemd, FF(str), QS(str), 0 }, { RS(SD_UUNIT), f_systemd, FF(str), QS(str), 0 }, { RS(SIGBLOCKED), f_status, FF(str), QS(str), 0 }, { RS(SIGCATCH), f_status, FF(str), QS(str), 0 }, { RS(SIGIGNORE), f_status, FF(str), QS(str), 0 }, { RS(SIGNALS), f_status, FF(str), QS(str), 0 }, { RS(SIGPENDING), f_status, FF(str), QS(str), 0 }, { RS(STATE), f_either, NULL, QS(s_ch), 0 }, { RS(SUPGIDS), f_status, FF(str), QS(str), 0 }, { RS(SUPGROUPS), x_supgrp, FF(str), QS(str), 0 }, { RS(TICS_ALL), f_stat, NULL, QS(ull_int), 0 }, { RS(TICS_ALL_C), f_stat, NULL, QS(ull_int), 0 }, { RS(TICS_DELTA), f_stat, NULL, QS(u_int), +1 }, { RS(TICS_SYSTEM), f_stat, NULL, QS(ull_int), 0 }, { RS(TICS_SYSTEM_C), f_stat, NULL, QS(ull_int), 0 }, { RS(TICS_USER), f_stat, NULL, QS(ull_int), 0 }, { RS(TICS_USER_C), f_stat, NULL, QS(ull_int), 0 }, { RS(TIME_START), f_stat, NULL, QS(ull_int), 0 }, { RS(TTY), f_stat, NULL, QS(s_int), 0 }, { RS(VM_DATA), f_status, NULL, QS(ul_int), 0 }, { RS(VM_EXE), f_status, NULL, QS(ul_int), 0 }, { RS(VM_LIB), f_status, NULL, QS(ul_int), 0 }, { RS(VM_LOCK), f_status, NULL, QS(ul_int), 0 }, { RS(VM_RSS), f_status, NULL, QS(ul_int), 0 }, { RS(VM_SIZE), f_status, NULL, QS(ul_int), 0 }, { RS(VM_STACK), f_status, NULL, QS(ul_int), 0 }, { RS(VM_SWAP), f_status, NULL, QS(ul_int), 0 }, { RS(VM_USED), f_status, NULL, QS(ul_int), 0 }, { RS(VSIZE_PGS), f_stat, NULL, QS(ul_int), 0 }, { RS(WCHAN_ADDR), f_stat, NULL, QS(addr), 0 }, { RS(WCHAN_NAME), 0, FF(str), QS(str), 0 }, { RS(noop), 0, NULL, QS(noop), 0 }, { RS(logical_end), 0, NULL, QS(noop), 0 }, { RS(physical_end), 0, NULL, QS(noop), 0 } }; #undef RS #undef FF #undef QS #undef setNAME #undef freNAME #undef srtNAME //#undef f_either // needed later #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 v_arg #undef v_cgroup #undef v_env #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 nSLOT info->hist->num_tasks TIC_t tics; HST_t *h; if (nSLOT + 1 >= Hr(HHist_siz)) { Hr(HHist_siz) += MEMORY_INCR; 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[nSLOT].pid) = p->tid; Hr(PHist_new[nSLOT].tics) = tics = (p->utime + p->stime); Hr(PHist_new[nSLOT].maj) = p->maj_flt; Hr(PHist_new[nSLOT].min) = p->min_flt; histput(info, nSLOT); if ((h = histget(info, p->tid))) { tics -= h->tics; p->pcpu = tics; p->maj_delta = p->maj_flt - h->maj; p->min_delta = p->min_flt - h->min; } nSLOT++; return 0; #undef nSLOT } // 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 * (int)sizeof(int) , hsz = (int)sizeof(HST_t) * Hr(HHist_siz); 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 History Memory Costs:" "\n\tHST_t size = %d, total allocated = %d," "\n\tthus PHist_new & PHist_sav consumed %dk (%d) total bytes." "\n" "\n\tTwo hash tables provide for %d entries each + 1 extra 'empty' image," "\n\tthus %dk (%d) bytes per table for %dk (%d) total bytes." "\n" "\n\tGrand total = %dk (%d) bytes." "\n" "\n Hash Results Report:" "\n\tTotal hashed = %d" "\n\tLevel-0 hash entries = %d (%d%% occupied)" "\n\tMax Depth = %d" "\n\n" , (int)sizeof(HST_t), Hr(HHist_siz) , hsz / 1024, hsz , HHASH_SIZE , sz / 1024, sz, (sz * 3) / 1024, sz * 3 , (hsz + (sz * 3)) / 1024, hsz + (sz * 3) , info->hist->num_tasks , total_occupied, (total_occupied * 100) / HHASH_SIZE , maxdepth); 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); pop -= numdepth; cross_foot += numdepth; if (0 == pop && cross_foot == total_occupied) break; } if (pop) { fprintf(stderr, "\t %5d (%3d%%) unchained entries (at depth 0)\n" , pop, (pop * 100) / total_occupied); cross_foot += pop; } fprintf(stderr, "\t -----\n" "\t %5d total entries occupied\n", cross_foot); if (maxdepth_sav > 1) { 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 Hr #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].setsfunc(info, this, p); info->dirty_stacks |= Item_table[item].freefunc ? 1 : 0; ++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].freefunc) Item_table[p->item].freefunc(p); 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 inline int items_check_failed ( int maxitems, enum pids_item *items) { int i; for (i = 0; i < maxitems; i++) { // a pids_item is currently unsigned, but we'll protect our future 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]].needhist; } 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 #ifdef FPRINT_STACKS static void validate_stacks ( void *stacks, const char *who) { #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; } // end: validate_stacks #endif // ___ 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_EXPORT struct pids_stack *procps_pids_read_next ( struct procps_pidsinfo *info) { static proc_t task; // static for initial zeroes + later dynamic free(s) if (info == NULL || ! READS_BEGUN) return NULL; if (info->dirty_stacks) { cleanup_stack(info->read->stacks[0]->head, info->maxitems); info->dirty_stacks = 0; } if (NULL == info->read_something(info->PT, &task)) return NULL; assign_results(info, info->read->stacks[0], &task); return info->read->stacks[0]; } // end: procps_pids_read_next PROCPS_EXPORT int procps_pids_read_open ( struct procps_pidsinfo *info, enum pids_reap_type which) { if (info == NULL || READS_BEGUN) return -EINVAL; if (!info->maxitems && !info->curitems) return -EINVAL; if (which != PROCPS_REAP_TASKS_ONLY && which != PROCPS_REAP_THREADS_TOO) return -EINVAL; if (!(info->read = (struct stacks_extent *)procps_pids_stacks_alloc(info, 1))) return -ENOMEM; if (!oldproc_open(info, 0)) return -1; info->read_something = which ? readeither : readproc; return 0; } // end: procps_pids_read_open PROCPS_EXPORT int procps_pids_read_shut ( struct procps_pidsinfo *info) { int rc; if (info == NULL || ! READS_BEGUN) return -EINVAL; oldproc_close(info); rc = free_extent(info, info->read); info->read = NULL; return rc; } // end: procps_pids_read_shut /* 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 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*(*reap_something)(PROCTAB*, proc_t*); struct pids_stacks *ext; int n_save; if (info == NULL || READS_BEGUN) return NULL; if (!info->maxitems && !info->curitems) return NULL; if (which != PROCPS_REAP_TASKS_ONLY && which != PROCPS_REAP_THREADS_TOO) return NULL; n_save = n_alloc; if (!info->anchor) { if ((!(info->anchor = calloc(sizeof(void *), MEMORY_INCR))) || (!(info->reaped.stacks = calloc(sizeof(void *), MEMORY_INCR))) || (!(ext = procps_pids_stacks_alloc(info, MEMORY_INCR)))) return NULL; memcpy(info->anchor, ext->stacks, sizeof(void *) * MEMORY_INCR); n_save = info->alloc_total = MEMORY_INCR; } if (info->dirty_stacks) cleanup_stacks_all(info); memset(&info->reaped.counts, 0, sizeof(struct pids_counts)); if (!oldproc_open(info, 0)) return NULL; toggle_history(info); reap_something = which ? readeither : readproc; for (n_inuse = 0; ; n_inuse++) { if (n_inuse == n_alloc) { n_alloc += MEMORY_INCR; if ((!(info->anchor = realloc(info->anchor, sizeof(void *) * n_alloc))) || (!(ext = procps_pids_stacks_alloc(info, MEMORY_INCR)))) return NULL; memcpy(info->anchor + n_inuse, ext->stacks, sizeof(void *) * MEMORY_INCR); } if (NULL == reap_something(info->PT, &task)) break; if (!tally_proc(info, &info->reaped.counts, &task)) { oldproc_close(info); return NULL; } assign_results(info, info->anchor[n_inuse], &task); } oldproc_close(info); if (n_save != n_alloc && !(info->reaped.stacks = realloc(info->reaped.stacks, sizeof(void *) * n_alloc))) return NULL; memcpy(info->reaped.stacks, info->anchor, sizeof(void *) * n_alloc); return &info->reaped; #undef n_alloc #undef n_inuse } // 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. and unless they have altered the depth of the stacks we're not gonna change anything either! */ 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); #ifdef FPRINT_STACKS validate_stacks(ext, __func__); #endif 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; #ifdef FPRINT_STACKS validate_stacks(p_blob, __func__); #endif 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; int rc; if (info == NULL || these == NULL) return -EINVAL; if ((*these)->stacks == NULL || (*these)->stacks[0] == NULL) return -EINVAL; ext = (struct stacks_extent *)(*these); 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 || READS_BEGUN) 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); #ifdef FPRINT_STACKS validate_stacks(these, __func__); #endif 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; // a pids_item is currently unsigned, but we'll protect our future 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].sortfunc, &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)->reaped.stacks) free((*info)->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