android pie 上top 指令是能够查看 thread 的信息,可是android 不能
在android o 上,更新toybox command
top -H -p threadid
diff --git a/generated/flags.h b/generated/flags.h
old mode 100644
new mode 100755
index 431b924..dd0f0f7
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -1199,19 +1199,21 @@
// iotop >0AaKOk*o*p*u*s#<1=7d#=3<1n#<1bq
#undef OPTSTR_iotop
-#define OPTSTR_iotop ">0AaKOk*o*p*u*s#<1=7d#=3<1n#<1bq"
+#define OPTSTR_iotop ">0AaKOHk*o*p*u*s#<1=7d#=3<1m#n#<1bq"
#ifdef CLEANUP_iotop
#undef CLEANUP_iotop
#undef FOR_iotop
#undef FLAG_q
#undef FLAG_b
#undef FLAG_n
+#undef FLAG_m
#undef FLAG_d
#undef FLAG_s
#undef FLAG_u
#undef FLAG_p
#undef FLAG_o
#undef FLAG_k
+#undef FLAG_H
#undef FLAG_O
#undef FLAG_K
#undef FLAG_a
@@ -2778,15 +2780,16 @@
#undef FLAG_v
#endif
-// top >0mO*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO] >0mO*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]
+// top >0O*Hk*o*p*u*s#<1d#=3<1m#n#<1bq[!oO] >0O*Hk*o*p*u*s#<1d#=3<1m#n#<1bq[!oO]
#undef OPTSTR_top
-#define OPTSTR_top ">0mO*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]"
+#define OPTSTR_top ">0O*Hk*o*p*u*s#<1d#=3<1m#n#<1bq[!oO]"
#ifdef CLEANUP_top
#undef CLEANUP_top
#undef FOR_top
#undef FLAG_q
#undef FLAG_b
#undef FLAG_n
+#undef FLAG_m
#undef FLAG_d
#undef FLAG_s
#undef FLAG_u
@@ -2795,7 +2798,6 @@
#undef FLAG_k
#undef FLAG_H
#undef FLAG_O
-#undef FLAG_m
#endif
// touch <1acd:mr:t:h[!dtr] <1acd:mr:t:h[!dtr]
@@ -4231,19 +4233,21 @@
#ifndef TT
#define TT this.iotop
#endif
-#define FLAG_q (FORCED_FLAG<<0)
-#define FLAG_b (FORCED_FLAG<<1)
-#define FLAG_n (FORCED_FLAG<<2)
-#define FLAG_d (FORCED_FLAG<<3)
-#define FLAG_s (FORCED_FLAG<<4)
-#define FLAG_u (FORCED_FLAG<<5)
-#define FLAG_p (FORCED_FLAG<<6)
-#define FLAG_o (FORCED_FLAG<<7)
-#define FLAG_k (FORCED_FLAG<<8)
-#define FLAG_O (FORCED_FLAG<<9)
-#define FLAG_K (FORCED_FLAG<<10)
-#define FLAG_a (FORCED_FLAG<<11)
-#define FLAG_A (FORCED_FLAG<<12)
+#define FLAG_q (1<<0)
+#define FLAG_b (1<<1)
+#define FLAG_n (1<<2)
+#define FLAG_m (1<<3)
+#define FLAG_d (1<<4)
+#define FLAG_s (1<<5)
+#define FLAG_u (1<<6)
+#define FLAG_p (1<<7)
+#define FLAG_o (1<<8)
+#define FLAG_k (1<<9)
+#define FLAG_H (1<<10)
+#define FLAG_O (1<<11)
+#define FLAG_K (1<<12)
+#define FLAG_a (1<<13)
+#define FLAG_A (1<<14)
#endif
#ifdef FOR_ip
@@ -5567,15 +5571,15 @@
#define FLAG_q (1<<0)
#define FLAG_b (1<<1)
#define FLAG_n (1<<2)
-#define FLAG_d (1<<3)
-#define FLAG_s (1<<4)
-#define FLAG_u (1<<5)
-#define FLAG_p (1<<6)
-#define FLAG_o (1<<7)
-#define FLAG_k (1<<8)
-#define FLAG_H (1<<9)
-#define FLAG_O (1<<10)
-#define FLAG_m (1<<11)
+#define FLAG_m (1<<3)
+#define FLAG_d (1<<4)
+#define FLAG_s (1<<5)
+#define FLAG_u (1<<6)
+#define FLAG_p (1<<7)
+#define FLAG_o (1<<8)
+#define FLAG_k (1<<9)
+#define FLAG_H (1<<10)
+#define FLAG_O (1<<11)
#endif
#ifdef FOR_touch
diff --git a/generated/globals.h b/generated/globals.h
old mode 100644
new mode 100755
index 18e101f..60969ef
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -1240,6 +1240,7 @@
} ps;
struct {
long n;
+ long m;
long d;
long s;
struct arg_list *u;
diff --git a/generated/help.h b/generated/help.h
old mode 100644
new mode 100755
index 811b534..bdafa16
--- a/generated/help.h
+++ b/generated/help.h
@@ -484,9 +484,9 @@
#define HELP_pgrep "usage: pgrep [-clfnovx] [-d DELIM] [-L SIGNAL] [PATTERN] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]\n\nSearch for process(es). PATTERN is an extended regular expression checked\nagainst command names.\n\n-c Show only count of matches\n-d Use DELIM instead of newline\n-L Send SIGNAL instead of printing name\n-l Show command name\n-f Check full command line for PATTERN\n-G Match real Group ID(s)\n-g Match Process Group(s) (0 is current user)\n-n Newest match only\n-o Oldest match only\n-P Match Parent Process ID(s)\n-s Match Session ID(s) (0 for current)\n-t Match Terminal(s)\n-U Match real User ID(s)\n-u Match effective User ID(s)\n-v Negate the match\n-x Match whole command (not substring)\n\n"
-#define HELP_iotop "usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]\n\nRank processes by I/O.\n\n-A All I/O, not just disk\n-a Accumulated I/O (not percentage)\n-K Kilobytes\n-k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)\n-O Only show processes doing I/O\n-o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)\n-s Sort by field number (0-X, default 6)\n-b Batch mode (no tty)\n-d Delay SECONDS between each cycle (default 3)\n-n Exit after NUMBER iterations\n-p Show these PIDs\n-u Show these USERs\n-q Quiet (no header lines)\n\nCursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force\nupdate, R to reverse sort, Q to exit.\n\n"
+#define HELP_iotop "usage: iotop [-AaKObq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]\n\nRank processes by I/O.\n\n-A All I/O, not just disk\n-a Accumulated I/O (not percentage)\n-H Show threads\n-K Kilobytes\n-k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)\n-m Maximum number of tasks to show\n-O Only show processes doing I/O\n-o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)\n-s Sort by field number (0-X, default 6)\n-b Batch mode (no tty)\n-d Delay SECONDS between each cycle (default 3)\n-n Exit after NUMBER iterations\n-p Show these PIDs\n-u Show these USERs\n-q Quiet (no header lines)\n\nCursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force\nupdate, R to reverse sort, Q to exit.\n\n"
-#define HELP_top "usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]\n\nShow process activity in real time.\n\n-H Show threads\n-k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)\n-o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)\n-O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)\n-s Sort by field number (1-X, default 9)\n-b Batch mode (no tty)\n-d Delay SECONDS between each cycle (default 3)\n-n Exit after NUMBER iterations\n-p Show these PIDs\n-u Show these USERs\n-q Quiet (no header lines)\n\nCursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force\nupdate, R to reverse sort, Q to exit.\n\n"
+#define HELP_top "usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,]\n\nShow process activity in real time.\n\n-H Show threads\n-k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)\n-o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)\n-O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)\n-s Sort by field number (1-X, default 9)\n-b Batch mode (no tty)\n-d Delay SECONDS between each cycle (default 3)\n-m Maximum number of tasks to show\n-n Exit after NUMBER iterations\n-p Show these PIDs\n-u Show these USERs\n-q Quiet (no header lines)\n\nCursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force\nupdate, R to reverse sort, Q to exit.\n\n"
#define HELP_ps "usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]\n\nList processes.\n\nWhich processes to show (selections may be comma separated lists):\n\n-A All processes\n-a Processes with terminals that aren't session leaders\n-d All processes that aren't session leaders\n-e Same as -A\n-g Belonging to GROUPs\n-G Belonging to real GROUPs (before sgid)\n-p PIDs (--pid)\n-P Parent PIDs (--ppid)\n-s In session IDs\n-t Attached to selected TTYs\n-T Show threads\n-u Owned by USERs\n-U Owned by real USERs (before suid)\n\nOutput modifiers:\n\n-k Sort FIELDs in +increasing or -decreasting order (--sort)\n-M Measure field widths (expanding as necessary)\n-n Show numeric USER and GROUP\n-w Wide output (don't truncate fields)\n\nWhich FIELDs to show. (Default = -o PID,TTY,TIME,CMD)\n\n-f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)\n-l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)\n-o Output FIELDs instead of defaults, each with optional :size and =title\n-O Add FIELDS to defaults\n-Z Include LABEL\n\nCommand line -o fields:\n\n ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])\n CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)\n COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)\n\nProcess attribute -o FIELDs:\n\n ADDR Instruction pointer BIT Is this process 32 or 64 bits\n CPU Which processor running on ETIME Elapsed time since PID start\n F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id\n GROUP Group name LABEL Security label\n MAJFL Major page faults MINFL Minor page faults\n NI Niceness (lower is faster)\n PCPU Percentage of CPU time used PCY Android scheduling policy\n PGID Process Group ID\n PID Process ID PPID Parent Process ID\n PRI Priority (higher is faster) PSR Processor last executed on\n RGID Real (before sgid) group ID RGROUP Real (before sgid) group name\n RSS Resident Set Size (pages in use) RTPRIO Realtime priority\n RUID Real (before suid) user ID RUSER Real (before suid) user name\n S Process state:\n R (running) S (sleeping) D (device I/O) T (stopped) t (traced)\n Z (zombie) X (deader) x (dead) K (wakekill) W (waking)\n SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)\n STAT Process state (S) plus:\n < high priority N low priority L locked memory\n s session leader + foreground l multithreaded\n STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)\n SZ Memory Size (4k pages needed to completely swap out process)\n TCNT Thread count TID Thread ID\n TIME CPU time consumed TTY Controlling terminal\n UID User id USER User name\n VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory\n WCHAN What are we waiting in kernel for\n\n"
diff --git a/generated/newtoys.h b/generated/newtoys.h
old mode 100644
new mode 100755
index 46cc2db..b47443a
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -242,7 +242,7 @@
USE_TFTPD(NEWTOY(tftpd, "rcu:l", TOYFLAG_BIN))
USE_TIME(NEWTOY(time, "<1^p", TOYFLAG_USR|TOYFLAG_BIN))
USE_TIMEOUT(NEWTOY(timeout, "<2^vk:s: ", TOYFLAG_BIN))
-USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_TOP(NEWTOY(top, ">0O*" "Hk*o*p*u*s#<1d#=3<1m#n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_TOUCH(NEWTOY(touch, "<1acd:mr:t:h[!dtr]", TOYFLAG_BIN))
USE_SH(OLDTOY(toysh, sh, TOYFLAG_BIN))
USE_TR(NEWTOY(tr, "^>2<1Ccsd[+cC]", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/lib/interestingtimes.c b/lib/interestingtimes.c
old mode 100644
new mode 100755
index f028f5e..073a67d
--- a/lib/interestingtimes.c
+++ b/lib/interestingtimes.c
@@ -5,13 +5,13 @@
#include "toys.h"
-int xgettty(void)
+int tty_fd(void)
{
int i, j;
for (i = 0; i<3; i++) if (isatty(j = (i+1)%3)) return j;
- return xopen("/dev/tty", O_RDWR);
+ return notstdio(open("/dev/tty", O_RDWR));
}
// Quick and dirty query size of terminal, doesn't do ANSI probe fallback.
diff --git a/lib/lib.c b/lib/lib.c
old mode 100644
new mode 100755
index d011af0..29f8163
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1264,3 +1264,11 @@
if (fd) fclose(fp);
}
+// Return unix time in milliseconds
+long long millitime(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec*1000+ts.tv_nsec/1000000;
+}
diff --git a/lib/lib.h b/lib/lib.h
old mode 100644
new mode 100755
index 0b93bde..ac027ce
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -239,6 +239,7 @@
char *getusername(uid_t uid);
char *getgroupname(gid_t gid);
void do_lines(int fd, void (*call)(char **pline, long len));
+long long millitime(void);
#define HR_SPACE 1 // Space between number and units
#define HR_B 2 // Use "B" for single byte units
@@ -269,7 +270,7 @@
int draw_trim(char *str, int padto, int width);
// interestingtimes.c
-int xgettty(void);
+int tty_fd(void);
int terminal_size(unsigned *xx, unsigned *yy);
int terminal_probesize(unsigned *xx, unsigned *yy);
int scan_key_getsize(char *scratch, int miliwait, unsigned *xx, unsigned *yy);
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
old mode 100644
new mode 100755
index a0dc53f..b8542e6
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -46,8 +46,10 @@
USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
// stayroot because iotop needs root to read other process' proc/$$/io
-USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
-USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
+// TOP and IOTOP have a large common option block used for common processing,
+// the default values are different but the flags are in the same order.
+USE_TOP(NEWTOY(top, ">0O*" "Hk*o*p*u*s#<1d#=3<1m#n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_IOTOP(NEWTOY(iotop, ">0AaKO" "Hk*o*p*u*s#<1=7d#=3<1m#n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
USE_PKILL(NEWTOY(pkill, "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -130,7 +132,7 @@
bool "top"
default y
help
- usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
+ usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,]
Show process activity in real time.
@@ -141,6 +143,7 @@
-s Sort by field number (1-X, default 9)
-b Batch mode (no tty)
-d Delay SECONDS between each cycle (default 3)
+ -m Maximum number of tasks to show
-n Exit after NUMBER iterations
-p Show these PIDs
-u Show these USERs
@@ -160,8 +163,10 @@
-A All I/O, not just disk
-a Accumulated I/O (not percentage)
+ -H Show threads
-K Kilobytes
-k Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
+ -m Maximum number of tasks to show
-O Only show processes doing I/O
-o Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
-s Sort by field number (0-X, default 6)
@@ -243,6 +248,7 @@
} ps;
struct {
long n;
+ long m;
long d;
long s;
struct arg_list *u;
@@ -280,15 +286,23 @@
void (*show_process)(void *tb);
)
-struct strawberry {
- struct strawberry *next, *prev;
+// Linked list of -o fields selected for display, in order, with :len and =title
+
+struct ofields {
+ struct ofields *next, *prev;
short which, len, reverse;
char *title;
- char forever[];
};
-/* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
- * table 1-4) but we shift and repurpose fields, with the result being: */
+/* The function get_ps() reads all the data about one process, saving it in
+ * toybox as a struct procpid. Simple ps calls then pass toybuf directly to
+ * show_ps(), but features like sorting append a copy to a linked list
+ * for further processing once all processes have been read.
+ *
+ * struct procpid contains a slot[] array of 64 bit values, with the following
+ * data at each position in the array. Most is read from /proc/$PID/stat (see
+ * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but
+ * we we replace several fields with don't use with other data. */
enum {
SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
@@ -297,7 +311,7 @@
SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
- SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
+ SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child utime
SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
@@ -313,7 +327,7 @@
SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
- SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
+ SLOT_upticks, /*uptime-starttime*/ SLOT_argv0len, // argv[0] length
SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
@@ -322,16 +336,44 @@
SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
SLOT_pcy, /*Android sched policy*/
- SLOT_count
+ SLOT_count /* Size of array */
};
+/* In addition to slot[], carevup contains 6 string fields to display
+ command name, tty device, selinux label... They're stored one after the
+ other in str[] (separated by null terminators), and offset[] contains the
+ starting position of each string after the first (which is always 0). */
+
// Data layout in toybuf
-struct carveup {
+struct procpid {
long long slot[SLOT_count]; // data (see enum above)
- unsigned short offset[6]; // offset of fields in str[] (skip name, always 0)
+ unsigned short offset[6]; // offset of fields in str[] (skip CMD, always 0)
char state;
- char str[]; // name, tty, command, wchan, attr, cmdline
+ char str[]; // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME
};
+
+/* The typos[] array lists all the types understood by "ps -o", I.E all the
+ * columns ps and top know how to display. Each entry has:
+ *
+ * name: the column name, displayed at top and used to select column with -o
+ *
+ * width: the display width. Fields are padded to this width when displaying
+ * to a terminal (negative means right justified). Strings are truncated
+ * to fit, numerical fields are padded but not truncated (although
+ * the display code reclaims unused padding from later fields to try to
+ * get the overflow back).
+ *
+ * slot: which slot[] out of procpid. Negative means it's a string field.
+ * Setting bit |64 requests extra display/sort processing.
+ *
+ * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the
+ * first string argument and the prefix is the first argument to TAGGED_ARRAY
+ * so in this case "NAME" becomes PS_NAME which is the offset into typos[]
+ * for that entry, and also _PS_NAME (the bit position, 1<<PS_NAME).
+ * We record active columns in TT.bits, ala:
+ *
+ * if (TT.bits & _PS_NAME) printf("-o included PS_NAME");
+ */
// TODO: Android uses -30 for LABEL, but ideally it would auto-size.
// 64|slot means compare as string when sorting
@@ -339,7 +381,7 @@
char *name;
signed char width, slot;
} static const typos[] = TAGGED_ARRAY(PS,
- // Numbers
+ // Numbers. (What's in slot[] is what's displayed, sorted numerically.)
{"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
{"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
{"SZ", 5, SLOT_vsize}, {"RSS", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
@@ -348,31 +390,31 @@
{"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
{"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
- // String fields
+ // String fields (-1 is procpid->str, rest are str+offset[1-slot])
{"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
{"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
{"ARGS", -27, -6}, {"CMD", -15, -1},
- // user/group
+ // user/group (may call getpwuid() or similar)
{"UID", 5, SLOT_uid}, {"USER", -12, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
{"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
{"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
- // clock displays
+ // clock displays (00:00:00)
{"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
{"TIME+", 9, SLOT_utime},
- // Percentage displays
+ // Percentage displays (fixed point, one decimal digit. 123 -> 12.3)
{"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
{"%CPU", 4, SLOT_utime2},
- // human_readable
+ // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc)
{"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
{"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
{"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
{"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
- // Misc
+ // Misc (special cases)
{"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
{"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
);
@@ -421,7 +463,7 @@
}
// Convert field to string representation
-static char *string_field(struct carveup *tb, struct strawberry *field)
+static char *string_field(struct procpid *tb, struct ofields *field)
{
char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
int which = field->which, sl = typos[which].slot;
@@ -550,8 +592,8 @@
// Display process data that get_ps() read from /proc, formatting with TT.fields
static void show_ps(void *p)
{
- struct carveup *tb = p;
- struct strawberry *field;
+ struct procpid *tb = p;
+ struct ofields *field;
int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
// Loop through fields to display
@@ -561,6 +603,7 @@
// Output the field, appropriately padded
// Minimum one space between each field
+ if (width<2) break;
if (field != TT.fields) {
putchar(' ');
width--;
@@ -610,7 +653,7 @@
}
// dirtree callback: read data about process to display, store, or discard it.
-// Fills toybuf with struct carveup and either DIRTREE_SAVEs a copy to ->extra
+// Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra
// (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
static int get_ps(struct dirtree *new)
{
@@ -618,12 +661,12 @@
char *name; // Path under /proc/$PID directory
long long bits; // Only fetch extra data if an -o field is displaying it
} fetch[] = {
- // sources for carveup->offset[] data
+ // sources for procpid->offset[] data
{"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
{"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
{"", _PS_NAME}
};
- struct carveup *tb = (void *)toybuf;
+ struct procpid *tb = (void *)toybuf;
long long *slot = tb->slot;
char *name, *s, *buf = tb->str, *end = 0;
int i, j, fd;
@@ -635,13 +678,16 @@
|(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
memset(slot, 0, sizeof(tb->slot));
- tb->slot[SLOT_tid] = *slot = atol(new->name);
- if (TT.threadparent && TT.threadparent->extra)
- if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
+ slot[SLOT_tid] = *slot = atol(new->name);
+ if (TT.threadparent && TT.threadparent->extra) {
+ *slot = *(((struct procpid *)TT.threadparent->extra)->slot);
+ // Parent also shows up as a thread, discard duplicate
+ if (*slot == slot[SLOT_tid]) return 0;
+ }
fd = dirtree_parentfd(new);
len = 2048;
- sprintf(buf, "%lld/stat", *slot);
+ sprintf(buf, "%lld/stat", slot[SLOT_tid]);
if (!readfileat(fd, buf, buf, &len)) return 0;
// parse oddball fields (name and state). Name can have embedded ')' so match
@@ -682,7 +728,7 @@
{
off_t temp = len;
- sprintf(buf, "%lld/status", *slot);
+ sprintf(buf, "%lld/status", slot[SLOT_tid]);
if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
s = strafter(buf, "\nUid:");
slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
@@ -696,7 +742,7 @@
if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
off_t temp = len;
- sprintf(buf, "%lld/io", *slot);
+ sprintf(buf, "%lld/io", slot[SLOT_tid]);
if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
@@ -719,7 +765,7 @@
if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
off_t temp = len;
- sprintf(buf, "%lld/statm", *slot);
+ sprintf(buf, "%lld/statm", slot[SLOT_tid]);
if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
for (s = buf, i=0; i<3; i++)
@@ -731,7 +777,7 @@
if (TT.bits&_PS_BIT) {
off_t temp = 6;
- sprintf(buf, "%lld/exe", *slot);
+ sprintf(buf, "%lld/exe", slot[SLOT_tid]);
if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
if (buf[4] == 1) slot[SLOT_bits] = 32;
else if (buf[4] == 2) slot[SLOT_bits] = 64;
@@ -739,7 +785,8 @@
}
// Do we need Android scheduling policy?
- if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
+ if (TT.bits&_PS_PCY)
+ get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]);
// Fetch string data while parentfd still available, appending to buf.
// (There's well over 3k of toybuf left. We could dynamically malloc, but
@@ -756,11 +803,11 @@
// Determine remaining space, reserving minimum of 256 bytes/field and
// 260 bytes scratch space at the end (for output conversion later).
len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
- sprintf(buf, "%lld/%s", *slot, fetch[j].name);
+ sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name);
// For exe we readlink instead of read contents
if (j==3 || j==5) {
- struct carveup *ptb = 0;
+ struct procpid *ptb = 0;
int k;
// Thread doesn't have exe or argv[0], so use parent's
@@ -771,7 +818,7 @@
else {
if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
else {
- if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
+ if (!ptb || slot[SLOT_argv0len]) ptb = tb;
i = ptb->slot[SLOT_argv0len];
s = ptb->str+ptb->offset[4];
while (-1!=(k = stridx(s, '/')) && k<i) {
@@ -795,7 +842,7 @@
if (rdev) {
// Can we readlink() our way to a name?
for (i = 0; i<3; i++) {
- sprintf(buf, "%lld/fd/%i", *slot, i);
+ sprintf(buf, "%lld/fd/%i", slot[SLOT_tid], i);
if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
&& st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
break;
@@ -881,7 +928,7 @@
static int get_threads(struct dirtree *new)
{
struct dirtree *dt;
- struct carveup *tb;
+ struct procpid *tb;
unsigned pid, kcount;
if (!new->parent) return get_ps(new);
@@ -915,22 +962,23 @@
// Save or display
if (!TT.show_process) return DIRTREE_SAVE;
TT.show_process((void *)new->extra);
- dt = new->child;
- new->child = 0;
- while (dt->child) {
- new = dt->child->next;
- TT.show_process((void *)dt->child->extra);
- free(dt->child);
- dt->child = new;
+ if ((dt = new->child)) {
+ new->child = 0;
+ while (dt->child) {
+ new = dt->child->next;
+ TT.show_process((void *)dt->child->extra);
+ free(dt->child);
+ dt->child = new;
+ }
+ free(dt);
}
- free(dt);
return 0;
}
static char *parse_ko(void *data, char *type, int length)
{
- struct strawberry *field;
+ struct ofields *field;
char *width, *title, *end, *s;
int i, j, k;
@@ -950,10 +998,11 @@
if (!title) length = width-type;
} else width = 0;
- // Allocate structure, copy title
- field = xzalloc(sizeof(struct strawberry)+(length+1)*!!title);
+ // Allocate structure plus extra space to append a copy of title data
+ // (this way it's same lifetime, freeing struct automatically frees title)
+ field = xzalloc(sizeof(struct ofields)+(length+1)*!!title);
if (title) {
- memcpy(field->title = field->forever, title, length);
+ memcpy(field->title = (char *)(field+1), title, length);
field->title[field->len = length] = 0;
}
@@ -991,15 +1040,15 @@
return 0;
}
-static long long get_headers(struct strawberry *fields, char *buf, int blen)
+static long long get_headers(struct ofields *field, char *buf, int blen)
{
long long bits = 0;
int len = 0;
- for (; fields; fields = fields->next) {
- len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
- fields->title);
- bits |= 1LL<<fields->which;
+ for (; field; field = field->next) {
+ len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len,
+ field->title);
+ bits |= 1LL<<field->which;
}
return bits;
@@ -1086,8 +1135,8 @@
// sort for -k
static int ksort(void *aa, void *bb)
{
- struct strawberry *field;
- struct carveup *ta = *(struct carveup **)aa, *tb = *(struct carveup **)bb;
+ struct ofields *field;
+ struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb;
int ret = 0, slot;
for (field = TT.kfields; field && !ret; field = field->next) {
@@ -1111,7 +1160,7 @@
return ret;
}
-static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt)
+static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
{
while (dt) {
struct dirtree *next = dt->next;
@@ -1125,9 +1174,9 @@
return tb;
}
-static struct carveup **collate(int count, struct dirtree *dt)
+static struct procpid **collate(int count, struct dirtree *dt)
{
- struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
+ struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *));
collate_leaves(tbsort, dt);
@@ -1143,33 +1192,6 @@
comma_args(arg ? arg : &def, fields, err, parse_ko);
}
-static void shared_main(void)
-{
- int i;
-
- TT.ticks = sysconf(_SC_CLK_TCK);
- if (!TT.width) {
- TT.width = 80;
- TT.height = 25;
- // If ps can't query terminal size pad to 80 but do -w
- if (toys.which->name[1] == 's') {
- if (!isatty(1) || !terminal_size(&TT.width, &TT.height))
- toys.optflags |= FLAG_w;
- }
- }
-
- // find controlling tty, falling back to /dev/tty if none
- for (i = 0; !TT.tty && i<4; i++) {
- struct stat st;
- int fd = i;
-
- if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
-
- if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
- if (i==3) close(fd);
- }
-}
-
void ps_main(void)
{
char **arg;
@@ -1177,7 +1199,18 @@
char *not_o;
int i;
- shared_main();
+ TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
+
+ if (-1 != (i = tty_fd())) {
+ struct stat st;
+
+ if (!fstat(i, &st)) TT.tty = st.st_rdev;
+ }
+
+ // If we can't query terminal size pad to 80 but do -w
+ TT.width = 80;
+ if (!isatty(1) || !terminal_size(&TT.width, 0))
+ toys.optflags |= FLAG_w;
if (toys.optflags&FLAG_w) TT.width = 99999;
// parse command line options other than -o
@@ -1215,20 +1248,20 @@
default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
if (TT.ps.O) {
- if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
+ if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->prev;
comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
- if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
+ if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->next;
}
dlist_terminate(TT.fields);
// -f and -n change the meaning of some fields
if (toys.optflags&(FLAG_f|FLAG_n)) {
- struct strawberry *ever;
+ struct ofields *field;
- for (ever = TT.fields; ever; ever = ever->next) {
- if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
- && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
- ever->which--;
+ for (field = TT.fields; field; field = field->next) {
+ if ((toys.optflags&FLAG_n) && field->which>=PS_UID
+ && field->which<=PS_RGROUP && (typos[field->which].slot&64))
+ field->which--;
}
}
@@ -1243,11 +1276,11 @@
? get_threads : get_ps);
if ((dt != DIRTREE_ABORTVAL) && toys.optflags&(FLAG_k|FLAG_M)) {
- struct carveup **tbsort = collate(TT.kcount, dt);
+ struct procpid **tbsort = collate(TT.kcount, dt);
if (toys.optflags&FLAG_M) {
for (i = 0; i<TT.kcount; i++) {
- struct strawberry *field;
+ struct ofields *field;
for (field = TT.fields; field; field = field->next) {
int len = strlen(string_field(tbsort[i], field));
@@ -1262,7 +1295,7 @@
}
if (toys.optflags&FLAG_k)
- qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
+ qsort(tbsort, TT.kcount, sizeof(struct procpid *), (void *)ksort);
for (i = 0; i<TT.kcount; i++) {
show_ps(tbsort[i]);
free(tbsort[i]);
@@ -1290,16 +1323,16 @@
// select which of the -o fields to sort by
static void setsort(int pos)
{
- struct strawberry *field, *going2;
+ struct ofields *field, *field2;
int i = 0;
if (pos<0) pos = 0;
for (field = TT.fields; field; field = field->next) {
if ((TT.sortpos = i++)<pos && field->next) continue;
- going2 = TT.kfields;
- going2->which = field->which;
- going2->len = field->len;
+ field2 = TT.kfields;
+ field2->which = field->which;
+ field2->len = field->len;
break;
}
}
@@ -1333,20 +1366,12 @@
return line-1;
}
-static long long millitime(void)
-{
- struct timespec ts;
-
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return ts.tv_sec*1000+ts.tv_nsec/1000000;
-}
-
static void top_common(
int (*filter)(long long *oslot, long long *nslot, int milis))
{
long long timeout = 0, now, stats[16];
struct proclist {
- struct carveup **tb;
+ struct procpid **tb;
int count;
long long whence;
} plist[2], *plold, *plnew, old, new, mix;
@@ -1392,11 +1417,12 @@
// Collate old and new into "mix", depends on /proc read in pid sort order
old = *plold;
new = *plnew;
- mix.tb = xmalloc((old.count+new.count)*sizeof(struct carveup));
+ mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid));
mix.count = 0;
while (old.count || new.count) {
- struct carveup *otb = *old.tb, *ntb = *new.tb;
+ struct procpid *otb = old.count ? *old.tb : 0,
+ *ntb = new.count ? *new.tb : 0;
// If we just have old for this process, it exited. Discard it.
if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
@@ -1426,7 +1452,7 @@
char was, is;
if (recalc) {
- qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
+ qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort);
if (!(toys.optflags&FLAG_b)) {
printf("\033[H\033[J");
if (toys.signal) {
@@ -1434,21 +1460,22 @@
terminal_probesize(&TT.width, &TT.height);
}
}
+ if (TT.top.m) TT.height = TT.top.m+5;
lines = TT.height;
}
if (recalc && !(toys.optflags&FLAG_q)) {
// Display "top" header.
if (*toys.which->name == 't') {
- struct strawberry alluc;
+ struct ofields field;
long long ll, up = 0;
long run[6];
int j;
// Count running, sleeping, stopped, zombie processes.
- alluc.which = PS_S;
+ field.which = PS_S;
memset(run, 0, sizeof(run));
for (i = 0; i<mix.count; i++)
- run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
+ run[1+stridx("RSTZ", *string_field(mix.tb[i], &field))]++;
sprintf(toybuf,
"Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
"%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
@@ -1490,24 +1517,24 @@
}
lines = header_line(lines, 0);
} else {
- struct strawberry *fields;
- struct carveup tb;
+ struct ofields *field;
+ struct procpid tb;
- memset(&tb, 0, sizeof(struct carveup));
+ memset(&tb, 0, sizeof(struct procpid));
pos = stpcpy(toybuf, "Totals:");
- for (fields = TT.fields; fields; fields = fields->next) {
+ for (field = TT.fields; field; field = field->next) {
long long ll, bits = 0;
- int slot = typos[fields->which].slot&63;
+ int slot = typos[field->which].slot&63;
- if (fields->which<PS_C || fields->which>PS_DIO) continue;
- ll = 1LL<<fields->which;
+ if (field->which<PS_C || field->which>PS_DIO) continue;
+ ll = 1LL<<field->which;
if (bits&ll) continue;
bits |= ll;
for (i=0; i<mix.count; i++)
tb.slot[slot] += mix.tb[i]->slot[slot];
pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
- " %s: %*s,", typos[fields->which].name,
- fields->len, string_field(&tb, fields));
+ " %s: %*s,", typos[field->which].name,
+ field->len, string_field(&tb, field));
}
*--pos = 0;
lines = header_line(lines, 0);
@@ -1564,7 +1591,7 @@
timeout = 0;
break;
} else if (toupper(i)=='R')
- ((struct strawberry *)TT.kfields)->reverse *= -1;
+ ((struct ofields *)TT.kfields)->reverse *= -1;
else {
i -= 256;
if (i == KEY_LEFT) setsort(TT.sortpos-1);
@@ -1595,15 +1622,23 @@
static void top_setup(char *defo, char *defk)
{
TT.top.d *= 1000;
+
+ TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
+ TT.tty = tty_fd() != -1;
+
+ // Are we doing "batch" output or interactive?
if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
else {
+ // Grab starting time, make terminal raw, switch off cursor,
+ // set signal handler to put terminal/cursor back to normal at exit.
TT.time = millitime();
set_terminal(0, 1, 0);
sigatexit(tty_sigreset);
xsignal(SIGWINCH, generic_signal);
printf("\033[?25l\033[0m");
+ TT.width = 80;
+ TT.height = 25;
}
- shared_main();
comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
@@ -1627,10 +1662,10 @@
if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
top_setup(toybuf, "-%CPU,-ETIME,-PID");
if (TT.top.O) {
- struct strawberry *fields = TT.fields;
+ struct ofields *field = TT.fields;
- fields = fields->next->next;
- comma_args(TT.top.O, &fields, "bad -O", parse_ko);
+ field = field->next->next;
+ comma_args(TT.top.O, &field, "bad -O", parse_ko);
}
top_common(merge_deltas);
@@ -1674,7 +1709,7 @@
regex_t reg;
};
-static void do_pgk(struct carveup *tb)
+static void do_pgk(struct procpid *tb)
{
if (TT.pgrep.signal) {
if (kill(*tb->slot, TT.pgrep.signal)) {
@@ -1695,7 +1730,7 @@
static void match_pgrep(void *p)
{
- struct carveup *tb = p;
+ struct procpid *tb = p;
regmatch_t match;
struct regex_list *reg;
char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
@@ -1743,7 +1778,7 @@
TT.pgrep.self = getpid();
- // No signal names start with "L", so no need for "L: " parsing.
+ // No signal names start with "L", so no need for "L: " in optstr.
if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
error_exit("bad -L '%s'", TT.pgrep.L);
网友评论