]> granicus.if.org Git - procps-ng/commitdiff
library: allow duplicated results for <pids> interface
authorJim Warner <james.warner@comcast.net>
Tue, 13 Oct 2015 05:00:00 +0000 (00:00 -0500)
committerCraig Small <csmall@enc.com.au>
Wed, 14 Oct 2015 10:50:31 +0000 (21:50 +1100)
Ok, here is that rather major internal redesign hinted
at in the three previous commits. Its need was quickly
revealed after adapting top then attempting to display
newly added 'CGNAME' fields and an existing 'CGROUPS'.

That very quickly generated a SEGV. And the reason was
just as quickly recognized. Both fields relied on that
proc_t.cgroup member yet whichever result structure is
first in a stack is the one which assumes ownership of
of the vectored sting by resetting its cgroup to NULL.

So this commit introduces reference counting for a few
of the fields in the proc_t. Specifically there are 17
entries in the Item_table dealing with strings/vectors
where ownership is transferred to newlib. Now whenever
such fields are represented more than once in a stack,
the strings will be duplicated instead of transferred.
In this way we can generally remain optimized avoiding
string copies, yet still accommodate them when needed.

There's an exception to this scheme: those true string
vectors (CGROUP_V, CMDLINE_V and ENVIRON_V). When such
fields are duplicated in a stack the result structures
beyond the first will be set to NULL, which the caller
will (should) already be equipped to deal with anyway.

Signed-off-by: Jim Warner <james.warner@comcast.net>
proc/pids.c

index 62ac2937f698d57cb52b2f972fd23e1e27e64211..a9efcfcc00bb69d268c6e1ef524adb873808657c 100644 (file)
 enum pids_item PROCPS_PIDS_logical_end  = PROCPS_PIDS_noop + 1;
 enum pids_item PROCPS_PIDS_physical_end = PROCPS_PIDS_noop + 2;
 
+    // these represent the proc_t fields whose storage cannot be managed
+    // optimally if they are ever referenced more than once in any stack
+enum rel_ref {
+    ref_CGROUP,    ref_CGROUP_V, ref_CMD,       ref_CMDLINE,
+    ref_CMDLINE_V, ref_ENVIRON,  ref_ENVIRON_V, ref_SD_MACH,
+    ref_SD_OUID,   ref_SD_SEAT,  ref_SD_SESS,   ref_SD_SLICE,
+    ref_SD_UNIT,   ref_SD_UUNIT, ref_SUPGIDS,   ref_SUPGROUPS,
+    MAXIMUM_ref
+};
 
 struct stacks_extent {
     struct pids_stack **stacks;
@@ -90,37 +99,44 @@ struct procps_pidsinfo {
     PROCTAB *PT;                       // the old library essential interface
     unsigned long hertz;               // for TIME_ALL & TIME_ELAPSED calculations
     unsigned long long boot_seconds;   // for TIME_ELAPSED calculation
+    int ref_counts[MAXIMUM_ref];       // ref counts for special string fields
 };
 
 
 // ___ 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
-                 because of the CVT_set requirement & for future flexibility */
+static char **dupstrvecs (char **pvec) {
+    /* maybe someday we'll actually duplicate these string vectors but,
+       for now, let's just gently tell the user 'thanks, but no thanks'
+       ( remember, that user was already provided with one valid copy ) */
+    return NULL;
+}
 
 #define setNAME(e) set_results_ ## e
 #define setDECL(e) static void setNAME(e) \
     (struct procps_pidsinfo *I, struct pids_result *R, proc_t *P)
 
-        // convert pages to kib
+// 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
+// strdup of a static char array
 #define DUP_set(e,x) setDECL(e) { \
     (void)I; R->result.str = strdup(P-> x); }
-        // regular assignment copy
+// 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
+// take ownership of a regular char* string if possible, else duplicate
 #define STR_set(e,x) setDECL(e) { \
-    (void)I; R->result.str = P-> x; P-> x = NULL; }
-        // take ownership of a vectorized single string
+    if (I->ref_counts[ref_ ## e] > 1) R->result.str = strdup(P-> x); \
+    else { R->result.str = P-> x; P-> x = NULL; } }
+// take ownership of a vectorized single string if possible, else duplicate
 #define STV_set(e,x) setDECL(e) { \
-    (void)I; R->result.str = *P-> x; P-> x = NULL; }
-        // take ownership of true vectorized strings
+    if (I->ref_counts[ref_ ## e] > 1) R->result.str = strdup(*P-> x); \
+    else { R->result.str = *P-> x; P-> x = NULL; } }
+// take ownership of true vectorized strings if possible, else return NULL
 #define VEC_set(e,x) setDECL(e) { \
-    (void)I; R->result.strv = P-> x; P-> x = NULL; }
+    if (I->ref_counts[ref_ ## e] > 1) R->result.strv = dupstrvecs(P-> x); \
+    else { R->result.strv = P-> x; P-> x = NULL; } }
 
 REG_set(ADDR_END_CODE,    ul_int,  end_code)
 REG_set(ADDR_KSTK_EIP,    ul_int,  kstk_eip)
@@ -370,126 +386,127 @@ static struct {
     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
+    int      refcount;            // the result needs reference counts
 } Item_table[] = {
-/*    setsfunc               oldflags    freefunc   sortfunc      needhist
-      ---------------------  ----------  ---------  ------------  -------- */
-    { RS(ADDR_END_CODE),     f_stat,     NULL,      QS(ul_int),   0       },
-    { RS(ADDR_KSTK_EIP),     f_stat,     NULL,      QS(ul_int),   0       },
-    { RS(ADDR_KSTK_ESP),     f_stat,     NULL,      QS(ul_int),   0       },
-    { RS(ADDR_START_CODE),   f_stat,     NULL,      QS(ul_int),   0       },
-    { RS(ADDR_START_STACK),  f_stat,     NULL,      QS(ul_int),   0       },
-    { RS(ALARM),             f_stat,     NULL,      QS(sl_int),   0       },
-    { RS(CGNAME),            x_cgroup,   FF(str),   QS(str),      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       }, // free w/ simple_read...
-    { RS(ID_EGROUP),         f_grp,      NULL,      QS(str),      0       },
-    { RS(ID_EUID),           0,          NULL,      QS(u_int),    0       }, // free w/ simple_read...
-    { 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       }, // free w/ simple_nextpid
-    { 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),           0,          NULL,      QS(s_int),    0       }, // free w/ simple_nextpid
-    { 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_ALL),          f_stat,     NULL,      QS(ull_int),  0       },
-    { RS(TIME_ELAPSED),      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(TTY_NAME),          f_stat,     FF(str),   QS(strvers),  0       },
-    { RS(TTY_NUMBER),        f_stat,     FF(str),   QS(strvers),  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(ul_int),   0       },
-    { RS(WCHAN_NAME),        0,          FF(str),   QS(str),      0       }, // tid already free
-    { RS(extra),             0,          NULL,      QS(ull_int),  0       },
-    { RS(noop),              0,          NULL,      QS(noop),     0       },
-    { RS(logical_end),       0,          NULL,      QS(noop),     0       },
-    { RS(physical_end),      0,          NULL,      QS(noop),     0       }
+/*    setsfunc               oldflags    freefunc   sortfunc      needhist  refcount
+      ---------------------  ----------  ---------  ------------  --------  ------------- */
+    { RS(ADDR_END_CODE),     f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(ADDR_KSTK_EIP),     f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(ADDR_KSTK_ESP),     f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(ADDR_START_CODE),   f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(ADDR_START_STACK),  f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(ALARM),             f_stat,     NULL,      QS(sl_int),   0,        -1            },
+    { RS(CGNAME),            x_cgroup,   FF(str),   QS(str),      0,        ref_CGROUP    }, // refcount: diff result, same source
+    { RS(CGROUP),            x_cgroup,   FF(str),   QS(str),      0,        ref_CGROUP    }, // refcount: diff result, same source
+    { RS(CGROUP_V),          v_cgroup,   FF(strv),  QS(strv),     0,        ref_CGROUP_V  },
+    { RS(CMD),               f_either,   FF(str),   QS(str),      0,        ref_CMD       },
+    { RS(CMDLINE),           x_cmdline,  FF(str),   QS(str),      0,        ref_CMDLINE   },
+    { RS(CMDLINE_V),         v_arg,      FF(strv),  QS(strv),     0,        ref_CMDLINE_V },
+    { RS(ENVIRON),           x_environ,  FF(str),   QS(str),      0,        ref_ENVIRON   },
+    { RS(ENVIRON_V),         v_env,      FF(strv),  QS(strv),     0,        ref_ENVIRON_V },
+    { RS(EXIT_SIGNAL),       f_stat,     NULL,      QS(s_int),    0,        -1            },
+    { RS(FLAGS),             f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(FLT_MAJ),           f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(FLT_MAJ_C),         f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(FLT_MAJ_DELTA),     f_stat,     NULL,      QS(ul_int),   +1,       -1            },
+    { RS(FLT_MIN),           f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(FLT_MIN_C),         f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(FLT_MIN_DELTA),     f_stat,     NULL,      QS(ul_int),   +1,       -1            },
+    { RS(ID_EGID),           0,          NULL,      QS(u_int),    0,        -1            }, // oldflags: free w/ simple_read...
+    { RS(ID_EGROUP),         f_grp,      NULL,      QS(str),      0,        -1            },
+    { RS(ID_EUID),           0,          NULL,      QS(u_int),    0,        -1            }, // oldflags: free w/ simple_read...
+    { RS(ID_EUSER),          f_usr,      NULL,      QS(str),      0,        -1            },
+    { RS(ID_FGID),           f_status,   NULL,      QS(u_int),    0,        -1            },
+    { RS(ID_FGROUP),         x_ogroup,   NULL,      QS(str),      0,        -1            },
+    { RS(ID_FUID),           f_status,   NULL,      QS(u_int),    0,        -1            },
+    { RS(ID_FUSER),          x_ouser,    NULL,      QS(str),      0,        -1            },
+    { RS(ID_PGRP),           f_stat,     NULL,      QS(s_int),    0,        -1            },
+    { RS(ID_PID),            0,          NULL,      QS(s_int),    0,        -1            }, // oldflags: free w/ simple_nextpid
+    { RS(ID_PPID),           f_either,   NULL,      QS(s_int),    0,        -1            },
+    { RS(ID_RGID),           f_status,   NULL,      QS(u_int),    0,        -1            },
+    { RS(ID_RGROUP),         x_ogroup,   NULL,      QS(str),      0,        -1            },
+    { RS(ID_RUID),           f_status,   NULL,      QS(u_int),    0,        -1            },
+    { RS(ID_RUSER),          x_ouser,    NULL,      QS(str),      0,        -1            },
+    { RS(ID_SESSION),        f_stat,     NULL,      QS(s_int),    0,        -1            },
+    { RS(ID_SGID),           f_status,   NULL,      QS(u_int),    0,        -1            },
+    { RS(ID_SGROUP),         x_ogroup,   NULL,      QS(str),      0,        -1            },
+    { RS(ID_SUID),           f_status,   NULL,      QS(u_int),    0,        -1            },
+    { RS(ID_SUSER),          x_ouser,    NULL,      QS(str),      0,        -1            },
+    { RS(ID_TGID),           0,          NULL,      QS(s_int),    0,        -1            }, // oldflags: free w/ simple_nextpid
+    { RS(ID_TPGID),          f_stat,     NULL,      QS(s_int),    0,        -1            },
+    { RS(LXCNAME),           f_lxc,      NULL,      QS(str),      0,        -1            },
+    { RS(MEM_CODE),          f_statm,    NULL,      QS(sl_int),   0,        -1            },
+    { RS(MEM_CODE_KIB),      f_statm,    NULL,      QS(ul_int),   0,        -1            },
+    { RS(MEM_DATA),          f_statm,    NULL,      QS(sl_int),   0,        -1            },
+    { RS(MEM_DATA_KIB),      f_statm,    NULL,      QS(ul_int),   0,        -1            },
+    { RS(MEM_DT),            f_statm,    NULL,      QS(sl_int),   0,        -1            },
+    { RS(MEM_LRS),           f_statm,    NULL,      QS(sl_int),   0,        -1            },
+    { RS(MEM_RES),           f_statm,    NULL,      QS(sl_int),   0,        -1            },
+    { RS(MEM_RES_KIB),       f_statm,    NULL,      QS(ul_int),   0,        -1            },
+    { RS(MEM_SHR),           f_statm,    NULL,      QS(sl_int),   0,        -1            },
+    { RS(MEM_SHR_KIB),       f_statm,    NULL,      QS(ul_int),   0,        -1            },
+    { RS(MEM_VIRT),          f_statm,    NULL,      QS(sl_int),   0,        -1            },
+    { RS(MEM_VIRT_KIB),      f_statm,    NULL,      QS(ul_int),   0,        -1            },
+    { RS(NICE),              f_stat,     NULL,      QS(sl_int),   0,        -1            },
+    { RS(NLWP),              f_either,   NULL,      QS(s_int),    0,        -1            },
+    { RS(NS_IPC),            f_ns,       NULL,      QS(ul_int),   0,        -1            },
+    { RS(NS_MNT),            f_ns,       NULL,      QS(ul_int),   0,        -1            },
+    { RS(NS_NET),            f_ns,       NULL,      QS(ul_int),   0,        -1            },
+    { RS(NS_PID),            f_ns,       NULL,      QS(ul_int),   0,        -1            },
+    { RS(NS_USER),           f_ns,       NULL,      QS(ul_int),   0,        -1            },
+    { RS(NS_UTS),            f_ns,       NULL,      QS(ul_int),   0,        -1            },
+    { RS(OOM_ADJ),           f_oom,      NULL,      QS(s_int),    0,        -1            },
+    { RS(OOM_SCORE),         f_oom,      NULL,      QS(s_int),    0,        -1            },
+    { RS(PRIORITY),          f_stat,     NULL,      QS(s_int),    0,        -1            },
+    { RS(PROCESSOR),         f_stat,     NULL,      QS(u_int),    0,        -1            },
+    { RS(RSS),               f_stat,     NULL,      QS(sl_int),   0,        -1            },
+    { RS(RSS_RLIM),          f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(RTPRIO),            f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(SCHED_CLASS),       f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(SD_MACH),           f_systemd,  FF(str),   QS(str),      0,        ref_SD_MACH   },
+    { RS(SD_OUID),           f_systemd,  FF(str),   QS(str),      0,        ref_SD_OUID   },
+    { RS(SD_SEAT),           f_systemd,  FF(str),   QS(str),      0,        ref_SD_SEAT   },
+    { RS(SD_SESS),           f_systemd,  FF(str),   QS(str),      0,        ref_SD_SESS   },
+    { RS(SD_SLICE),          f_systemd,  FF(str),   QS(str),      0,        ref_SD_SLICE  },
+    { RS(SD_UNIT),           f_systemd,  FF(str),   QS(str),      0,        ref_SD_UNIT   },
+    { RS(SD_UUNIT),          f_systemd,  FF(str),   QS(str),      0,        ref_SD_UUNIT  },
+    { RS(SIGBLOCKED),        f_status,   FF(str),   QS(str),      0,        -1            },
+    { RS(SIGCATCH),          f_status,   FF(str),   QS(str),      0,        -1            },
+    { RS(SIGIGNORE),         f_status,   FF(str),   QS(str),      0,        -1            },
+    { RS(SIGNALS),           f_status,   FF(str),   QS(str),      0,        -1            },
+    { RS(SIGPENDING),        f_status,   FF(str),   QS(str),      0,        -1            },
+    { RS(STATE),             f_either,   NULL,      QS(s_ch),     0,        -1            },
+    { RS(SUPGIDS),           f_status,   FF(str),   QS(str),      0,        ref_SUPGIDS   },
+    { RS(SUPGROUPS),         x_supgrp,   FF(str),   QS(str),      0,        ref_SUPGROUPS },
+    { RS(TICS_ALL),          f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TICS_ALL_C),        f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TICS_DELTA),        f_stat,     NULL,      QS(u_int),    +1,       -1            },
+    { RS(TICS_SYSTEM),       f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TICS_SYSTEM_C),     f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TICS_USER),         f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TICS_USER_C),       f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TIME_ALL),          f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TIME_ELAPSED),      f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TIME_START),        f_stat,     NULL,      QS(ull_int),  0,        -1            },
+    { RS(TTY),               f_stat,     NULL,      QS(s_int),    0,        -1            },
+    { RS(TTY_NAME),          f_stat,     FF(str),   QS(strvers),  0,        -1            },
+    { RS(TTY_NUMBER),        f_stat,     FF(str),   QS(strvers),  0,        -1            },
+    { RS(VM_DATA),           f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_EXE),            f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_LIB),            f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_LOCK),           f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_RSS),            f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_SIZE),           f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_STACK),          f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_SWAP),           f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VM_USED),           f_status,   NULL,      QS(ul_int),   0,        -1            },
+    { RS(VSIZE_PGS),         f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(WCHAN_ADDR),        f_stat,     NULL,      QS(ul_int),   0,        -1            },
+    { RS(WCHAN_NAME),        0,          FF(str),   QS(str),      0,        -1            }, // oldflags: tid already free
+    { RS(extra),             0,          NULL,      QS(ull_int),  0,        -1            },
+    { RS(noop),              0,          NULL,      QS(noop),     0,        -1            },
+    { RS(logical_end),       0,          NULL,      QS(noop),     0,        -1            },
+    { RS(physical_end),      0,          NULL,      QS(noop),     0,        -1            }
 };
 
 #undef RS
@@ -858,12 +875,15 @@ static inline int items_check_failed (
 static inline void libflags_set (
         struct procps_pidsinfo *info)
 {
-    int i;
+    int i, n;
 
+    memset (info->ref_counts, 0, sizeof(info->ref_counts));
     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;
+        n = Item_table[info->items[i]].refcount;
+        if (n > -1) ++info->ref_counts[n];
     }
     if (info->flags & f_either) {
         if (!(info->flags & f_stat))