blob: 1b8c18516fd880440f6f9479bf76f2c95d98da03 [file] [log] [blame]
Bernhard Reutner-Fischerd9cf7ac2006-04-12 18:39:58 +00001/* vi: set sw=4 ts=4: */
Eric Andersen420b2082002-09-17 22:14:58 +00002/*
3 * A tiny 'top' utility.
4 *
Eric Andersen08a72202002-09-30 20:52:10 +00005 * This is written specifically for the linux /proc/<PID>/stat(m)
6 * files format.
7
8 * This reads the PIDs of all processes and their status and shows
9 * the status of processes (first ones that fit to screen) at given
10 * intervals.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000011 *
Eric Andersen420b2082002-09-17 22:14:58 +000012 * NOTES:
13 * - At startup this changes to /proc, all the reads are then
14 * relative to that.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000015 *
Eric Andersen420b2082002-09-17 22:14:58 +000016 * (C) Eero Tamminen <oak at welho dot com>
Eric Andersen08a72202002-09-30 20:52:10 +000017 *
Eric Andersenaff114c2004-04-14 17:51:38 +000018 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
Eric Andersen420b2082002-09-17 22:14:58 +000019 */
Eric Andersen08a72202002-09-30 20:52:10 +000020
21/* Original code Copyrights */
22/*
23 * Copyright (c) 1992 Branko Lankester
24 * Copyright (c) 1992 Roger Binns
25 * Copyright (C) 1994-1996 Charles L. Blake.
26 * Copyright (C) 1992-1998 Michael K. Johnson
27 * May be distributed under the conditions of the
28 * GNU Library General Public License
29 */
30
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000031#include "libbb.h"
Eric Andersen420b2082002-09-17 22:14:58 +000032
Eric Andersen08a72202002-09-30 20:52:10 +000033
Denis Vlasenko85818632007-04-19 14:47:11 +000034typedef struct top_status_t {
Mike Frysinger0aa6ba52007-02-08 08:21:58 +000035 unsigned long vsz;
Denis Vlasenko459e4d62006-11-05 00:43:51 +000036#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
37 unsigned long ticks;
38 unsigned pcpu; /* delta of ticks */
39#endif
40 unsigned pid, ppid;
41 unsigned uid;
42 char state[4];
Denis Vlasenko98ebab82007-06-30 14:47:41 +000043 char comm[COMM_LEN];
Denis Vlasenko459e4d62006-11-05 00:43:51 +000044} top_status_t;
Denis Vlasenko85818632007-04-19 14:47:11 +000045
Denis Vlasenko98ebab82007-06-30 14:47:41 +000046typedef struct jiffy_counts_t {
Denis Vlasenko85818632007-04-19 14:47:11 +000047 unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;
48 unsigned long long total;
49 unsigned long long busy;
50} jiffy_counts_t;
51
Denis Vlasenko459e4d62006-11-05 00:43:51 +000052/* This structure stores some critical information from one frame to
53 the next. Used for finding deltas. */
Denis Vlasenko85818632007-04-19 14:47:11 +000054typedef struct save_hist {
Denis Vlasenko459e4d62006-11-05 00:43:51 +000055 unsigned long ticks;
56 unsigned pid;
Denis Vlasenko85818632007-04-19 14:47:11 +000057} save_hist;
58
59typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
60
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +000061
Denis Vlasenko85818632007-04-19 14:47:11 +000062enum { SORT_DEPTH = 3 };
63
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +000064
Denis Vlasenko85818632007-04-19 14:47:11 +000065struct globals {
66 top_status_t *top;
67 int ntop;
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +000068#if ENABLE_FEATURE_TOPMEM
69 smallint sort_field;
70 smallint inverted;
71#endif
Denis Vlasenko85818632007-04-19 14:47:11 +000072#if ENABLE_FEATURE_USE_TERMIOS
73 struct termios initial_settings;
74#endif
75#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +000076 cmp_funcp sort_function[1];
Denis Vlasenko85818632007-04-19 14:47:11 +000077#else
78 cmp_funcp sort_function[SORT_DEPTH];
79 struct save_hist *prev_hist;
80 int prev_hist_count;
81 jiffy_counts_t jif, prev_jif;
82 /* int hist_iterations; */
83 unsigned total_pcpu;
84 /* unsigned long total_vsz; */
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +000085 char line_buf[80];
Denis Vlasenko85818632007-04-19 14:47:11 +000086#endif
Denis Vlasenko459e4d62006-11-05 00:43:51 +000087};
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +000088enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
Denis Vlasenko85818632007-04-19 14:47:11 +000089#define G (*(struct globals*)&bb_common_bufsiz1)
Denis Vlasenkoe7c1ad12007-09-08 17:21:01 +000090#define INIT_G() \
91 do { \
92 struct G_sizecheck { \
93 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
94 }; \
95 } while (0)
Denis Vlasenko85818632007-04-19 14:47:11 +000096#define top (G.top )
97#define ntop (G.ntop )
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +000098#define sort_field (G.sort_field )
99#define inverted (G.inverted )
100#define initial_settings (G.initial_settings )
Denis Vlasenko85818632007-04-19 14:47:11 +0000101#define sort_function (G.sort_function )
Denis Vlasenko85818632007-04-19 14:47:11 +0000102#define prev_hist (G.prev_hist )
103#define prev_hist_count (G.prev_hist_count )
104#define jif (G.jif )
105#define prev_jif (G.prev_jif )
106#define total_pcpu (G.total_pcpu )
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000107#define line_buf (G.line_buf )
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000108
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000109
Denis Vlasenkoc12f5302006-10-06 09:49:47 +0000110#define OPT_BATCH_MODE (option_mask32 & 0x4)
Eric Andersen08a72202002-09-30 20:52:10 +0000111
Denis Vlasenko85818632007-04-19 14:47:11 +0000112
Denis Vlasenkofa076802006-11-05 00:38:51 +0000113#if ENABLE_FEATURE_USE_TERMIOS
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000114static int pid_sort(top_status_t *P, top_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +0000115{
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000116 /* Buggy wrt pids with high bit set */
117 /* (linux pids are in [1..2^15-1]) */
Rob Landleyb2804552006-02-13 22:04:27 +0000118 return (Q->pid - P->pid);
Eric Andersen08a72202002-09-30 20:52:10 +0000119}
Mike Frysinger223b8872005-07-30 09:42:05 +0000120#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000121
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000122static int mem_sort(top_status_t *P, top_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +0000123{
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000124 /* We want to avoid unsigned->signed and truncation errors */
Mike Frysinger0aa6ba52007-02-08 08:21:58 +0000125 if (Q->vsz < P->vsz) return -1;
126 return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
Eric Andersen08a72202002-09-30 20:52:10 +0000127}
128
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000129
Denis Vlasenko85818632007-04-19 14:47:11 +0000130#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000131
132static int pcpu_sort(top_status_t *P, top_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +0000133{
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000134 /* Buggy wrt ticks with high bit set */
135 /* Affects only processes for which ticks overflow */
136 return (int)Q->pcpu - (int)P->pcpu;
Eric Andersen08a72202002-09-30 20:52:10 +0000137}
138
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000139static int time_sort(top_status_t *P, top_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +0000140{
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000141 /* We want to avoid unsigned->signed and truncation errors */
142 if (Q->ticks < P->ticks) return -1;
143 return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
Eric Andersen08a72202002-09-30 20:52:10 +0000144}
145
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000146static int mult_lvl_cmp(void* a, void* b)
147{
Rob Landleyb2804552006-02-13 22:04:27 +0000148 int i, cmp_val;
Eric Andersen08a72202002-09-30 20:52:10 +0000149
Denis Vlasenkofa076802006-11-05 00:38:51 +0000150 for (i = 0; i < SORT_DEPTH; i++) {
Rob Landleyb2804552006-02-13 22:04:27 +0000151 cmp_val = (*sort_function[i])(a, b);
152 if (cmp_val != 0)
153 return cmp_val;
154 }
155 return 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000156}
157
Eric Andersen08a72202002-09-30 20:52:10 +0000158
Rob Landley997650b2006-04-24 23:13:46 +0000159static void get_jiffy_counts(void)
Rob Landleyb2804552006-02-13 22:04:27 +0000160{
Rob Landleyd921b2e2006-08-03 15:41:12 +0000161 FILE* fp = xfopen("stat", "r");
Rob Landley997650b2006-04-24 23:13:46 +0000162 prev_jif = jif;
163 if (fscanf(fp, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
164 &jif.usr,&jif.nic,&jif.sys,&jif.idle,
165 &jif.iowait,&jif.irq,&jif.softirq,&jif.steal) < 4) {
Denis Vlasenko42dfcd22006-09-09 12:55:02 +0000166 bb_error_msg_and_die("failed to read /proc/stat");
Rob Landleyb2804552006-02-13 22:04:27 +0000167 }
Rob Landley997650b2006-04-24 23:13:46 +0000168 fclose(fp);
169 jif.total = jif.usr + jif.nic + jif.sys + jif.idle
170 + jif.iowait + jif.irq + jif.softirq + jif.steal;
171 /* procps 2.x does not count iowait as busy time */
172 jif.busy = jif.total - jif.idle - jif.iowait;
Eric Andersen08a72202002-09-30 20:52:10 +0000173}
174
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000175
Eric Andersen08a72202002-09-30 20:52:10 +0000176static void do_stats(void)
177{
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000178 top_status_t *cur;
Denis Vlasenko35fb5122006-11-01 09:16:49 +0000179 pid_t pid;
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000180 int i, last_i, n;
Rob Landley997650b2006-04-24 23:13:46 +0000181 struct save_hist *new_hist;
Eric Andersen08a72202002-09-30 20:52:10 +0000182
Rob Landley997650b2006-04-24 23:13:46 +0000183 get_jiffy_counts();
184 total_pcpu = 0;
Mike Frysinger0aa6ba52007-02-08 08:21:58 +0000185 /* total_vsz = 0; */
Rob Landley997650b2006-04-24 23:13:46 +0000186 new_hist = xmalloc(sizeof(struct save_hist)*ntop);
Rob Landleyb2804552006-02-13 22:04:27 +0000187 /*
188 * Make a pass through the data to get stats.
189 */
Rob Landley997650b2006-04-24 23:13:46 +0000190 /* hist_iterations = 0; */
191 i = 0;
192 for (n = 0; n < ntop; n++) {
Rob Landleyb2804552006-02-13 22:04:27 +0000193 cur = top + n;
194
195 /*
196 * Calculate time in cur process. Time is sum of user time
Rob Landley997650b2006-04-24 23:13:46 +0000197 * and system time
Rob Landleyb2804552006-02-13 22:04:27 +0000198 */
Rob Landleyb2804552006-02-13 22:04:27 +0000199 pid = cur->pid;
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000200 new_hist[n].ticks = cur->ticks;
Rob Landley997650b2006-04-24 23:13:46 +0000201 new_hist[n].pid = pid;
Rob Landleyb2804552006-02-13 22:04:27 +0000202
203 /* find matching entry from previous pass */
Rob Landley997650b2006-04-24 23:13:46 +0000204 cur->pcpu = 0;
205 /* do not start at index 0, continue at last used one
206 * (brought hist_iterations from ~14000 down to 172) */
207 last_i = i;
208 if (prev_hist_count) do {
209 if (prev_hist[i].pid == pid) {
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000210 cur->pcpu = cur->ticks - prev_hist[i].ticks;
211 total_pcpu += cur->pcpu;
Rob Landleyb2804552006-02-13 22:04:27 +0000212 break;
213 }
Rob Landley997650b2006-04-24 23:13:46 +0000214 i = (i+1) % prev_hist_count;
215 /* hist_iterations++; */
216 } while (i != last_i);
Mike Frysinger0aa6ba52007-02-08 08:21:58 +0000217 /* total_vsz += cur->vsz; */
Eric Andersen08a72202002-09-30 20:52:10 +0000218 }
219
220 /*
Rob Landleyb2804552006-02-13 22:04:27 +0000221 * Save cur frame's information.
Eric Andersen08a72202002-09-30 20:52:10 +0000222 */
Rob Landley997650b2006-04-24 23:13:46 +0000223 free(prev_hist);
224 prev_hist = new_hist;
225 prev_hist_count = ntop;
Eric Andersen08a72202002-09-30 20:52:10 +0000226}
Denis Vlasenkofa076802006-11-05 00:38:51 +0000227#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
Eric Andersen08a72202002-09-30 20:52:10 +0000228
Denis Vlasenko6ee023c2007-08-23 10:52:52 +0000229#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
230/* formats 7 char string (8 with terminating NUL) */
231static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
232{
233 unsigned t;
234 if (value >= total) { /* 100% ? */
235 strcpy(pbuf, " 100% ");
236 return pbuf;
237 }
238 /* else generate " [N/space]N.N% " string */
239 value = 1000 * value / total;
240 t = value / 100;
241 value = value % 100;
242 pbuf[0] = ' ';
243 pbuf[1] = t ? t + '0' : ' ';
244 pbuf[2] = '0' + (value / 10);
245 pbuf[3] = '.';
246 pbuf[4] = '0' + (value % 10);
247 pbuf[5] = '%';
248 pbuf[6] = ' ';
249 pbuf[7] = '\0';
250 return pbuf;
251}
252#endif
253
Denis Vlasenko05241802007-08-29 18:34:26 +0000254static unsigned long display_header(int scr_width)
Eric Andersen420b2082002-09-17 22:14:58 +0000255{
256 FILE *fp;
257 char buf[80];
Rob Landley997650b2006-04-24 23:13:46 +0000258 char scrbuf[80];
Eric Andersen420b2082002-09-17 22:14:58 +0000259 unsigned long total, used, mfree, shared, buffers, cached;
Denis Vlasenko6ee023c2007-08-23 10:52:52 +0000260#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
Denis Vlasenko110967a2007-07-15 19:27:48 +0000261 unsigned total_diff;
Denis Vlasenko856be772007-08-17 08:29:48 +0000262#endif
Denis Vlasenko110967a2007-07-15 19:27:48 +0000263
Eric Andersen420b2082002-09-17 22:14:58 +0000264 /* read memory info */
Rob Landleyd921b2e2006-08-03 15:41:12 +0000265 fp = xfopen("meminfo", "r");
Eric Andersen420b2082002-09-17 22:14:58 +0000266
Eric Andersen7857c032003-10-11 18:47:20 +0000267 /*
268 * Old kernels (such as 2.4.x) had a nice summary of memory info that
269 * we could parse, however this is gone entirely in 2.6. Try parsing
270 * the old way first, and if that fails, parse each field manually.
271 *
272 * First, we read in the first line. Old kernels will have bogus
273 * strings we don't care about, whereas new kernels will start right
274 * out with MemTotal:
"Vladimir N. Oleynik"70678bc2005-11-29 12:32:33 +0000275 * -- PFM.
Eric Andersen7857c032003-10-11 18:47:20 +0000276 */
277 if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
"Vladimir N. Oleynik"70678bc2005-11-29 12:32:33 +0000278 fgets(buf, sizeof(buf), fp); /* skip first line */
Eric Andersen7857c032003-10-11 18:47:20 +0000279
280 fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
Denis Vlasenko5a654472007-06-10 17:11:59 +0000281 &total, &used, &mfree, &shared, &buffers, &cached);
282 /* convert to kilobytes */
283 used /= 1024;
284 mfree /= 1024;
285 shared /= 1024;
286 buffers /= 1024;
287 cached /= 1024;
288 total /= 1024;
Eric Andersen7857c032003-10-11 18:47:20 +0000289 } else {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000290 /*
Eric Andersen7857c032003-10-11 18:47:20 +0000291 * Revert to manual parsing, which incidentally already has the
292 * sizes in kilobytes. This should be safe for both 2.4 and
293 * 2.6.
294 */
Eric Andersen7857c032003-10-11 18:47:20 +0000295
296 fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
297
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000298 /*
Eric Andersen7857c032003-10-11 18:47:20 +0000299 * MemShared: is no longer present in 2.6. Report this as 0,
300 * to maintain consistent behavior with normal procps.
301 */
302 if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
303 shared = 0;
304
305 fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
306 fscanf(fp, "Cached: %lu %s\n", &cached, buf);
307
308 used = total - mfree;
Eric Andersen420b2082002-09-17 22:14:58 +0000309 }
310 fclose(fp);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000311
Denis Vlasenko110967a2007-07-15 19:27:48 +0000312 /* output memory info */
Rob Landley997650b2006-04-24 23:13:46 +0000313 if (scr_width > sizeof(scrbuf))
314 scr_width = sizeof(scrbuf);
315 snprintf(scrbuf, scr_width,
Denis Vlasenkoc1166c32007-07-15 19:23:38 +0000316 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
Rob Landleyb2804552006-02-13 22:04:27 +0000317 used, mfree, shared, buffers, cached);
Denis Vlasenko110967a2007-07-15 19:27:48 +0000318 /* clear screen & go to top */
Denis Vlasenko266bc172006-09-29 17:16:39 +0000319 printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000320
Denis Vlasenko856be772007-08-17 08:29:48 +0000321#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
322 /*
323 * xxx% = (jif.xxx - prev_jif.xxx) / (jif.total - prev_jif.total) * 100%
324 */
325 /* using (unsigned) casts to make operations cheaper */
326 total_diff = ((unsigned)(jif.total - prev_jif.total) ? : 1);
Denis Vlasenko74511962007-06-11 16:31:55 +0000327#if ENABLE_FEATURE_TOP_DECIMALS
Denis Vlasenko110967a2007-07-15 19:27:48 +0000328/* Generated code is approx +0.3k */
Denis Vlasenko24c5fba2007-07-15 19:25:01 +0000329#define CALC_STAT(xxx) char xxx[8]
Denis Vlasenko6ee023c2007-08-23 10:52:52 +0000330#define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(jif.xxx - prev_jif.xxx), total_diff)
Denis Vlasenko24c5fba2007-07-15 19:25:01 +0000331#define FMT "%s"
Denis Vlasenko74511962007-06-11 16:31:55 +0000332#else
Denis Vlasenkob1e5add2007-06-10 18:04:54 +0000333#define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(jif.xxx - prev_jif.xxx) / total_diff
334#define SHOW_STAT(xxx) xxx
Denis Vlasenko24c5fba2007-07-15 19:25:01 +0000335#define FMT "%4u%% "
Denis Vlasenko74511962007-06-11 16:31:55 +0000336#endif
Denis Vlasenko856be772007-08-17 08:29:48 +0000337 { /* need block: CALC_STAT are declarations */
338 CALC_STAT(usr);
339 CALC_STAT(sys);
340 CALC_STAT(nic);
341 CALC_STAT(idle);
342 CALC_STAT(iowait);
343 CALC_STAT(irq);
344 CALC_STAT(softirq);
345 //CALC_STAT(steal);
Denis Vlasenko5a654472007-06-10 17:11:59 +0000346
Denis Vlasenko856be772007-08-17 08:29:48 +0000347 snprintf(scrbuf, scr_width,
348 /* Barely fits in 79 chars when in "decimals" mode. */
349 "CPU:"FMT"usr"FMT"sys"FMT"nice"FMT"idle"FMT"io"FMT"irq"FMT"softirq",
350 SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
351 SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
352 //, SHOW_STAT(steal) - what is this 'steal' thing?
353 // I doubt anyone wants to know it
354 );
355 }
356 puts(scrbuf);
Denis Vlasenko5a654472007-06-10 17:11:59 +0000357#undef SHOW_STAT
358#undef CALC_STAT
Denis Vlasenkob1e5add2007-06-10 18:04:54 +0000359#undef FMT
Denis Vlasenko856be772007-08-17 08:29:48 +0000360#endif
Denis Vlasenko5a654472007-06-10 17:11:59 +0000361
Denis Vlasenko110967a2007-07-15 19:27:48 +0000362 /* read load average as a string */
363 buf[0] = '\0';
364 open_read_close("loadavg", buf, sizeof("N.NN N.NN N.NN")-1);
365 buf[sizeof("N.NN N.NN N.NN")-1] = '\0';
Denis Vlasenko25d80622006-10-27 09:34:22 +0000366 snprintf(scrbuf, scr_width, "Load average: %s", buf);
Denis Vlasenko5a654472007-06-10 17:11:59 +0000367 puts(scrbuf);
Rob Landley997650b2006-04-24 23:13:46 +0000368
Eric Andersen7857c032003-10-11 18:47:20 +0000369 return total;
Eric Andersen420b2082002-09-17 22:14:58 +0000370}
371
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000372static NOINLINE void display_process_list(int count, int scr_width)
Eric Andersen420b2082002-09-17 22:14:58 +0000373{
Rob Landley997650b2006-04-24 23:13:46 +0000374 enum {
Denis Vlasenko74511962007-06-11 16:31:55 +0000375 BITS_PER_INT = sizeof(int)*8
Rob Landley997650b2006-04-24 23:13:46 +0000376 };
377
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000378 top_status_t *s = top;
Mike Frysinger0aa6ba52007-02-08 08:21:58 +0000379 char vsz_str_buf[8];
Denis Vlasenko05241802007-08-29 18:34:26 +0000380 unsigned long total_memory = display_header(scr_width); /* or use total_vsz? */
Denis Vlasenko74511962007-06-11 16:31:55 +0000381 /* xxx_shift and xxx_scale variables allow us to replace
382 * expensive divides with multiply and shift */
383 unsigned pmem_shift, pmem_scale, pmem_half;
Denis Vlasenkofa076802006-11-05 00:38:51 +0000384#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Denis Vlasenko74511962007-06-11 16:31:55 +0000385 unsigned pcpu_shift, pcpu_scale, pcpu_half;
Denis Vlasenkofa076802006-11-05 00:38:51 +0000386 unsigned busy_jifs;
Rob Landley997650b2006-04-24 23:13:46 +0000387
Eric Andersen420b2082002-09-17 22:14:58 +0000388 /* what info of the processes is shown */
Denis Vlasenko266bc172006-09-29 17:16:39 +0000389 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
Denis Vlasenko74511962007-06-11 16:31:55 +0000390 " PID PPID USER STAT VSZ %MEM %CPU COMMAND");
Eric Andersen08a72202002-09-30 20:52:10 +0000391#else
Denis Vlasenko74511962007-06-11 16:31:55 +0000392
393 /* !CPU_USAGE_PERCENTAGE */
Denis Vlasenko266bc172006-09-29 17:16:39 +0000394 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
Denis Vlasenko74511962007-06-11 16:31:55 +0000395 " PID PPID USER STAT VSZ %MEM COMMAND");
Rob Landley997650b2006-04-24 23:13:46 +0000396#endif
397
Denis Vlasenko24c5fba2007-07-15 19:25:01 +0000398#if ENABLE_FEATURE_TOP_DECIMALS
399#define UPSCALE 1000
400#define CALC_STAT(name, val) div_t name = div((val), 10)
401#define SHOW_STAT(name) name.quot, '0'+name.rem
402#define FMT "%3u.%c"
403#else
404#define UPSCALE 100
405#define CALC_STAT(name, val) unsigned name = (val)
406#define SHOW_STAT(name) name
407#define FMT "%4u%%"
408#endif
Rob Landley997650b2006-04-24 23:13:46 +0000409 /*
Mike Frysinger0aa6ba52007-02-08 08:21:58 +0000410 * MEM% = s->vsz/MemTotal
Rob Landley997650b2006-04-24 23:13:46 +0000411 */
Denis Vlasenko74511962007-06-11 16:31:55 +0000412 pmem_shift = BITS_PER_INT-11;
413 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
Mike Frysinger0aa6ba52007-02-08 08:21:58 +0000414 /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
Rob Landley997650b2006-04-24 23:13:46 +0000415 while (pmem_scale >= 512) {
416 pmem_scale /= 4;
417 pmem_shift -= 2;
418 }
Denis Vlasenko74511962007-06-11 16:31:55 +0000419 pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
Denis Vlasenkofa076802006-11-05 00:38:51 +0000420#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
421 busy_jifs = jif.busy - prev_jif.busy;
422 /* This happens if there were lots of short-lived processes
423 * between two top updates (e.g. compilation) */
424 if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
425
Rob Landley997650b2006-04-24 23:13:46 +0000426 /*
427 * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
428 * (pcpu is delta of sys+user time between samples)
429 */
430 /* (jif.xxx - prev_jif.xxx) and s->pcpu are
431 * in 0..~64000 range (HZ*update_interval).
432 * we assume that unsigned is at least 32-bit.
433 */
434 pcpu_shift = 6;
Denis Vlasenko74511962007-06-11 16:31:55 +0000435 pcpu_scale = (UPSCALE*64*(uint16_t)busy_jifs ? : 1);
436 while (pcpu_scale < (1U<<(BITS_PER_INT-2))) {
Rob Landley997650b2006-04-24 23:13:46 +0000437 pcpu_scale *= 4;
438 pcpu_shift += 2;
439 }
440 pcpu_scale /= ( (uint16_t)(jif.total-prev_jif.total)*total_pcpu ? : 1);
441 /* we want (s->pcpu * pcpu_scale) to never overflow */
442 while (pcpu_scale >= 1024) {
443 pcpu_scale /= 4;
444 pcpu_shift -= 2;
445 }
Denis Vlasenko74511962007-06-11 16:31:55 +0000446 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
Rob Landley997650b2006-04-24 23:13:46 +0000447 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
Eric Andersen08a72202002-09-30 20:52:10 +0000448#endif
Denis Vlasenko74511962007-06-11 16:31:55 +0000449
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000450 scr_width += 2; /* account for leading '\n' and trailing NUL */
451 /* Ok, all preliminary data is ready, go thru the list */
Denis Vlasenko266bc172006-09-29 17:16:39 +0000452 while (count-- > 0) {
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000453 unsigned col;
Denis Vlasenko74511962007-06-11 16:31:55 +0000454 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
455#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
456 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
457#endif
Eric Andersen420b2082002-09-17 22:14:58 +0000458
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000459 if (s->vsz >= 100000)
Denis Vlasenkob308d812007-08-28 19:35:34 +0000460 sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
Manuel Novoa III d4993302002-09-18 19:27:10 +0000461 else
Mike Frysinger0aa6ba52007-02-08 08:21:58 +0000462 sprintf(vsz_str_buf, "%7ld", s->vsz);
Denis Vlasenko74511962007-06-11 16:31:55 +0000463 // PID PPID USER STAT VSZ %MEM [%CPU] COMMAND
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000464 col = snprintf(line_buf, scr_width,
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000465 "\n" "%5u%6u %-8.8s %s%s" FMT
Denis Vlasenko74511962007-06-11 16:31:55 +0000466#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
467 FMT
468#endif
469 " ",
470 s->pid, s->ppid, get_cached_username(s->uid),
471 s->state, vsz_str_buf,
472 SHOW_STAT(pmem)
473#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
474 , SHOW_STAT(pcpu)
475#endif
476 );
Denis Vlasenko4d7605a2007-09-08 17:42:00 +0000477 if (col + 1 < scr_width)
478 read_cmdline(line_buf + col, scr_width - col - 1, s->pid, s->comm);
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000479 fputs(line_buf, stdout);
Bernhard Reutner-Fischere2922e42006-05-19 12:48:56 +0000480 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
Rob Landley997650b2006-04-24 23:13:46 +0000481 jif.busy - prev_jif.busy, jif.total - prev_jif.total); */
Eric Andersen420b2082002-09-17 22:14:58 +0000482 s++;
Eric Andersen08a72202002-09-30 20:52:10 +0000483 }
Rob Landley997650b2006-04-24 23:13:46 +0000484 /* printf(" %d", hist_iterations); */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000485 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
Rob Landley997650b2006-04-24 23:13:46 +0000486 fflush(stdout);
Eric Andersen44608e92002-10-22 12:21:15 +0000487}
Denis Vlasenko24c5fba2007-07-15 19:25:01 +0000488#undef UPSCALE
Denis Vlasenko74511962007-06-11 16:31:55 +0000489#undef SHOW_STAT
490#undef CALC_STAT
491#undef FMT
Eric Andersen44608e92002-10-22 12:21:15 +0000492
493static void clearmems(void)
494{
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000495 clear_username_cache();
Eric Andersen08a72202002-09-30 20:52:10 +0000496 free(top);
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000497 top = NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000498 ntop = 0;
499}
500
Denis Vlasenkofa076802006-11-05 00:38:51 +0000501#if ENABLE_FEATURE_USE_TERMIOS
Eric Andersen08a72202002-09-30 20:52:10 +0000502#include <termios.h>
Eric Andersen08a72202002-09-30 20:52:10 +0000503#include <signal.h>
504
Eric Andersen08a72202002-09-30 20:52:10 +0000505static void reset_term(void)
506{
507 tcsetattr(0, TCSANOW, (void *) &initial_settings);
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000508 if (ENABLE_FEATURE_CLEAN_UP) {
509 clearmems();
Denis Vlasenkofa076802006-11-05 00:38:51 +0000510#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000511 free(prev_hist);
Eric Andersen08a72202002-09-30 20:52:10 +0000512#endif
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000513 }
Eric Andersen08a72202002-09-30 20:52:10 +0000514}
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000515
Rob Landleyb2804552006-02-13 22:04:27 +0000516static void sig_catcher(int sig ATTRIBUTE_UNUSED)
Eric Andersen08a72202002-09-30 20:52:10 +0000517{
518 reset_term();
Rob Landleydb1ab1a2006-06-28 14:11:25 +0000519 exit(1);
Eric Andersen08a72202002-09-30 20:52:10 +0000520}
Denis Vlasenkofa076802006-11-05 00:38:51 +0000521#endif /* FEATURE_USE_TERMIOS */
Eric Andersen08a72202002-09-30 20:52:10 +0000522
Denis Vlasenkoe7c1ad12007-09-08 17:21:01 +0000523/*
524 * TOPMEM support
525 */
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000526
527typedef unsigned long mem_t;
528
529typedef struct topmem_status_t {
530 unsigned pid;
531 char comm[COMM_LEN];
532 /* vsz doesn't count /dev/xxx mappings except /dev/zero */
533 mem_t vsz ;
534 mem_t vszrw ;
535 mem_t rss ;
536 mem_t rss_sh ;
537 mem_t dirty ;
538 mem_t dirty_sh;
539 mem_t stack ;
540} topmem_status_t;
541
542enum { NUM_SORT_FIELD = 7 };
543
544#define topmem ((topmem_status_t*)top)
545
546#if ENABLE_FEATURE_TOPMEM
547static int topmem_sort(char *a, char *b)
548{
549 int n;
550 mem_t l, r;
551
552 n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
553 l = *(mem_t*)(a + n);
554 r = *(mem_t*)(b + n);
555// if (l == r) {
556// l = a->mapped_rw;
557// r = b->mapped_rw;
558// }
559 /* We want to avoid unsigned->signed and truncation errors */
560 /* l>r: -1, l=r: 0, l<r: 1 */
561 n = (l > r) ? -1 : (l != r);
562 return inverted ? -n : n;
563}
564
565/* Cut "NNNN " out of " NNNN kb" */
566static char *grab_number(char *str, const char *match, unsigned sz)
567{
568 if (strncmp(str, match, sz) == 0) {
569 str = skip_whitespace(str + sz);
570 (skip_non_whitespace(str))[1] = '\0';
571 return xstrdup(str);
572 }
573 return NULL;
574}
575
576/* display header info (meminfo / loadavg) */
577static void display_topmem_header(int scr_width)
578{
579 char linebuf[128];
580 int i;
581 FILE *fp;
582 union {
583 struct {
584 /* 1 */ char *total;
585 /* 2 */ char *mfree;
586 /* 3 */ char *buf;
587 /* 4 */ char *cache;
588 /* 5 */ char *swaptotal;
589 /* 6 */ char *swapfree;
590 /* 7 */ char *dirty;
591 /* 8 */ char *mwrite;
592 /* 9 */ char *anon;
593 /* 10 */ char *map;
594 /* 11 */ char *slab;
595 };
596 char *str[11];
597 } Z;
598#define total Z.total
599#define mfree Z.mfree
600#define buf Z.buf
601#define cache Z.cache
602#define swaptotal Z.swaptotal
603#define swapfree Z.swapfree
604#define dirty Z.dirty
605#define mwrite Z.mwrite
606#define anon Z.anon
607#define map Z.map
608#define slab Z.slab
609#define str Z.str
610
611 memset(&Z, 0, sizeof(Z));
612
613 /* read memory info */
614 fp = xfopen("meminfo", "r");
615 while (fgets(linebuf, sizeof(linebuf), fp)) {
616 char *p;
617
618#define SCAN(match, name) \
619 p = grab_number(linebuf, match, sizeof(match)-1); \
620 if (p) { name = p; continue; }
621
622 SCAN("MemTotal:", total);
623 SCAN("MemFree:", mfree);
624 SCAN("Buffers:", buf);
625 SCAN("Cached:", cache);
626 SCAN("SwapTotal:", swaptotal);
627 SCAN("SwapFree:", swapfree);
628 SCAN("Dirty:", dirty);
629 SCAN("Writeback:", mwrite);
630 SCAN("AnonPages:", anon);
631 SCAN("Mapped:", map);
632 SCAN("Slab:", slab);
633#undef SCAN
634 }
635 fclose(fp);
636
637#define S(s) (s ? s : "0")
638 snprintf(linebuf, sizeof(linebuf),
639 "Mem %stotal %sanon %smap %sfree",
640 S(total), S(anon), S(map), S(mfree));
641 printf(OPT_BATCH_MODE ? "%.*s\n" : "\e[H\e[J%.*s\n", scr_width, linebuf);
642
643 snprintf(linebuf, sizeof(linebuf),
644 " %sslab %sbuf %scache %sdirty %swrite",
645 S(slab), S(buf), S(cache), S(dirty), S(mwrite));
646 printf("%.*s\n", scr_width, linebuf);
647
648 snprintf(linebuf, sizeof(linebuf),
649 "Swap %stotal %sfree", // TODO: % used?
650 S(swaptotal), S(swapfree));
651 printf("%.*s\n", scr_width, linebuf);
652#undef S
653
654 for (i = 0; i < ARRAY_SIZE(str); i++)
655 free(str[i]);
656#undef total
657#undef free
658#undef buf
659#undef cache
660#undef swaptotal
661#undef swapfree
662#undef dirty
663#undef write
664#undef anon
665#undef map
666#undef slab
667#undef str
668}
669
670// Converts unsigned long long value into compact 5-char
671// representation. Sixth char is always ' '
672static void smart_ulltoa6(unsigned long long ul, char buf[6])
673{
674 const char *fmt;
675 char c;
676 unsigned v, u, idx = 0;
677
678 if (ul > 99999) { // do not scale if 99999 or less
679 ul *= 10;
680 do {
681 ul /= 1024;
682 idx++;
683 } while (ul >= 100000);
684 }
685 v = ul; // ullong divisions are expensive, avoid them
686
687 fmt = " 123456789";
688 u = v / 10;
689 v = v % 10;
690 if (!idx) {
691 // 99999 or less: use "12345" format
692 // u is value/10, v is last digit
693 c = buf[0] = " 123456789"[u/1000];
694 if (c != ' ') fmt = "0123456789";
695 c = buf[1] = fmt[u/100%10];
696 if (c != ' ') fmt = "0123456789";
697 c = buf[2] = fmt[u/10%10];
698 if (c != ' ') fmt = "0123456789";
699 buf[3] = fmt[u%10];
700 buf[4] = "0123456789"[v];
701 } else {
702 // value has been scaled into 0..9999.9 range
703 // u is value, v is 1/10ths (allows for 92.1M format)
704 if (u >= 100) {
705 // value is >= 100: use "1234M', " 123M" formats
706 c = buf[0] = " 123456789"[u/1000];
707 if (c != ' ') fmt = "0123456789";
708 c = buf[1] = fmt[u/100%10];
709 if (c != ' ') fmt = "0123456789";
710 v = u % 10;
711 u = u / 10;
712 buf[2] = fmt[u%10];
713 } else {
714 // value is < 100: use "92.1M" format
715 c = buf[0] = " 123456789"[u/10];
716 if (c != ' ') fmt = "0123456789";
717 buf[1] = fmt[u%10];
718 buf[2] = '.';
719 }
720 buf[3] = "0123456789"[v];
721 // see http://en.wikipedia.org/wiki/Tera
722 buf[4] = " mgtpezy"[idx];
723 }
724 buf[5] = ' ';
725}
726
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000727static NOINLINE void display_topmem_process_list(int count, int scr_width)
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000728{
729#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
730#define MIN_WIDTH sizeof(HDR_STR)
731 const topmem_status_t *s = topmem;
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000732
733 display_topmem_header(scr_width);
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000734 strcpy(line_buf, HDR_STR " COMMAND");
735 line_buf[5 + sort_field * 6] = '*';
736 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000737
738 while (--count >= 0) {
739 // PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000740 smart_ulltoa6(s->pid , &line_buf[0*6]);
741 smart_ulltoa6(s->vsz , &line_buf[1*6]);
742 smart_ulltoa6(s->vszrw , &line_buf[2*6]);
743 smart_ulltoa6(s->rss , &line_buf[3*6]);
744 smart_ulltoa6(s->rss_sh , &line_buf[4*6]);
745 smart_ulltoa6(s->dirty , &line_buf[5*6]);
746 smart_ulltoa6(s->dirty_sh, &line_buf[6*6]);
747 smart_ulltoa6(s->stack , &line_buf[7*6]);
748 line_buf[8*6] = '\0';
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000749 if (scr_width > MIN_WIDTH) {
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000750 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000751 }
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000752 printf("\n""%.*s", scr_width, line_buf);
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000753 s++;
754 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000755 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000756 fflush(stdout);
757#undef HDR_STR
758#undef MIN_WIDTH
759}
760#else
761void display_topmem_process_list(int count, int scr_width);
762int topmem_sort(char *a, char *b);
763#endif /* TOPMEM */
764
Denis Vlasenkoe7c1ad12007-09-08 17:21:01 +0000765/*
766 * end TOPMEM support
767 */
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000768
769enum {
770 TOP_MASK = 0
771 | PSSCAN_PID
772 | PSSCAN_PPID
773 | PSSCAN_VSZ
774 | PSSCAN_STIME
775 | PSSCAN_UTIME
776 | PSSCAN_STATE
777 | PSSCAN_COMM
778 | PSSCAN_UIDGID,
779 TOPMEM_MASK = 0
780 | PSSCAN_PID
781 | PSSCAN_SMAPS
782 | PSSCAN_COMM,
783};
784
Denis Vlasenko06af2162007-02-03 17:28:39 +0000785int top_main(int argc, char **argv);
Eric Andersen420b2082002-09-17 22:14:58 +0000786int top_main(int argc, char **argv)
787{
Denis Vlasenko266bc172006-09-29 17:16:39 +0000788 int count, lines, col;
Denis Vlasenkob308d812007-08-28 19:35:34 +0000789 unsigned interval;
Denis Vlasenkoe7c1ad12007-09-08 17:21:01 +0000790 int iterations;
Denis Vlasenko266bc172006-09-29 17:16:39 +0000791 char *sinterval, *siterations;
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000792 SKIP_FEATURE_TOPMEM(const) unsigned scan_mask = TOP_MASK;
Denis Vlasenkofa076802006-11-05 00:38:51 +0000793#if ENABLE_FEATURE_USE_TERMIOS
Eric Andersen08a72202002-09-30 20:52:10 +0000794 struct termios new_settings;
Denis Vlasenkob308d812007-08-28 19:35:34 +0000795 struct pollfd pfd[1];
Eric Andersen08a72202002-09-30 20:52:10 +0000796 unsigned char c;
Denis Vlasenkob308d812007-08-28 19:35:34 +0000797
798 pfd[0].fd = 0;
799 pfd[0].events = POLLIN;
Denis Vlasenkofa076802006-11-05 00:38:51 +0000800#endif /* FEATURE_USE_TERMIOS */
Eric Andersen08a72202002-09-30 20:52:10 +0000801
Denis Vlasenkoe7c1ad12007-09-08 17:21:01 +0000802 INIT_G();
803
Denis Vlasenkob308d812007-08-28 19:35:34 +0000804 interval = 5; /* default update rate is 5 seconds */
Denis Vlasenkoe7c1ad12007-09-08 17:21:01 +0000805 iterations = 0; /* infinite */
Denis Vlasenko85818632007-04-19 14:47:11 +0000806
807 /* do normal option parsing */
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000808 opt_complementary = "-";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000809 getopt32(argv, "d:n:b", &sinterval, &siterations);
Denis Vlasenkob308d812007-08-28 19:35:34 +0000810 if (option_mask32 & 0x1) {
811 /* Need to limit it to not overflow poll timeout */
812 interval = xatou16(sinterval); // -d
813 }
814 if (option_mask32 & 0x2)
815 iterations = xatoi_u(siterations); // -n
Denis Vlasenkoc12f5302006-10-06 09:49:47 +0000816 //if (option_mask32 & 0x4) // -b
Eric Andersen420b2082002-09-17 22:14:58 +0000817
Eric Andersen44608e92002-10-22 12:21:15 +0000818 /* change to /proc */
Rob Landleyd921b2e2006-08-03 15:41:12 +0000819 xchdir("/proc");
Denis Vlasenkofa076802006-11-05 00:38:51 +0000820#if ENABLE_FEATURE_USE_TERMIOS
Eric Andersen08a72202002-09-30 20:52:10 +0000821 tcgetattr(0, (void *) &initial_settings);
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000822 memcpy(&new_settings, &initial_settings, sizeof(new_settings));
Denis Vlasenko42dfcd22006-09-09 12:55:02 +0000823 /* unbuffered input, turn off echo */
824 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
Eric Andersen08a72202002-09-30 20:52:10 +0000825
Rob Landley997650b2006-04-24 23:13:46 +0000826 signal(SIGTERM, sig_catcher);
Rob Landleydb1ab1a2006-06-28 14:11:25 +0000827 signal(SIGINT, sig_catcher);
Eric Andersen08a72202002-09-30 20:52:10 +0000828 tcsetattr(0, TCSANOW, (void *) &new_settings);
829 atexit(reset_term);
Denis Vlasenkofa076802006-11-05 00:38:51 +0000830#endif /* FEATURE_USE_TERMIOS */
Mike Frysinger223b8872005-07-30 09:42:05 +0000831
Denis Vlasenkofa076802006-11-05 00:38:51 +0000832#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Eric Andersen08a72202002-09-30 20:52:10 +0000833 sort_function[0] = pcpu_sort;
834 sort_function[1] = mem_sort;
835 sort_function[2] = time_sort;
836#else
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000837 sort_function[0] = mem_sort;
Denis Vlasenkofa076802006-11-05 00:38:51 +0000838#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
Mike Frysinger223b8872005-07-30 09:42:05 +0000839
Eric Andersen08a72202002-09-30 20:52:10 +0000840 while (1) {
Denis Vlasenko459e4d62006-11-05 00:43:51 +0000841 procps_status_t *p = NULL;
Eric Andersen44608e92002-10-22 12:21:15 +0000842
Denis Vlasenkoe7c1ad12007-09-08 17:21:01 +0000843 lines = 24; /* default */
Rob Landley997650b2006-04-24 23:13:46 +0000844 col = 79;
Denis Vlasenkofa076802006-11-05 00:38:51 +0000845#if ENABLE_FEATURE_USE_TERMIOS
Rob Landley997650b2006-04-24 23:13:46 +0000846 get_terminal_width_height(0, &col, &lines);
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000847 if (lines < 5 || col < 10) {
Rob Landley997650b2006-04-24 23:13:46 +0000848 sleep(interval);
849 continue;
850 }
Denis Vlasenkofa076802006-11-05 00:38:51 +0000851#endif /* FEATURE_USE_TERMIOS */
Denis Vlasenko4c1d88d2007-09-08 17:34:05 +0000852 if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */
853 col = LINE_BUF_SIZE-2;
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000854 if (!ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && scan_mask == TOP_MASK)
855 lines -= 3;
856 else
857 lines -= 4;
Rob Landley997650b2006-04-24 23:13:46 +0000858
859 /* read process IDs & status for all the processes */
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000860 while ((p = procps_scan(p, scan_mask)) != NULL) {
861 int n;
862 if (scan_mask == TOP_MASK) {
863 n = ntop;
864 top = xrealloc(top, (++ntop) * sizeof(*top));
865 top[n].pid = p->pid;
866 top[n].ppid = p->ppid;
867 top[n].vsz = p->vsz;
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000868#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000869 top[n].ticks = p->stime + p->utime;
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000870#endif
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000871 top[n].uid = p->uid;
872 strcpy(top[n].state, p->state);
873 strcpy(top[n].comm, p->comm);
874 } else { /* TOPMEM */
875#if ENABLE_FEATURE_TOPMEM
876 if (!(p->mapped_ro | p->mapped_rw))
877 continue; /* kernel threads are ignored */
878 n = ntop;
879 top = xrealloc(topmem, (++ntop) * sizeof(*topmem));
880 strcpy(topmem[n].comm, p->comm);
881 topmem[n].pid = p->pid;
882 topmem[n].vsz = p->mapped_rw + p->mapped_ro;
883 topmem[n].vszrw = p->mapped_rw;
884 topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
885 topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
886 topmem[n].dirty = p->private_dirty + p->shared_dirty;
887 topmem[n].dirty_sh = p->shared_dirty;
888 topmem[n].stack = p->stack;
889#endif
890 }
Eric Andersen44608e92002-10-22 12:21:15 +0000891 }
892 if (ntop == 0) {
Denis Vlasenko74511962007-06-11 16:31:55 +0000893 bb_error_msg_and_die("no process info in /proc");
Rob Landleyb2804552006-02-13 22:04:27 +0000894 }
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000895
896 if (scan_mask == TOP_MASK) {
Denis Vlasenkofa076802006-11-05 00:38:51 +0000897#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000898 if (!prev_hist_count) {
899 do_stats();
900 usleep(100000);
901 clearmems();
902 continue;
903 }
Eric Andersen08a72202002-09-30 20:52:10 +0000904 do_stats();
Denis Vlasenkof7d07b12007-06-30 08:03:26 +0000905/* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000906 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
Eric Andersen08a72202002-09-30 20:52:10 +0000907#else
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000908 qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
Denis Vlasenkofa076802006-11-05 00:38:51 +0000909#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000910 } else { /* TOPMEM */
911 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
912 }
Denis Vlasenko266bc172006-09-29 17:16:39 +0000913 count = lines;
Denis Vlasenko25d80622006-10-27 09:34:22 +0000914 if (OPT_BATCH_MODE || count > ntop) {
Denis Vlasenko266bc172006-09-29 17:16:39 +0000915 count = ntop;
Eric Andersen08a72202002-09-30 20:52:10 +0000916 }
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000917 if (scan_mask == TOP_MASK)
918 display_process_list(count, col);
919 else
920 display_topmem_process_list(count, col);
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000921 clearmems();
922 if (iterations >= 0 && !--iterations)
923 break;
924#if !ENABLE_FEATURE_USE_TERMIOS
925 sleep(interval);
926#else
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000927 if (safe_poll(pfd, 1, interval * 1000) > 0) {
Denis Vlasenkob308d812007-08-28 19:35:34 +0000928 if (read(0, &c, 1) != 1) /* signal */
929 break;
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000930 if (c == initial_settings.c_cc[VINTR])
"Vladimir N. Oleynik"c218a292006-02-15 17:15:56 +0000931 break;
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000932 c |= 0x20; /* lowercase */
933 if (c == 'q')
934 break;
935 if (c == 'n') {
936 USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000937 sort_function[0] = pid_sort;
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000938 }
939 if (c == 'm') {
940 USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
Eric Andersen08a72202002-09-30 20:52:10 +0000941 sort_function[0] = mem_sort;
Denis Vlasenko8bdba4d2007-08-29 18:18:08 +0000942#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Eric Andersen08a72202002-09-30 20:52:10 +0000943 sort_function[1] = pcpu_sort;
944 sort_function[2] = time_sort;
Eric Andersen08a72202002-09-30 20:52:10 +0000945#endif
946 }
Denis Vlasenkofa076802006-11-05 00:38:51 +0000947#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000948 if (c == 'p') {
949 USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
Eric Andersen08a72202002-09-30 20:52:10 +0000950 sort_function[0] = pcpu_sort;
951 sort_function[1] = mem_sort;
952 sort_function[2] = time_sort;
953 }
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000954 if (c == 't') {
955 USE_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
Eric Andersen08a72202002-09-30 20:52:10 +0000956 sort_function[0] = time_sort;
957 sort_function[1] = mem_sort;
958 sort_function[2] = pcpu_sort;
959 }
Denis Vlasenkoff6e8e22007-09-08 16:51:19 +0000960#if ENABLE_FEATURE_TOPMEM
961 if (c == 's') {
962 scan_mask = TOPMEM_MASK;
963 free(prev_hist);
964 prev_hist = NULL;
965 prev_hist_count = 0;
966 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
967 }
968 if (c == 'r')
969 inverted ^= 1;
970#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000971#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000972 }
Denis Vlasenkofa076802006-11-05 00:38:51 +0000973#endif /* FEATURE_USE_TERMIOS */
Eric Andersen420b2082002-09-17 22:14:58 +0000974 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000975 bb_putchar('\n');
Eric Andersen420b2082002-09-17 22:14:58 +0000976 return EXIT_SUCCESS;
977}