diff options
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | libbb/procps.c | 25 | ||||
-rw-r--r-- | procps/Config.in | 8 | ||||
-rw-r--r-- | procps/ps.c | 31 | ||||
-rw-r--r-- | procps/top.c | 21 |
5 files changed, 74 insertions, 13 deletions
diff --git a/include/libbb.h b/include/libbb.h index fd61517c2..1694d2c2d 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1275,6 +1275,7 @@ enum { COMM_LEN = 16 }; #endif typedef struct procps_status_t { DIR *dir; + IF_FEATURE_SHOW_THREADS(DIR *task_dir;) uint8_t shift_pages_to_bytes; uint8_t shift_pages_to_kb; /* Fields are set to 0/NULL if failed to determine (or not requested) */ @@ -1348,6 +1349,7 @@ enum { PSSCAN_CPU = (1 << 19) * ENABLE_FEATURE_TOP_SMP_PROCESS, PSSCAN_NICE = (1 << 20) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS, PSSCAN_RUIDGID = (1 << 21) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS, + PSSCAN_TASKS = (1 << 22) * ENABLE_FEATURE_SHOW_THREADS, /* These are all retrieved from proc/NN/stat in one go: */ PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID /**/ | PSSCAN_COMM | PSSCAN_STATE diff --git a/libbb/procps.c b/libbb/procps.c index 974661785..845a2141b 100644 --- a/libbb/procps.c +++ b/libbb/procps.c @@ -110,6 +110,10 @@ static procps_status_t* FAST_FUNC alloc_procps_scan(void) void FAST_FUNC free_procps_scan(procps_status_t* sp) { closedir(sp->dir); +#if ENABLE_FEATURE_SHOW_THREADS + if (sp->task_dir) + closedir(sp->task_dir); +#endif free(sp->argv0); free(sp->exe); IF_SELINUX(free(sp->context);) @@ -189,14 +193,35 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) sp = alloc_procps_scan(); for (;;) { +#if ENABLE_FEATURE_SHOW_THREADS + if ((flags & PSSCAN_TASKS) && sp->task_dir) { + entry = readdir(sp->task_dir); + if (entry) + goto got_entry; + closedir(sp->task_dir); + sp->task_dir = NULL; + } +#endif entry = readdir(sp->dir); if (entry == NULL) { free_procps_scan(sp); return NULL; } + IF_FEATURE_SHOW_THREADS(got_entry:) pid = bb_strtou(entry->d_name, NULL, 10); if (errno) continue; +#if ENABLE_FEATURE_SHOW_THREADS + if ((flags & PSSCAN_TASKS) && !sp->task_dir) { + /* We found another /proc/PID. Do not use it, + * there will be /proc/PID/task/PID (same PID!), + * so just go ahead and dive into /proc/PID/task. */ + char task_dir[sizeof("/proc/%u/task") + sizeof(int)*3]; + sprintf(task_dir, "/proc/%u/task", pid); + sp->task_dir = xopendir(task_dir); + continue; + } +#endif /* After this point we have to break, not continue * ("continue" would mean that current /proc/NNN diff --git a/procps/Config.in b/procps/Config.in index 9146ff6bf..6a9a36638 100644 --- a/procps/Config.in +++ b/procps/Config.in @@ -188,6 +188,13 @@ config FEATURE_TOPMEM help Enable 's' in top (gives lots of memory info). +config FEATURE_SHOW_THREADS + bool "Support for showing threads in ps/top" + default n + depends on PS || TOP + help + Enables ps -T option and 'h' command in top + config UPTIME bool "uptime" default n @@ -203,5 +210,4 @@ config WATCH watch is used to execute a program periodically, showing output to the screen. - endmenu diff --git a/procps/ps.c b/procps/ps.c index 6523f0f38..b35b49c04 100644 --- a/procps/ps.c +++ b/procps/ps.c @@ -17,7 +17,6 @@ enum { MAX_WIDTH = 2*1024 }; #if ENABLE_DESKTOP #include <sys/times.h> /* for times() */ -//#include <sys/sysinfo.h> /* for sysinfo() */ #ifndef AT_CLKTCK #define AT_CLKTCK 17 #endif @@ -61,6 +60,7 @@ struct globals { #define kernel_HZ (G.kernel_HZ ) #define seconds_since_boot (G.seconds_since_boot) #define default_o (G.default_o ) +#define INIT_G() do { } while (0) #if ENABLE_FEATURE_PS_TIME /* for ELF executables, notes are pushed before environment and args */ @@ -452,21 +452,34 @@ int ps_main(int argc UNUSED_PARAM, char **argv) { procps_status_t *p; llist_t* opt_o = NULL; - IF_SELINUX(int opt;) + int opt; + enum { + OPT_Z = (1 << 0), + OPT_o = (1 << 1), + OPT_a = (1 << 2), + OPT_A = (1 << 3), + OPT_d = (1 << 4), + OPT_e = (1 << 5), + OPT_f = (1 << 6), + OPT_l = (1 << 7), + OPT_T = (1 << 8) * ENABLE_FEATURE_SHOW_THREADS, + }; + + INIT_G(); // POSIX: // -a Write information for all processes associated with terminals // Implementations may omit session leaders from this list // -A Write information for all processes // -d Write information for all processes, except session leaders - // -e Write information for all processes (equivalent to -A.) + // -e Write information for all processes (equivalent to -A) // -f Generate a full listing // -l Generate a long listing // -o col1,col2,col3=header // Select which columns to display /* We allow (and ignore) most of the above. FIXME */ opt_complementary = "o::"; - IF_SELINUX(opt =) getopt32(argv, "Zo:aAdefl", &opt_o); + opt = getopt32(argv, "Zo:aAdefl" IF_FEATURE_SHOW_THREADS("T"), &opt_o); if (opt_o) { do { parse_o(llist_pop(&opt_o)); @@ -474,7 +487,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv) } else { /* Below: parse_o() needs char*, NOT const char*... */ #if ENABLE_SELINUX - if (!(opt & 1) || !is_selinux_enabled()) { + if (!(opt & OPT_Z) || !is_selinux_enabled()) { /* no -Z or no SELinux: do not show LABEL */ strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1); } else @@ -485,6 +498,10 @@ int ps_main(int argc UNUSED_PARAM, char **argv) parse_o(default_o); } post_process(); +#if ENABLE_FEATURE_SHOW_THREADS + if (opt & OPT_T) + need_flags |= PSSCAN_TASKS; +#endif /* Was INT_MAX, but some libc's go belly up with printf("%.*s") * and such large widths */ @@ -497,7 +514,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv) format_header(); p = NULL; - while ((p = procps_scan(p, need_flags))) { + while ((p = procps_scan(p, need_flags)) != NULL) { format_process(p); } @@ -558,7 +575,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | PSSCAN_VSZ | PSSCAN_COMM | use_selinux - ))) { + )) != NULL) { #if ENABLE_SELINUX if (use_selinux) { len = printf("%5u %-32.32s %s ", diff --git a/procps/top.c b/procps/top.c index a1ad7881e..dbaaca14f 100644 --- a/procps/top.c +++ b/procps/top.c @@ -976,7 +976,10 @@ int top_main(int argc UNUSED_PARAM, char **argv) /* read process IDs & status for all the processes */ while ((p = procps_scan(p, scan_mask)) != NULL) { int n; - if (scan_mask == TOP_MASK) { +#if ENABLE_FEATURE_TOPMEM + if (scan_mask != TOPMEM_MASK) +#endif + { n = ntop; top = xrealloc_vector(top, 6, ntop++); top[n].pid = p->pid; @@ -991,8 +994,9 @@ int top_main(int argc UNUSED_PARAM, char **argv) #if ENABLE_FEATURE_TOP_SMP_PROCESS top[n].last_seen_on_cpu = p->last_seen_on_cpu; #endif - } else { /* TOPMEM */ + } #if ENABLE_FEATURE_TOPMEM + else { /* TOPMEM */ if (!(p->mapped_ro | p->mapped_rw)) continue; /* kernel threads are ignored */ n = ntop; @@ -1007,15 +1011,15 @@ int top_main(int argc UNUSED_PARAM, char **argv) topmem[n].dirty = p->private_dirty + p->shared_dirty; topmem[n].dirty_sh = p->shared_dirty; topmem[n].stack = p->stack; -#endif } +#endif } /* end of "while we read /proc" */ if (ntop == 0) { bb_error_msg("no process info in /proc"); break; } - if (scan_mask == TOP_MASK) { + if (scan_mask != TOPMEM_MASK) { #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE if (!prev_hist_count) { do_stats(); @@ -1039,7 +1043,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) if (OPT_BATCH_MODE) { lines_rem = INT_MAX; } - if (scan_mask == TOP_MASK) + if (scan_mask != TOPMEM_MASK) display_process_list(lines_rem, col); #if ENABLE_FEATURE_TOPMEM else @@ -1076,6 +1080,13 @@ int top_main(int argc UNUSED_PARAM, char **argv) sort_function[2] = time_sort; # endif } +#if ENABLE_FEATURE_SHOW_THREADS + if (c == 'h' + IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) + ) { + scan_mask ^= PSSCAN_TASKS; + } +#endif # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE if (c == 'p') { IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |