OSDN Git Service

ps,top: add an option to show threads. +260 bytes of code
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 19 Sep 2009 20:29:42 +0000 (22:29 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 19 Sep 2009 20:29:42 +0000 (22:29 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
include/libbb.h
libbb/procps.c
procps/Config.in
procps/ps.c
procps/top.c

index fd61517..1694d2c 100644 (file)
@@ -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
index 9746617..845a214 100644 (file)
@@ -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
index 9146ff6..6a9a366 100644 (file)
@@ -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
index 6523f0f..b35b49c 100644 (file)
@@ -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  ",
index a1ad788..dbaaca1 100644 (file)
@@ -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;)