#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) */
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
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);)
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
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
watch is used to execute a program periodically, showing
output to the screen.
-
endmenu
#if ENABLE_DESKTOP
#include <sys/times.h> /* for times() */
-//#include <sys/sysinfo.h> /* for sysinfo() */
#ifndef AT_CLKTCK
#define AT_CLKTCK 17
#endif
#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 */
{
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));
} 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
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 */
format_header();
p = NULL;
- while ((p = procps_scan(p, need_flags))) {
+ while ((p = procps_scan(p, need_flags)) != NULL) {
format_process(p);
}
| PSSCAN_VSZ
| PSSCAN_COMM
| use_selinux
- ))) {
+ )) != NULL) {
#if ENABLE_SELINUX
if (use_selinux) {
len = printf("%5u %-32.32s %s ",
/* 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;
#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;
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();
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
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;)