--- /dev/null
+/*
+ * pids.c - task/thread/process related declarations for libproc
+ *
+ * Copyright (C) 1998-2005 Albert Cahalan
+ * Copyright (C) 2015 Craig Small <csmall@enc.com.au>
+ * Copyright (C) 2015 Jim Warner <james.warner@comcast.net>
+ *
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <proc/pids.h>
+#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 <stdio.h>
+ 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