top.c 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * A tiny 'top' utility.
  4. *
  5. * 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.
  11. *
  12. * NOTES:
  13. * - At startup this changes to /proc, all the reads are then
  14. * relative to that.
  15. *
  16. * (C) Eero Tamminen <oak at welho dot com>
  17. *
  18. * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
  19. *
  20. * Sept 2008: Vineet Gupta <vineet.gupta@arc.com>
  21. * Added Support for reporting SMP Information
  22. * - CPU where Process was last seen running
  23. * (to see effect of sched_setaffinity() etc)
  24. * - CPU Time Split (idle/IO/wait etc) PER CPU
  25. *
  26. * Copyright (c) 1992 Branko Lankester
  27. * Copyright (c) 1992 Roger Binns
  28. * Copyright (C) 1994-1996 Charles L. Blake.
  29. * Copyright (C) 1992-1998 Michael K. Johnson
  30. *
  31. * Licensed under GPLv2, see file LICENSE in this tarball for details.
  32. */
  33. #include "libbb.h"
  34. typedef struct top_status_t {
  35. unsigned long vsz;
  36. #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];
  43. char comm[COMM_LEN];
  44. #if ENABLE_FEATURE_TOP_SMP_PROCESS
  45. int last_seen_on_cpu;
  46. #endif
  47. } top_status_t;
  48. typedef struct jiffy_counts_t {
  49. /* Linux 2.4.x has only first four */
  50. unsigned long long usr, nic, sys, idle;
  51. unsigned long long iowait, irq, softirq, steal;
  52. unsigned long long total;
  53. unsigned long long busy;
  54. } jiffy_counts_t;
  55. /* This structure stores some critical information from one frame to
  56. the next. Used for finding deltas. */
  57. typedef struct save_hist {
  58. unsigned long ticks;
  59. pid_t pid;
  60. } save_hist;
  61. typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
  62. enum { SORT_DEPTH = 3 };
  63. struct globals {
  64. top_status_t *top;
  65. int ntop;
  66. #if ENABLE_FEATURE_TOPMEM
  67. smallint sort_field;
  68. smallint inverted;
  69. #endif
  70. #if ENABLE_FEATURE_TOP_SMP_CPU
  71. smallint smp_cpu_info; /* one/many cpu info lines? */
  72. #endif
  73. #if ENABLE_FEATURE_USE_TERMIOS
  74. struct termios initial_settings;
  75. #endif
  76. #if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  77. cmp_funcp sort_function[1];
  78. #else
  79. cmp_funcp sort_function[SORT_DEPTH];
  80. struct save_hist *prev_hist;
  81. int prev_hist_count;
  82. jiffy_counts_t cur_jif, prev_jif;
  83. /* int hist_iterations; */
  84. unsigned total_pcpu;
  85. /* unsigned long total_vsz; */
  86. #endif
  87. #if ENABLE_FEATURE_TOP_SMP_CPU
  88. /* Per CPU samples: current and last */
  89. jiffy_counts_t *cpu_jif, *cpu_prev_jif;
  90. int num_cpus;
  91. #endif
  92. char line_buf[80];
  93. }; //FIX_ALIASING; - large code growth
  94. enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
  95. #define G (*(struct globals*)&bb_common_bufsiz1)
  96. struct BUG_bad_size {
  97. char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
  98. char BUG_line_buf_too_small[LINE_BUF_SIZE > 80 ? 1 : -1];
  99. };
  100. #define INIT_G() do { } while (0)
  101. #define top (G.top )
  102. #define ntop (G.ntop )
  103. #define sort_field (G.sort_field )
  104. #define inverted (G.inverted )
  105. #define smp_cpu_info (G.smp_cpu_info )
  106. #define initial_settings (G.initial_settings )
  107. #define sort_function (G.sort_function )
  108. #define prev_hist (G.prev_hist )
  109. #define prev_hist_count (G.prev_hist_count )
  110. #define cur_jif (G.cur_jif )
  111. #define prev_jif (G.prev_jif )
  112. #define cpu_jif (G.cpu_jif )
  113. #define cpu_prev_jif (G.cpu_prev_jif )
  114. #define num_cpus (G.num_cpus )
  115. #define total_pcpu (G.total_pcpu )
  116. #define line_buf (G.line_buf )
  117. enum {
  118. OPT_d = (1 << 0),
  119. OPT_n = (1 << 1),
  120. OPT_b = (1 << 2),
  121. OPT_m = (1 << 3),
  122. OPT_EOF = (1 << 4), /* pseudo: "we saw EOF in stdin" */
  123. };
  124. #define OPT_BATCH_MODE (option_mask32 & OPT_b)
  125. #if ENABLE_FEATURE_USE_TERMIOS
  126. static int pid_sort(top_status_t *P, top_status_t *Q)
  127. {
  128. /* Buggy wrt pids with high bit set */
  129. /* (linux pids are in [1..2^15-1]) */
  130. return (Q->pid - P->pid);
  131. }
  132. #endif
  133. static int mem_sort(top_status_t *P, top_status_t *Q)
  134. {
  135. /* We want to avoid unsigned->signed and truncation errors */
  136. if (Q->vsz < P->vsz) return -1;
  137. return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
  138. }
  139. #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  140. static int pcpu_sort(top_status_t *P, top_status_t *Q)
  141. {
  142. /* Buggy wrt ticks with high bit set */
  143. /* Affects only processes for which ticks overflow */
  144. return (int)Q->pcpu - (int)P->pcpu;
  145. }
  146. static int time_sort(top_status_t *P, top_status_t *Q)
  147. {
  148. /* We want to avoid unsigned->signed and truncation errors */
  149. if (Q->ticks < P->ticks) return -1;
  150. return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
  151. }
  152. static int mult_lvl_cmp(void* a, void* b)
  153. {
  154. int i, cmp_val;
  155. for (i = 0; i < SORT_DEPTH; i++) {
  156. cmp_val = (*sort_function[i])(a, b);
  157. if (cmp_val != 0)
  158. return cmp_val;
  159. }
  160. return 0;
  161. }
  162. static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
  163. {
  164. #if !ENABLE_FEATURE_TOP_SMP_CPU
  165. static const char fmt[] = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
  166. #else
  167. static const char fmt[] = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
  168. #endif
  169. int ret;
  170. if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */)
  171. return 0;
  172. ret = sscanf(line_buf, fmt,
  173. &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
  174. &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
  175. &p_jif->steal);
  176. if (ret >= 4) {
  177. p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle
  178. + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal;
  179. /* procps 2.x does not count iowait as busy time */
  180. p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait;
  181. }
  182. return ret;
  183. }
  184. static void get_jiffy_counts(void)
  185. {
  186. FILE* fp = xfopen_for_read("stat");
  187. /* We need to parse cumulative counts even if SMP CPU display is on,
  188. * they are used to calculate per process CPU% */
  189. prev_jif = cur_jif;
  190. if (read_cpu_jiffy(fp, &cur_jif) < 4)
  191. bb_error_msg_and_die("can't read /proc/stat");
  192. #if !ENABLE_FEATURE_TOP_SMP_CPU
  193. fclose(fp);
  194. return;
  195. #else
  196. if (!smp_cpu_info) {
  197. fclose(fp);
  198. return;
  199. }
  200. if (!num_cpus) {
  201. /* First time here. How many CPUs?
  202. * There will be at least 1 /proc/stat line with cpu%d
  203. */
  204. while (1) {
  205. cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
  206. if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
  207. break;
  208. num_cpus++;
  209. }
  210. if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */
  211. smp_cpu_info = 0;
  212. cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
  213. /* Otherwise the first per cpu display shows all 100% idles */
  214. usleep(50000);
  215. } else { /* Non first time invocation */
  216. jiffy_counts_t *tmp;
  217. int i;
  218. /* First switch the sample pointers: no need to copy */
  219. tmp = cpu_prev_jif;
  220. cpu_prev_jif = cpu_jif;
  221. cpu_jif = tmp;
  222. /* Get the new samples */
  223. for (i = 0; i < num_cpus; i++)
  224. read_cpu_jiffy(fp, &cpu_jif[i]);
  225. }
  226. #endif
  227. fclose(fp);
  228. }
  229. static void do_stats(void)
  230. {
  231. top_status_t *cur;
  232. pid_t pid;
  233. int i, last_i, n;
  234. struct save_hist *new_hist;
  235. get_jiffy_counts();
  236. total_pcpu = 0;
  237. /* total_vsz = 0; */
  238. new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
  239. /*
  240. * Make a pass through the data to get stats.
  241. */
  242. /* hist_iterations = 0; */
  243. i = 0;
  244. for (n = 0; n < ntop; n++) {
  245. cur = top + n;
  246. /*
  247. * Calculate time in cur process. Time is sum of user time
  248. * and system time
  249. */
  250. pid = cur->pid;
  251. new_hist[n].ticks = cur->ticks;
  252. new_hist[n].pid = pid;
  253. /* find matching entry from previous pass */
  254. cur->pcpu = 0;
  255. /* do not start at index 0, continue at last used one
  256. * (brought hist_iterations from ~14000 down to 172) */
  257. last_i = i;
  258. if (prev_hist_count) do {
  259. if (prev_hist[i].pid == pid) {
  260. cur->pcpu = cur->ticks - prev_hist[i].ticks;
  261. total_pcpu += cur->pcpu;
  262. break;
  263. }
  264. i = (i+1) % prev_hist_count;
  265. /* hist_iterations++; */
  266. } while (i != last_i);
  267. /* total_vsz += cur->vsz; */
  268. }
  269. /*
  270. * Save cur frame's information.
  271. */
  272. free(prev_hist);
  273. prev_hist = new_hist;
  274. prev_hist_count = ntop;
  275. }
  276. #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
  277. #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
  278. /* formats 7 char string (8 with terminating NUL) */
  279. static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
  280. {
  281. unsigned t;
  282. if (value >= total) { /* 100% ? */
  283. strcpy(pbuf, " 100% ");
  284. return pbuf;
  285. }
  286. /* else generate " [N/space]N.N% " string */
  287. value = 1000 * value / total;
  288. t = value / 100;
  289. value = value % 100;
  290. pbuf[0] = ' ';
  291. pbuf[1] = t ? t + '0' : ' ';
  292. pbuf[2] = '0' + (value / 10);
  293. pbuf[3] = '.';
  294. pbuf[4] = '0' + (value % 10);
  295. pbuf[5] = '%';
  296. pbuf[6] = ' ';
  297. pbuf[7] = '\0';
  298. return pbuf;
  299. }
  300. #endif
  301. #if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
  302. static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
  303. {
  304. /*
  305. * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
  306. */
  307. unsigned total_diff;
  308. jiffy_counts_t *p_jif, *p_prev_jif;
  309. int i;
  310. # if ENABLE_FEATURE_TOP_SMP_CPU
  311. int n_cpu_lines;
  312. # endif
  313. /* using (unsigned) casts to make operations cheaper */
  314. # define CALC_TOTAL_DIFF do { \
  315. total_diff = (unsigned)(p_jif->total - p_prev_jif->total); \
  316. if (total_diff == 0) total_diff = 1; \
  317. } while (0)
  318. # if ENABLE_FEATURE_TOP_DECIMALS
  319. # define CALC_STAT(xxx) char xxx[8]
  320. # define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
  321. # define FMT "%s"
  322. # else
  323. # define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
  324. # define SHOW_STAT(xxx) xxx
  325. # define FMT "%4u%% "
  326. # endif
  327. # if !ENABLE_FEATURE_TOP_SMP_CPU
  328. {
  329. i = 1;
  330. p_jif = &cur_jif;
  331. p_prev_jif = &prev_jif;
  332. # else
  333. /* Loop thru CPU(s) */
  334. n_cpu_lines = smp_cpu_info ? num_cpus : 1;
  335. if (n_cpu_lines > *lines_rem_p)
  336. n_cpu_lines = *lines_rem_p;
  337. for (i = 0; i < n_cpu_lines; i++) {
  338. p_jif = &cpu_jif[i];
  339. p_prev_jif = &cpu_prev_jif[i];
  340. # endif
  341. CALC_TOTAL_DIFF;
  342. { /* Need a block: CALC_STAT are declarations */
  343. CALC_STAT(usr);
  344. CALC_STAT(sys);
  345. CALC_STAT(nic);
  346. CALC_STAT(idle);
  347. CALC_STAT(iowait);
  348. CALC_STAT(irq);
  349. CALC_STAT(softirq);
  350. /*CALC_STAT(steal);*/
  351. snprintf(scrbuf, scr_width,
  352. /* Barely fits in 79 chars when in "decimals" mode. */
  353. # if ENABLE_FEATURE_TOP_SMP_CPU
  354. "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
  355. (smp_cpu_info ? utoa(i) : ""),
  356. # else
  357. "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
  358. # endif
  359. SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
  360. SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
  361. /*, SHOW_STAT(steal) - what is this 'steal' thing? */
  362. /* I doubt anyone wants to know it */
  363. );
  364. puts(scrbuf);
  365. }
  366. }
  367. # undef SHOW_STAT
  368. # undef CALC_STAT
  369. # undef FMT
  370. *lines_rem_p -= i;
  371. }
  372. #else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
  373. # define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
  374. #endif
  375. static unsigned long display_header(int scr_width, int *lines_rem_p)
  376. {
  377. FILE *fp;
  378. char buf[80];
  379. char scrbuf[80];
  380. unsigned long total, used, mfree, shared, buffers, cached;
  381. /* read memory info */
  382. fp = xfopen_for_read("meminfo");
  383. /*
  384. * Old kernels (such as 2.4.x) had a nice summary of memory info that
  385. * we could parse, however this is gone entirely in 2.6. Try parsing
  386. * the old way first, and if that fails, parse each field manually.
  387. *
  388. * First, we read in the first line. Old kernels will have bogus
  389. * strings we don't care about, whereas new kernels will start right
  390. * out with MemTotal:
  391. * -- PFM.
  392. */
  393. if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
  394. fgets(buf, sizeof(buf), fp); /* skip first line */
  395. fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
  396. &total, &used, &mfree, &shared, &buffers, &cached);
  397. /* convert to kilobytes */
  398. used /= 1024;
  399. mfree /= 1024;
  400. shared /= 1024;
  401. buffers /= 1024;
  402. cached /= 1024;
  403. total /= 1024;
  404. } else {
  405. /*
  406. * Revert to manual parsing, which incidentally already has the
  407. * sizes in kilobytes. This should be safe for both 2.4 and
  408. * 2.6.
  409. */
  410. fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
  411. /*
  412. * MemShared: is no longer present in 2.6. Report this as 0,
  413. * to maintain consistent behavior with normal procps.
  414. */
  415. if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
  416. shared = 0;
  417. fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
  418. fscanf(fp, "Cached: %lu %s\n", &cached, buf);
  419. used = total - mfree;
  420. }
  421. fclose(fp);
  422. /* output memory info */
  423. if (scr_width > (int)sizeof(scrbuf))
  424. scr_width = sizeof(scrbuf);
  425. snprintf(scrbuf, scr_width,
  426. "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
  427. used, mfree, shared, buffers, cached);
  428. /* go to top & clear to the end of screen */
  429. printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf);
  430. (*lines_rem_p)--;
  431. /* Display CPU time split as percentage of total time
  432. * This displays either a cumulative line or one line per CPU
  433. */
  434. display_cpus(scr_width, scrbuf, lines_rem_p);
  435. /* read load average as a string */
  436. buf[0] = '\0';
  437. open_read_close("loadavg", buf, sizeof(buf) - 1);
  438. buf[sizeof(buf) - 1] = '\n';
  439. *strchr(buf, '\n') = '\0';
  440. snprintf(scrbuf, scr_width, "Load average: %s", buf);
  441. puts(scrbuf);
  442. (*lines_rem_p)--;
  443. return total;
  444. }
  445. static NOINLINE void display_process_list(int lines_rem, int scr_width)
  446. {
  447. enum {
  448. BITS_PER_INT = sizeof(int) * 8
  449. };
  450. top_status_t *s;
  451. char vsz_str_buf[8];
  452. unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */
  453. /* xxx_shift and xxx_scale variables allow us to replace
  454. * expensive divides with multiply and shift */
  455. unsigned pmem_shift, pmem_scale, pmem_half;
  456. #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  457. unsigned tmp_unsigned;
  458. unsigned pcpu_shift, pcpu_scale, pcpu_half;
  459. unsigned busy_jifs;
  460. #endif
  461. /* what info of the processes is shown */
  462. printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
  463. " PID PPID USER STAT VSZ %MEM"
  464. IF_FEATURE_TOP_SMP_PROCESS(" CPU")
  465. IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
  466. " COMMAND");
  467. lines_rem--;
  468. #if ENABLE_FEATURE_TOP_DECIMALS
  469. # define UPSCALE 1000
  470. # define CALC_STAT(name, val) div_t name = div((val), 10)
  471. # define SHOW_STAT(name) name.quot, '0'+name.rem
  472. # define FMT "%3u.%c"
  473. #else
  474. # define UPSCALE 100
  475. # define CALC_STAT(name, val) unsigned name = (val)
  476. # define SHOW_STAT(name) name
  477. # define FMT "%4u%%"
  478. #endif
  479. /*
  480. * MEM% = s->vsz/MemTotal
  481. */
  482. pmem_shift = BITS_PER_INT-11;
  483. pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
  484. /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
  485. while (pmem_scale >= 512) {
  486. pmem_scale /= 4;
  487. pmem_shift -= 2;
  488. }
  489. pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
  490. #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  491. busy_jifs = cur_jif.busy - prev_jif.busy;
  492. /* This happens if there were lots of short-lived processes
  493. * between two top updates (e.g. compilation) */
  494. if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
  495. /*
  496. * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
  497. * (pcpu is delta of sys+user time between samples)
  498. */
  499. /* (cur_jif.xxx - prev_jif.xxx) and s->pcpu are
  500. * in 0..~64000 range (HZ*update_interval).
  501. * we assume that unsigned is at least 32-bit.
  502. */
  503. pcpu_shift = 6;
  504. pcpu_scale = UPSCALE*64 * (uint16_t)busy_jifs;
  505. if (pcpu_scale == 0)
  506. pcpu_scale = 1;
  507. while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
  508. pcpu_scale *= 4;
  509. pcpu_shift += 2;
  510. }
  511. tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
  512. if (tmp_unsigned != 0)
  513. pcpu_scale /= tmp_unsigned;
  514. /* we want (s->pcpu * pcpu_scale) to never overflow */
  515. while (pcpu_scale >= 1024) {
  516. pcpu_scale /= 4;
  517. pcpu_shift -= 2;
  518. }
  519. pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
  520. /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
  521. #endif
  522. /* Ok, all preliminary data is ready, go through the list */
  523. scr_width += 2; /* account for leading '\n' and trailing NUL */
  524. if (lines_rem > ntop)
  525. lines_rem = ntop;
  526. s = top;
  527. while (--lines_rem >= 0) {
  528. unsigned col;
  529. CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
  530. #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  531. CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
  532. #endif
  533. if (s->vsz >= 100000)
  534. sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
  535. else
  536. sprintf(vsz_str_buf, "%7ld", s->vsz);
  537. /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */
  538. col = snprintf(line_buf, scr_width,
  539. "\n" "%5u%6u %-8.8s %s%s" FMT
  540. IF_FEATURE_TOP_SMP_PROCESS(" %3d")
  541. IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
  542. " ",
  543. s->pid, s->ppid, get_cached_username(s->uid),
  544. s->state, vsz_str_buf,
  545. SHOW_STAT(pmem)
  546. IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
  547. IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
  548. );
  549. if ((int)(col + 1) < scr_width)
  550. read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
  551. fputs(line_buf, stdout);
  552. /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
  553. cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
  554. s++;
  555. }
  556. /* printf(" %d", hist_iterations); */
  557. bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
  558. fflush_all();
  559. }
  560. #undef UPSCALE
  561. #undef SHOW_STAT
  562. #undef CALC_STAT
  563. #undef FMT
  564. static void clearmems(void)
  565. {
  566. clear_username_cache();
  567. free(top);
  568. top = NULL;
  569. ntop = 0;
  570. }
  571. #if ENABLE_FEATURE_USE_TERMIOS
  572. static void reset_term(void)
  573. {
  574. tcsetattr_stdin_TCSANOW(&initial_settings);
  575. if (ENABLE_FEATURE_CLEAN_UP) {
  576. clearmems();
  577. # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  578. free(prev_hist);
  579. # endif
  580. }
  581. }
  582. static void sig_catcher(int sig UNUSED_PARAM)
  583. {
  584. reset_term();
  585. exit(EXIT_FAILURE);
  586. }
  587. #endif /* FEATURE_USE_TERMIOS */
  588. /*
  589. * TOPMEM support
  590. */
  591. typedef unsigned long mem_t;
  592. typedef struct topmem_status_t {
  593. unsigned pid;
  594. char comm[COMM_LEN];
  595. /* vsz doesn't count /dev/xxx mappings except /dev/zero */
  596. mem_t vsz ;
  597. mem_t vszrw ;
  598. mem_t rss ;
  599. mem_t rss_sh ;
  600. mem_t dirty ;
  601. mem_t dirty_sh;
  602. mem_t stack ;
  603. } topmem_status_t;
  604. enum { NUM_SORT_FIELD = 7 };
  605. #define topmem ((topmem_status_t*)top)
  606. #if ENABLE_FEATURE_TOPMEM
  607. static int topmem_sort(char *a, char *b)
  608. {
  609. int n;
  610. mem_t l, r;
  611. n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
  612. l = *(mem_t*)(a + n);
  613. r = *(mem_t*)(b + n);
  614. if (l == r) {
  615. l = ((topmem_status_t*)a)->dirty;
  616. r = ((topmem_status_t*)b)->dirty;
  617. }
  618. /* We want to avoid unsigned->signed and truncation errors */
  619. /* l>r: -1, l=r: 0, l<r: 1 */
  620. n = (l > r) ? -1 : (l != r);
  621. return inverted ? -n : n;
  622. }
  623. /* display header info (meminfo / loadavg) */
  624. static void display_topmem_header(int scr_width, int *lines_rem_p)
  625. {
  626. enum {
  627. TOTAL = 0, MFREE, BUF, CACHE,
  628. SWAPTOTAL, SWAPFREE, DIRTY,
  629. MWRITE, ANON, MAP, SLAB,
  630. NUM_FIELDS
  631. };
  632. static const char match[NUM_FIELDS][12] = {
  633. "\x09" "MemTotal:", // TOTAL
  634. "\x08" "MemFree:", // MFREE
  635. "\x08" "Buffers:", // BUF
  636. "\x07" "Cached:", // CACHE
  637. "\x0a" "SwapTotal:", // SWAPTOTAL
  638. "\x09" "SwapFree:", // SWAPFREE
  639. "\x06" "Dirty:", // DIRTY
  640. "\x0a" "Writeback:", // MWRITE
  641. "\x0a" "AnonPages:", // ANON
  642. "\x07" "Mapped:", // MAP
  643. "\x05" "Slab:", // SLAB
  644. };
  645. char meminfo_buf[4 * 1024];
  646. const char *Z[NUM_FIELDS];
  647. unsigned i;
  648. int sz;
  649. for (i = 0; i < NUM_FIELDS; i++)
  650. Z[i] = "?";
  651. /* read memory info */
  652. sz = open_read_close("meminfo", meminfo_buf, sizeof(meminfo_buf) - 1);
  653. if (sz >= 0) {
  654. char *p = meminfo_buf;
  655. meminfo_buf[sz] = '\0';
  656. /* Note that fields always appear in the match[] order */
  657. for (i = 0; i < NUM_FIELDS; i++) {
  658. char *found = strstr(p, match[i] + 1);
  659. if (found) {
  660. /* Cut "NNNN" out of " NNNN kb" */
  661. char *s = skip_whitespace(found + match[i][0]);
  662. p = skip_non_whitespace(s);
  663. *p++ = '\0';
  664. Z[i] = s;
  665. }
  666. }
  667. }
  668. snprintf(line_buf, LINE_BUF_SIZE,
  669. "Mem total:%s anon:%s map:%s free:%s",
  670. Z[TOTAL], Z[ANON], Z[MAP], Z[MFREE]);
  671. printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf);
  672. snprintf(line_buf, LINE_BUF_SIZE,
  673. " slab:%s buf:%s cache:%s dirty:%s write:%s",
  674. Z[SLAB], Z[BUF], Z[CACHE], Z[DIRTY], Z[MWRITE]);
  675. printf("%.*s\n", scr_width, line_buf);
  676. snprintf(line_buf, LINE_BUF_SIZE,
  677. "Swap total:%s free:%s", // TODO: % used?
  678. Z[SWAPTOTAL], Z[SWAPFREE]);
  679. printf("%.*s\n", scr_width, line_buf);
  680. (*lines_rem_p) -= 3;
  681. }
  682. static void ulltoa6_and_space(unsigned long long ul, char buf[6])
  683. {
  684. /* see http://en.wikipedia.org/wiki/Tera */
  685. smart_ulltoa5(ul, buf, " mgtpezy");
  686. buf[5] = ' ';
  687. }
  688. static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
  689. {
  690. #define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
  691. #define MIN_WIDTH sizeof(HDR_STR)
  692. const topmem_status_t *s = topmem;
  693. display_topmem_header(scr_width, &lines_rem);
  694. strcpy(line_buf, HDR_STR " COMMAND");
  695. line_buf[5 + sort_field * 6] = '*';
  696. printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
  697. lines_rem--;
  698. if (lines_rem > ntop)
  699. lines_rem = ntop;
  700. while (--lines_rem >= 0) {
  701. /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
  702. ulltoa6_and_space(s->pid , &line_buf[0*6]);
  703. ulltoa6_and_space(s->vsz , &line_buf[1*6]);
  704. ulltoa6_and_space(s->vszrw , &line_buf[2*6]);
  705. ulltoa6_and_space(s->rss , &line_buf[3*6]);
  706. ulltoa6_and_space(s->rss_sh , &line_buf[4*6]);
  707. ulltoa6_and_space(s->dirty , &line_buf[5*6]);
  708. ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
  709. ulltoa6_and_space(s->stack , &line_buf[7*6]);
  710. line_buf[8*6] = '\0';
  711. if (scr_width > (int)MIN_WIDTH) {
  712. read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
  713. }
  714. printf("\n""%.*s", scr_width, line_buf);
  715. s++;
  716. }
  717. bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
  718. fflush_all();
  719. #undef HDR_STR
  720. #undef MIN_WIDTH
  721. }
  722. #else
  723. void display_topmem_process_list(int lines_rem, int scr_width);
  724. int topmem_sort(char *a, char *b);
  725. #endif /* TOPMEM */
  726. /*
  727. * end TOPMEM support
  728. */
  729. enum {
  730. TOP_MASK = 0
  731. | PSSCAN_PID
  732. | PSSCAN_PPID
  733. | PSSCAN_VSZ
  734. | PSSCAN_STIME
  735. | PSSCAN_UTIME
  736. | PSSCAN_STATE
  737. | PSSCAN_COMM
  738. | PSSCAN_CPU
  739. | PSSCAN_UIDGID,
  740. TOPMEM_MASK = 0
  741. | PSSCAN_PID
  742. | PSSCAN_SMAPS
  743. | PSSCAN_COMM,
  744. };
  745. int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  746. int top_main(int argc UNUSED_PARAM, char **argv)
  747. {
  748. int iterations;
  749. unsigned lines, col;
  750. int lines_rem;
  751. unsigned interval;
  752. char *str_interval, *str_iterations;
  753. unsigned scan_mask = TOP_MASK;
  754. #if ENABLE_FEATURE_USE_TERMIOS
  755. struct termios new_settings;
  756. struct pollfd pfd[1];
  757. unsigned char c;
  758. pfd[0].fd = 0;
  759. pfd[0].events = POLLIN;
  760. #endif
  761. INIT_G();
  762. interval = 5; /* default update interval is 5 seconds */
  763. iterations = 0; /* infinite */
  764. #if ENABLE_FEATURE_TOP_SMP_CPU
  765. /*num_cpus = 0;*/
  766. /*smp_cpu_info = 0;*/ /* to start with show aggregate */
  767. cpu_jif = &cur_jif;
  768. cpu_prev_jif = &prev_jif;
  769. #endif
  770. /* all args are options; -n NUM */
  771. opt_complementary = "-"; /* options can be specified w/o dash */
  772. col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations);
  773. #if ENABLE_FEATURE_TOPMEM
  774. if (col & OPT_m) /* -m (busybox specific) */
  775. scan_mask = TOPMEM_MASK;
  776. #endif
  777. if (col & OPT_d) {
  778. /* work around for "-d 1" -> "-d -1" done by getopt32
  779. * (opt_complementary == "-" does this) */
  780. if (str_interval[0] == '-')
  781. str_interval++;
  782. /* Need to limit it to not overflow poll timeout */
  783. interval = xatou16(str_interval);
  784. }
  785. if (col & OPT_n) {
  786. if (str_iterations[0] == '-')
  787. str_iterations++;
  788. iterations = xatou(str_iterations);
  789. }
  790. /* change to /proc */
  791. xchdir("/proc");
  792. #if ENABLE_FEATURE_USE_TERMIOS
  793. tcgetattr(0, (void *) &initial_settings);
  794. memcpy(&new_settings, &initial_settings, sizeof(new_settings));
  795. /* unbuffered input, turn off echo */
  796. new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
  797. bb_signals(BB_FATAL_SIGS, sig_catcher);
  798. tcsetattr_stdin_TCSANOW(&new_settings);
  799. #endif
  800. #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  801. sort_function[0] = pcpu_sort;
  802. sort_function[1] = mem_sort;
  803. sort_function[2] = time_sort;
  804. #else
  805. sort_function[0] = mem_sort;
  806. #endif
  807. while (1) {
  808. procps_status_t *p = NULL;
  809. lines = 24; /* default */
  810. col = 79;
  811. #if ENABLE_FEATURE_USE_TERMIOS
  812. /* We output to stdout, we need size of stdout (not stdin)! */
  813. get_terminal_width_height(STDOUT_FILENO, &col, &lines);
  814. if (lines < 5 || col < 10) {
  815. sleep(interval);
  816. continue;
  817. }
  818. #endif
  819. if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */
  820. col = LINE_BUF_SIZE-2;
  821. /* read process IDs & status for all the processes */
  822. while ((p = procps_scan(p, scan_mask)) != NULL) {
  823. int n;
  824. #if ENABLE_FEATURE_TOPMEM
  825. if (scan_mask != TOPMEM_MASK)
  826. #endif
  827. {
  828. n = ntop;
  829. top = xrealloc_vector(top, 6, ntop++);
  830. top[n].pid = p->pid;
  831. top[n].ppid = p->ppid;
  832. top[n].vsz = p->vsz;
  833. #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  834. top[n].ticks = p->stime + p->utime;
  835. #endif
  836. top[n].uid = p->uid;
  837. strcpy(top[n].state, p->state);
  838. strcpy(top[n].comm, p->comm);
  839. #if ENABLE_FEATURE_TOP_SMP_PROCESS
  840. top[n].last_seen_on_cpu = p->last_seen_on_cpu;
  841. #endif
  842. }
  843. #if ENABLE_FEATURE_TOPMEM
  844. else { /* TOPMEM */
  845. if (!(p->mapped_ro | p->mapped_rw))
  846. continue; /* kernel threads are ignored */
  847. n = ntop;
  848. /* No bug here - top and topmem are the same */
  849. top = xrealloc_vector(topmem, 6, ntop++);
  850. strcpy(topmem[n].comm, p->comm);
  851. topmem[n].pid = p->pid;
  852. topmem[n].vsz = p->mapped_rw + p->mapped_ro;
  853. topmem[n].vszrw = p->mapped_rw;
  854. topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
  855. topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
  856. topmem[n].dirty = p->private_dirty + p->shared_dirty;
  857. topmem[n].dirty_sh = p->shared_dirty;
  858. topmem[n].stack = p->stack;
  859. }
  860. #endif
  861. } /* end of "while we read /proc" */
  862. if (ntop == 0) {
  863. bb_error_msg("no process info in /proc");
  864. break;
  865. }
  866. if (scan_mask != TOPMEM_MASK) {
  867. #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  868. if (!prev_hist_count) {
  869. do_stats();
  870. usleep(100000);
  871. clearmems();
  872. continue;
  873. }
  874. do_stats();
  875. /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
  876. qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
  877. #else
  878. qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
  879. #endif
  880. }
  881. #if ENABLE_FEATURE_TOPMEM
  882. else { /* TOPMEM */
  883. qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
  884. }
  885. #endif
  886. lines_rem = lines;
  887. if (OPT_BATCH_MODE) {
  888. lines_rem = INT_MAX;
  889. }
  890. if (scan_mask != TOPMEM_MASK)
  891. display_process_list(lines_rem, col);
  892. #if ENABLE_FEATURE_TOPMEM
  893. else
  894. display_topmem_process_list(lines_rem, col);
  895. #endif
  896. clearmems();
  897. if (iterations >= 0 && !--iterations)
  898. break;
  899. #if !ENABLE_FEATURE_USE_TERMIOS
  900. sleep(interval);
  901. #else
  902. if (option_mask32 & (OPT_b|OPT_EOF))
  903. /* batch mode, or EOF on stdin ("top </dev/null") */
  904. sleep(interval);
  905. else if (safe_poll(pfd, 1, interval * 1000) > 0) {
  906. if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */
  907. option_mask32 |= OPT_EOF;
  908. continue;
  909. }
  910. if (c == initial_settings.c_cc[VINTR])
  911. break;
  912. c |= 0x20; /* lowercase */
  913. if (c == 'q')
  914. break;
  915. if (c == 'n') {
  916. IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
  917. sort_function[0] = pid_sort;
  918. }
  919. if (c == 'm') {
  920. IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
  921. sort_function[0] = mem_sort;
  922. # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  923. sort_function[1] = pcpu_sort;
  924. sort_function[2] = time_sort;
  925. # endif
  926. }
  927. # if ENABLE_FEATURE_SHOW_THREADS
  928. if (c == 'h'
  929. IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
  930. ) {
  931. scan_mask ^= PSSCAN_TASKS;
  932. }
  933. # endif
  934. # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
  935. if (c == 'p') {
  936. IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
  937. sort_function[0] = pcpu_sort;
  938. sort_function[1] = mem_sort;
  939. sort_function[2] = time_sort;
  940. }
  941. if (c == 't') {
  942. IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
  943. sort_function[0] = time_sort;
  944. sort_function[1] = mem_sort;
  945. sort_function[2] = pcpu_sort;
  946. }
  947. # if ENABLE_FEATURE_TOPMEM
  948. if (c == 's') {
  949. scan_mask = TOPMEM_MASK;
  950. free(prev_hist);
  951. prev_hist = NULL;
  952. prev_hist_count = 0;
  953. sort_field = (sort_field + 1) % NUM_SORT_FIELD;
  954. }
  955. if (c == 'r')
  956. inverted ^= 1;
  957. # endif
  958. # if ENABLE_FEATURE_TOP_SMP_CPU
  959. /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
  960. if (c == 'c' || c == '1') {
  961. /* User wants to toggle per cpu <> aggregate */
  962. if (smp_cpu_info) {
  963. free(cpu_prev_jif);
  964. free(cpu_jif);
  965. cpu_jif = &cur_jif;
  966. cpu_prev_jif = &prev_jif;
  967. } else {
  968. /* Prepare for xrealloc() */
  969. cpu_jif = cpu_prev_jif = NULL;
  970. }
  971. num_cpus = 0;
  972. smp_cpu_info = !smp_cpu_info;
  973. get_jiffy_counts();
  974. }
  975. # endif
  976. # endif
  977. }
  978. #endif /* FEATURE_USE_TERMIOS */
  979. } /* end of "while (1)" */
  980. bb_putchar('\n');
  981. #if ENABLE_FEATURE_USE_TERMIOS
  982. reset_term();
  983. #endif
  984. return EXIT_SUCCESS;
  985. }