2006-04-13 00:09:58 +05:30
|
|
|
/* vi: set sw=4 ts=4: */
|
2002-09-18 03:44:58 +05:30
|
|
|
/*
|
|
|
|
* A tiny 'top' utility.
|
|
|
|
*
|
2002-10-01 02:22:10 +05:30
|
|
|
* This is written specifically for the linux /proc/<PID>/stat(m)
|
|
|
|
* files format.
|
2008-12-07 06:22:58 +05:30
|
|
|
*
|
2002-10-01 02:22:10 +05:30
|
|
|
* This reads the PIDs of all processes and their status and shows
|
|
|
|
* the status of processes (first ones that fit to screen) at given
|
|
|
|
* intervals.
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2002-09-18 03:44:58 +05:30
|
|
|
* NOTES:
|
|
|
|
* - At startup this changes to /proc, all the reads are then
|
|
|
|
* relative to that.
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2002-09-18 03:44:58 +05:30
|
|
|
* (C) Eero Tamminen <oak at welho dot com>
|
2002-10-01 02:22:10 +05:30
|
|
|
*
|
2004-04-14 23:21:38 +05:30
|
|
|
* Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
|
2008-09-25 16:18:06 +05:30
|
|
|
*
|
|
|
|
* Sept 2008: Vineet Gupta <vineet.gupta@arc.com>
|
|
|
|
* Added Support for reporting SMP Information
|
2011-01-25 17:18:47 +05:30
|
|
|
* - CPU where process was last seen running
|
2008-09-25 16:18:06 +05:30
|
|
|
* (to see effect of sched_setaffinity() etc)
|
2011-01-25 17:18:47 +05:30
|
|
|
* - CPU time split (idle/IO/wait etc) per CPU
|
2008-12-07 06:22:58 +05:30
|
|
|
*
|
2002-10-01 02:22:10 +05:30
|
|
|
* Copyright (c) 1992 Branko Lankester
|
|
|
|
* Copyright (c) 1992 Roger Binns
|
|
|
|
* Copyright (C) 1994-1996 Charles L. Blake.
|
|
|
|
* Copyright (C) 1992-1998 Michael K. Johnson
|
2008-12-07 06:22:58 +05:30
|
|
|
*
|
2010-08-16 23:44:46 +05:30
|
|
|
* Licensed under GPLv2, see file LICENSE in this source tree.
|
2002-09-18 03:44:58 +05:30
|
|
|
*/
|
2011-01-25 17:18:47 +05:30
|
|
|
/* How to snapshot /proc for debugging top problems:
|
2011-01-25 06:27:31 +05:30
|
|
|
* for f in /proc/[0-9]*""/stat; do
|
|
|
|
* n=${f#/proc/}
|
|
|
|
* n=${n%/stat}_stat
|
|
|
|
* cp $f $n
|
|
|
|
* done
|
2011-01-25 17:18:47 +05:30
|
|
|
* cp /proc/stat /proc/meminfo /proc/loadavg .
|
2011-01-25 06:27:31 +05:30
|
|
|
* top -bn1 >top.out
|
2011-01-25 17:18:47 +05:30
|
|
|
*
|
|
|
|
* ...and how to run top on it on another machine:
|
|
|
|
* rm -rf proc; mkdir proc
|
|
|
|
* for f in [0-9]*_stat; do
|
|
|
|
* p=${f%_stat}
|
|
|
|
* mkdir -p proc/$p
|
|
|
|
* cp $f proc/$p/stat
|
|
|
|
* done
|
|
|
|
* cp stat meminfo loadavg proc
|
|
|
|
* chroot . ./top -bn1 >top1.out
|
2011-01-25 06:27:31 +05:30
|
|
|
*/
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:config TOP
|
2018-12-28 07:50:17 +05:30
|
|
|
//config: bool "top (18 kb)"
|
2012-09-21 16:34:37 +05:30
|
|
|
//config: default y
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: The top program provides a dynamic real-time view of a running
|
|
|
|
//config: system.
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:
|
2017-01-11 20:57:12 +05:30
|
|
|
//config:config FEATURE_TOP_INTERACTIVE
|
|
|
|
//config: bool "Accept keyboard commands"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on TOP
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Without this, top will only refresh display every 5 seconds.
|
|
|
|
//config: No keyboard commands will work, only ^C to terminate.
|
2017-01-11 20:57:12 +05:30
|
|
|
//config:
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:config FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
//config: bool "Show CPU per-process usage percentage"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on TOP
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Make top display CPU usage for each process.
|
|
|
|
//config: This adds about 2k.
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_TOP_CPU_GLOBAL_PERCENTS
|
|
|
|
//config: bool "Show CPU global usage percentage"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Makes top display "CPU: NN% usr NN% sys..." line.
|
|
|
|
//config: This adds about 0.5k.
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_TOP_SMP_CPU
|
|
|
|
//config: bool "SMP CPU usage display ('c' key)"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Allow 'c' key to switch between individual/cumulative CPU stats
|
|
|
|
//config: This adds about 0.5k.
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_TOP_DECIMALS
|
|
|
|
//config: bool "Show 1/10th of a percent in CPU/mem statistics"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Show 1/10th of a percent in CPU/mem statistics.
|
|
|
|
//config: This adds about 0.3k.
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_TOP_SMP_PROCESS
|
|
|
|
//config: bool "Show CPU process runs on ('j' field)"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on TOP
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Show CPU where process was last found running on.
|
|
|
|
//config: This is the 'j' field.
|
2012-09-21 16:34:37 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_TOPMEM
|
|
|
|
//config: bool "Topmem command ('s' key)"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on TOP
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Enable 's' in top (gives lots of memory info).
|
2012-09-21 16:34:37 +05:30
|
|
|
|
2016-11-23 10:53:44 +05:30
|
|
|
//applet:IF_TOP(APPLET(top, BB_DIR_USR_BIN, BB_SUID_DROP))
|
|
|
|
|
|
|
|
//kbuild:lib-$(CONFIG_TOP) += top.o
|
|
|
|
|
2007-05-27 00:30:18 +05:30
|
|
|
#include "libbb.h"
|
2002-09-18 03:44:58 +05:30
|
|
|
|
2017-09-14 02:18:30 +05:30
|
|
|
#define ESC "\033"
|
2002-10-01 02:22:10 +05:30
|
|
|
|
2007-04-19 20:17:11 +05:30
|
|
|
typedef struct top_status_t {
|
2007-02-08 13:51:58 +05:30
|
|
|
unsigned long vsz;
|
2006-11-05 06:13:51 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
unsigned long ticks;
|
|
|
|
unsigned pcpu; /* delta of ticks */
|
|
|
|
#endif
|
|
|
|
unsigned pid, ppid;
|
|
|
|
unsigned uid;
|
|
|
|
char state[4];
|
2007-06-30 20:17:41 +05:30
|
|
|
char comm[COMM_LEN];
|
2008-09-25 16:18:06 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_SMP_PROCESS
|
|
|
|
int last_seen_on_cpu;
|
|
|
|
#endif
|
2006-11-05 06:13:51 +05:30
|
|
|
} top_status_t;
|
2007-04-19 20:17:11 +05:30
|
|
|
|
2007-06-30 20:17:41 +05:30
|
|
|
typedef struct jiffy_counts_t {
|
2009-03-03 17:25:31 +05:30
|
|
|
/* Linux 2.4.x has only first four */
|
|
|
|
unsigned long long usr, nic, sys, idle;
|
|
|
|
unsigned long long iowait, irq, softirq, steal;
|
2007-04-19 20:17:11 +05:30
|
|
|
unsigned long long total;
|
|
|
|
unsigned long long busy;
|
|
|
|
} jiffy_counts_t;
|
|
|
|
|
2006-11-05 06:13:51 +05:30
|
|
|
/* This structure stores some critical information from one frame to
|
|
|
|
the next. Used for finding deltas. */
|
2007-04-19 20:17:11 +05:30
|
|
|
typedef struct save_hist {
|
2006-11-05 06:13:51 +05:30
|
|
|
unsigned long ticks;
|
2008-05-16 03:00:45 +05:30
|
|
|
pid_t pid;
|
2007-04-19 20:17:11 +05:30
|
|
|
} save_hist;
|
|
|
|
|
|
|
|
typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
|
|
|
|
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2007-04-19 20:17:11 +05:30
|
|
|
enum { SORT_DEPTH = 3 };
|
|
|
|
|
2017-08-16 15:10:10 +05:30
|
|
|
/* Screens wider than this are unlikely */
|
|
|
|
enum { LINE_BUF_SIZE = 512 - 64 };
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2007-04-19 20:17:11 +05:30
|
|
|
struct globals {
|
|
|
|
top_status_t *top;
|
|
|
|
int ntop;
|
2011-05-07 00:04:04 +05:30
|
|
|
smallint inverted;
|
2007-09-08 22:21:19 +05:30
|
|
|
#if ENABLE_FEATURE_TOPMEM
|
|
|
|
smallint sort_field;
|
|
|
|
#endif
|
2008-09-25 16:18:06 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_SMP_CPU
|
|
|
|
smallint smp_cpu_info; /* one/many cpu info lines? */
|
|
|
|
#endif
|
2012-09-25 16:18:46 +05:30
|
|
|
unsigned lines; /* screen height */
|
2017-01-11 20:57:12 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_INTERACTIVE
|
2007-04-19 20:17:11 +05:30
|
|
|
struct termios initial_settings;
|
2012-09-21 16:34:37 +05:30
|
|
|
int scroll_ofs;
|
2012-09-25 16:18:46 +05:30
|
|
|
#define G_scroll_ofs G.scroll_ofs
|
|
|
|
#else
|
|
|
|
#define G_scroll_ofs 0
|
2007-04-19 20:17:11 +05:30
|
|
|
#endif
|
|
|
|
#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2007-08-29 23:48:08 +05:30
|
|
|
cmp_funcp sort_function[1];
|
2007-04-19 20:17:11 +05:30
|
|
|
#else
|
|
|
|
cmp_funcp sort_function[SORT_DEPTH];
|
|
|
|
struct save_hist *prev_hist;
|
2018-03-07 08:50:22 +05:30
|
|
|
unsigned prev_hist_count;
|
2008-09-25 16:41:37 +05:30
|
|
|
jiffy_counts_t cur_jif, prev_jif;
|
2007-04-19 20:17:11 +05:30
|
|
|
/* int hist_iterations; */
|
|
|
|
unsigned total_pcpu;
|
|
|
|
/* unsigned long total_vsz; */
|
2008-09-25 16:18:06 +05:30
|
|
|
#endif
|
|
|
|
#if ENABLE_FEATURE_TOP_SMP_CPU
|
|
|
|
/* Per CPU samples: current and last */
|
|
|
|
jiffy_counts_t *cpu_jif, *cpu_prev_jif;
|
2018-03-07 08:50:22 +05:30
|
|
|
unsigned num_cpus;
|
2012-09-21 16:34:37 +05:30
|
|
|
#endif
|
2017-01-11 20:57:12 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_INTERACTIVE
|
2012-09-21 16:34:37 +05:30
|
|
|
char kbd_input[KEYCODE_BUFFER_SIZE];
|
2007-04-19 20:17:11 +05:30
|
|
|
#endif
|
2017-08-16 15:10:10 +05:30
|
|
|
char line_buf[LINE_BUF_SIZE];
|
|
|
|
};
|
|
|
|
#define G (*ptr_to_globals)
|
2007-04-19 20:17:11 +05:30
|
|
|
#define top (G.top )
|
|
|
|
#define ntop (G.ntop )
|
2007-09-08 22:21:19 +05:30
|
|
|
#define sort_field (G.sort_field )
|
|
|
|
#define inverted (G.inverted )
|
2008-09-25 16:18:06 +05:30
|
|
|
#define smp_cpu_info (G.smp_cpu_info )
|
2007-09-08 22:21:19 +05:30
|
|
|
#define initial_settings (G.initial_settings )
|
2007-04-19 20:17:11 +05:30
|
|
|
#define sort_function (G.sort_function )
|
|
|
|
#define prev_hist (G.prev_hist )
|
|
|
|
#define prev_hist_count (G.prev_hist_count )
|
2008-09-25 16:41:37 +05:30
|
|
|
#define cur_jif (G.cur_jif )
|
2007-04-19 20:17:11 +05:30
|
|
|
#define prev_jif (G.prev_jif )
|
2008-09-25 16:18:06 +05:30
|
|
|
#define cpu_jif (G.cpu_jif )
|
|
|
|
#define cpu_prev_jif (G.cpu_prev_jif )
|
|
|
|
#define num_cpus (G.num_cpus )
|
2007-04-19 20:17:11 +05:30
|
|
|
#define total_pcpu (G.total_pcpu )
|
2007-09-08 23:04:05 +05:30
|
|
|
#define line_buf (G.line_buf )
|
2015-10-13 20:47:34 +05:30
|
|
|
#define INIT_G() do { \
|
2017-08-16 15:10:10 +05:30
|
|
|
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
|
2015-10-13 20:47:34 +05:30
|
|
|
BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
|
|
|
|
} while (0)
|
2007-08-29 23:48:08 +05:30
|
|
|
|
2008-03-20 01:08:46 +05:30
|
|
|
enum {
|
|
|
|
OPT_d = (1 << 0),
|
|
|
|
OPT_n = (1 << 1),
|
|
|
|
OPT_b = (1 << 2),
|
2019-03-26 17:40:21 +05:30
|
|
|
OPT_H = (1 << 3),
|
|
|
|
OPT_m = (1 << 4),
|
|
|
|
OPT_EOF = (1 << 5), /* pseudo: "we saw EOF in stdin" */
|
2008-03-20 01:08:46 +05:30
|
|
|
};
|
|
|
|
#define OPT_BATCH_MODE (option_mask32 & OPT_b)
|
2002-10-01 02:22:10 +05:30
|
|
|
|
2007-04-19 20:17:11 +05:30
|
|
|
|
2017-01-11 20:57:12 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_INTERACTIVE
|
2006-11-05 06:13:51 +05:30
|
|
|
static int pid_sort(top_status_t *P, top_status_t *Q)
|
2002-10-01 02:22:10 +05:30
|
|
|
{
|
2006-11-05 06:13:51 +05:30
|
|
|
/* Buggy wrt pids with high bit set */
|
|
|
|
/* (linux pids are in [1..2^15-1]) */
|
2006-02-14 03:34:27 +05:30
|
|
|
return (Q->pid - P->pid);
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
2005-07-30 15:12:05 +05:30
|
|
|
#endif
|
2002-10-01 02:22:10 +05:30
|
|
|
|
2006-11-05 06:13:51 +05:30
|
|
|
static int mem_sort(top_status_t *P, top_status_t *Q)
|
2002-10-01 02:22:10 +05:30
|
|
|
{
|
2006-11-05 06:13:51 +05:30
|
|
|
/* We want to avoid unsigned->signed and truncation errors */
|
2007-02-08 13:51:58 +05:30
|
|
|
if (Q->vsz < P->vsz) return -1;
|
|
|
|
return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
|
|
|
|
2006-11-05 06:13:51 +05:30
|
|
|
|
2007-04-19 20:17:11 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2006-11-05 06:13:51 +05:30
|
|
|
|
|
|
|
static int pcpu_sort(top_status_t *P, top_status_t *Q)
|
2002-10-01 02:22:10 +05:30
|
|
|
{
|
2006-11-05 06:13:51 +05:30
|
|
|
/* Buggy wrt ticks with high bit set */
|
|
|
|
/* Affects only processes for which ticks overflow */
|
|
|
|
return (int)Q->pcpu - (int)P->pcpu;
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
|
|
|
|
2006-11-05 06:13:51 +05:30
|
|
|
static int time_sort(top_status_t *P, top_status_t *Q)
|
2002-10-01 02:22:10 +05:30
|
|
|
{
|
2006-11-05 06:13:51 +05:30
|
|
|
/* We want to avoid unsigned->signed and truncation errors */
|
|
|
|
if (Q->ticks < P->ticks) return -1;
|
|
|
|
return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
|
|
|
|
2007-04-17 04:02:04 +05:30
|
|
|
static int mult_lvl_cmp(void* a, void* b)
|
|
|
|
{
|
2006-02-14 03:34:27 +05:30
|
|
|
int i, cmp_val;
|
|
|
|
|
2006-11-05 06:08:51 +05:30
|
|
|
for (i = 0; i < SORT_DEPTH; i++) {
|
2006-02-14 03:34:27 +05:30
|
|
|
cmp_val = (*sort_function[i])(a, b);
|
|
|
|
if (cmp_val != 0)
|
2011-05-07 00:04:04 +05:30
|
|
|
break;
|
2006-02-14 03:34:27 +05:30
|
|
|
}
|
2011-05-07 00:04:04 +05:30
|
|
|
return inverted ? -cmp_val : cmp_val;
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
|
|
|
|
2008-09-25 16:18:06 +05:30
|
|
|
static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
|
|
|
|
{
|
|
|
|
#if !ENABLE_FEATURE_TOP_SMP_CPU
|
2016-04-22 21:39:21 +05:30
|
|
|
static const char fmt[] ALIGN1 = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
|
2008-09-25 16:18:06 +05:30
|
|
|
#else
|
2016-04-22 21:39:21 +05:30
|
|
|
static const char fmt[] ALIGN1 = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
|
2008-09-25 16:18:06 +05:30
|
|
|
#endif
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */)
|
|
|
|
return 0;
|
|
|
|
ret = sscanf(line_buf, fmt,
|
|
|
|
&p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
|
|
|
|
&p_jif->iowait, &p_jif->irq, &p_jif->softirq,
|
|
|
|
&p_jif->steal);
|
2009-03-03 17:25:31 +05:30
|
|
|
if (ret >= 4) {
|
2008-09-25 16:18:06 +05:30
|
|
|
p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle
|
|
|
|
+ p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal;
|
|
|
|
/* procps 2.x does not count iowait as busy time */
|
|
|
|
p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2006-02-14 03:34:27 +05:30
|
|
|
|
2006-04-25 04:43:46 +05:30
|
|
|
static void get_jiffy_counts(void)
|
2006-02-14 03:34:27 +05:30
|
|
|
{
|
2008-07-22 04:35:26 +05:30
|
|
|
FILE* fp = xfopen_for_read("stat");
|
2008-09-25 16:18:06 +05:30
|
|
|
|
2008-09-25 16:41:37 +05:30
|
|
|
/* We need to parse cumulative counts even if SMP CPU display is on,
|
|
|
|
* they are used to calculate per process CPU% */
|
|
|
|
prev_jif = cur_jif;
|
|
|
|
if (read_cpu_jiffy(fp, &cur_jif) < 4)
|
2013-01-22 14:43:52 +05:30
|
|
|
bb_error_msg_and_die("can't read '%s'", "/proc/stat");
|
2008-09-25 16:41:37 +05:30
|
|
|
|
|
|
|
#if !ENABLE_FEATURE_TOP_SMP_CPU
|
2008-09-25 16:18:06 +05:30
|
|
|
fclose(fp);
|
|
|
|
return;
|
|
|
|
#else
|
2008-09-25 16:41:37 +05:30
|
|
|
if (!smp_cpu_info) {
|
2008-09-25 16:18:06 +05:30
|
|
|
fclose(fp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!num_cpus) {
|
|
|
|
/* First time here. How many CPUs?
|
|
|
|
* There will be at least 1 /proc/stat line with cpu%d
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
|
|
|
|
if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
|
|
|
|
break;
|
|
|
|
num_cpus++;
|
|
|
|
}
|
|
|
|
if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */
|
|
|
|
smp_cpu_info = 0;
|
|
|
|
|
|
|
|
cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
|
|
|
|
|
|
|
|
/* Otherwise the first per cpu display shows all 100% idles */
|
|
|
|
usleep(50000);
|
|
|
|
} else { /* Non first time invocation */
|
|
|
|
jiffy_counts_t *tmp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* First switch the sample pointers: no need to copy */
|
|
|
|
tmp = cpu_prev_jif;
|
|
|
|
cpu_prev_jif = cpu_jif;
|
|
|
|
cpu_jif = tmp;
|
|
|
|
|
|
|
|
/* Get the new samples */
|
|
|
|
for (i = 0; i < num_cpus; i++)
|
|
|
|
read_cpu_jiffy(fp, &cpu_jif[i]);
|
2006-02-14 03:34:27 +05:30
|
|
|
}
|
2008-09-25 16:18:06 +05:30
|
|
|
#endif
|
2006-04-25 04:43:46 +05:30
|
|
|
fclose(fp);
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
static void do_stats(void)
|
|
|
|
{
|
2006-11-05 06:13:51 +05:30
|
|
|
top_status_t *cur;
|
2006-11-01 14:46:49 +05:30
|
|
|
pid_t pid;
|
2018-03-07 08:50:22 +05:30
|
|
|
int n;
|
|
|
|
unsigned i, last_i;
|
2006-04-25 04:43:46 +05:30
|
|
|
struct save_hist *new_hist;
|
2002-10-01 02:22:10 +05:30
|
|
|
|
2006-04-25 04:43:46 +05:30
|
|
|
get_jiffy_counts();
|
|
|
|
total_pcpu = 0;
|
2007-02-08 13:51:58 +05:30
|
|
|
/* total_vsz = 0; */
|
2008-09-25 16:18:06 +05:30
|
|
|
new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
|
2006-02-14 03:34:27 +05:30
|
|
|
/*
|
|
|
|
* Make a pass through the data to get stats.
|
|
|
|
*/
|
2006-04-25 04:43:46 +05:30
|
|
|
/* hist_iterations = 0; */
|
|
|
|
i = 0;
|
|
|
|
for (n = 0; n < ntop; n++) {
|
2006-02-14 03:34:27 +05:30
|
|
|
cur = top + n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate time in cur process. Time is sum of user time
|
2006-04-25 04:43:46 +05:30
|
|
|
* and system time
|
2006-02-14 03:34:27 +05:30
|
|
|
*/
|
|
|
|
pid = cur->pid;
|
2006-11-05 06:13:51 +05:30
|
|
|
new_hist[n].ticks = cur->ticks;
|
2006-04-25 04:43:46 +05:30
|
|
|
new_hist[n].pid = pid;
|
2006-02-14 03:34:27 +05:30
|
|
|
|
|
|
|
/* find matching entry from previous pass */
|
2006-04-25 04:43:46 +05:30
|
|
|
cur->pcpu = 0;
|
|
|
|
/* do not start at index 0, continue at last used one
|
|
|
|
* (brought hist_iterations from ~14000 down to 172) */
|
|
|
|
last_i = i;
|
|
|
|
if (prev_hist_count) do {
|
|
|
|
if (prev_hist[i].pid == pid) {
|
2006-11-05 06:13:51 +05:30
|
|
|
cur->pcpu = cur->ticks - prev_hist[i].ticks;
|
|
|
|
total_pcpu += cur->pcpu;
|
2006-02-14 03:34:27 +05:30
|
|
|
break;
|
|
|
|
}
|
2006-04-25 04:43:46 +05:30
|
|
|
i = (i+1) % prev_hist_count;
|
|
|
|
/* hist_iterations++; */
|
|
|
|
} while (i != last_i);
|
2007-02-08 13:51:58 +05:30
|
|
|
/* total_vsz += cur->vsz; */
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-02-14 03:34:27 +05:30
|
|
|
* Save cur frame's information.
|
2002-10-01 02:22:10 +05:30
|
|
|
*/
|
2006-04-25 04:43:46 +05:30
|
|
|
free(prev_hist);
|
|
|
|
prev_hist = new_hist;
|
|
|
|
prev_hist_count = ntop;
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
2008-09-25 16:18:06 +05:30
|
|
|
|
2006-11-05 06:08:51 +05:30
|
|
|
#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
|
2002-10-01 02:22:10 +05:30
|
|
|
|
2007-08-23 16:22:52 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
|
|
|
|
/* formats 7 char string (8 with terminating NUL) */
|
|
|
|
static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
|
|
|
|
{
|
|
|
|
unsigned t;
|
|
|
|
if (value >= total) { /* 100% ? */
|
|
|
|
strcpy(pbuf, " 100% ");
|
|
|
|
return pbuf;
|
|
|
|
}
|
|
|
|
/* else generate " [N/space]N.N% " string */
|
|
|
|
value = 1000 * value / total;
|
|
|
|
t = value / 100;
|
|
|
|
value = value % 100;
|
|
|
|
pbuf[0] = ' ';
|
|
|
|
pbuf[1] = t ? t + '0' : ' ';
|
|
|
|
pbuf[2] = '0' + (value / 10);
|
|
|
|
pbuf[3] = '.';
|
|
|
|
pbuf[4] = '0' + (value % 10);
|
|
|
|
pbuf[5] = '%';
|
|
|
|
pbuf[6] = ' ';
|
|
|
|
pbuf[7] = '\0';
|
|
|
|
return pbuf;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-09-25 16:18:06 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
|
|
|
|
static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
|
|
|
|
{
|
|
|
|
/*
|
2008-09-25 16:41:37 +05:30
|
|
|
* xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
|
2008-09-25 16:18:06 +05:30
|
|
|
*/
|
|
|
|
unsigned total_diff;
|
|
|
|
jiffy_counts_t *p_jif, *p_prev_jif;
|
|
|
|
int i;
|
2009-09-12 02:56:42 +05:30
|
|
|
# if ENABLE_FEATURE_TOP_SMP_CPU
|
2008-09-25 16:18:06 +05:30
|
|
|
int n_cpu_lines;
|
2009-09-12 02:56:42 +05:30
|
|
|
# endif
|
2008-09-25 16:18:06 +05:30
|
|
|
|
|
|
|
/* using (unsigned) casts to make operations cheaper */
|
2009-09-12 18:41:50 +05:30
|
|
|
# define CALC_TOTAL_DIFF do { \
|
|
|
|
total_diff = (unsigned)(p_jif->total - p_prev_jif->total); \
|
|
|
|
if (total_diff == 0) total_diff = 1; \
|
|
|
|
} while (0)
|
2009-09-12 02:56:42 +05:30
|
|
|
|
|
|
|
# if ENABLE_FEATURE_TOP_DECIMALS
|
|
|
|
# define CALC_STAT(xxx) char xxx[8]
|
|
|
|
# define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
|
|
|
|
# define FMT "%s"
|
|
|
|
# else
|
|
|
|
# define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
|
|
|
|
# define SHOW_STAT(xxx) xxx
|
|
|
|
# define FMT "%4u%% "
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# if !ENABLE_FEATURE_TOP_SMP_CPU
|
2008-09-25 16:18:06 +05:30
|
|
|
{
|
|
|
|
i = 1;
|
2008-09-25 16:41:37 +05:30
|
|
|
p_jif = &cur_jif;
|
2008-09-25 16:18:06 +05:30
|
|
|
p_prev_jif = &prev_jif;
|
2009-09-12 02:56:42 +05:30
|
|
|
# else
|
2008-09-25 16:18:06 +05:30
|
|
|
/* Loop thru CPU(s) */
|
|
|
|
n_cpu_lines = smp_cpu_info ? num_cpus : 1;
|
|
|
|
if (n_cpu_lines > *lines_rem_p)
|
|
|
|
n_cpu_lines = *lines_rem_p;
|
|
|
|
|
|
|
|
for (i = 0; i < n_cpu_lines; i++) {
|
|
|
|
p_jif = &cpu_jif[i];
|
|
|
|
p_prev_jif = &cpu_prev_jif[i];
|
2009-09-12 02:56:42 +05:30
|
|
|
# endif
|
2009-09-12 18:41:50 +05:30
|
|
|
CALC_TOTAL_DIFF;
|
2008-09-25 16:18:06 +05:30
|
|
|
|
2008-09-25 16:41:37 +05:30
|
|
|
{ /* Need a block: CALC_STAT are declarations */
|
2008-09-25 16:18:06 +05:30
|
|
|
CALC_STAT(usr);
|
|
|
|
CALC_STAT(sys);
|
|
|
|
CALC_STAT(nic);
|
|
|
|
CALC_STAT(idle);
|
|
|
|
CALC_STAT(iowait);
|
|
|
|
CALC_STAT(irq);
|
|
|
|
CALC_STAT(softirq);
|
|
|
|
/*CALC_STAT(steal);*/
|
|
|
|
|
|
|
|
snprintf(scrbuf, scr_width,
|
|
|
|
/* Barely fits in 79 chars when in "decimals" mode. */
|
2009-09-12 02:56:42 +05:30
|
|
|
# if ENABLE_FEATURE_TOP_SMP_CPU
|
2008-09-25 16:18:06 +05:30
|
|
|
"CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
|
|
|
|
(smp_cpu_info ? utoa(i) : ""),
|
2009-09-12 02:56:42 +05:30
|
|
|
# else
|
2008-09-25 16:18:06 +05:30
|
|
|
"CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
|
2009-09-12 02:56:42 +05:30
|
|
|
# endif
|
2008-09-25 16:18:06 +05:30
|
|
|
SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
|
|
|
|
SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
|
|
|
|
/*, SHOW_STAT(steal) - what is this 'steal' thing? */
|
|
|
|
/* I doubt anyone wants to know it */
|
|
|
|
);
|
|
|
|
puts(scrbuf);
|
|
|
|
}
|
|
|
|
}
|
2009-09-12 02:56:42 +05:30
|
|
|
# undef SHOW_STAT
|
|
|
|
# undef CALC_STAT
|
|
|
|
# undef FMT
|
2008-09-25 16:18:06 +05:30
|
|
|
*lines_rem_p -= i;
|
|
|
|
}
|
|
|
|
#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
|
2009-09-12 02:56:42 +05:30
|
|
|
# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
|
2008-09-25 16:18:06 +05:30
|
|
|
#endif
|
|
|
|
|
2014-07-21 16:44:24 +05:30
|
|
|
enum {
|
|
|
|
MI_MEMTOTAL,
|
|
|
|
MI_MEMFREE,
|
|
|
|
MI_MEMSHARED,
|
|
|
|
MI_SHMEM,
|
|
|
|
MI_BUFFERS,
|
|
|
|
MI_CACHED,
|
|
|
|
MI_SWAPTOTAL,
|
|
|
|
MI_SWAPFREE,
|
|
|
|
MI_DIRTY,
|
|
|
|
MI_WRITEBACK,
|
|
|
|
MI_ANONPAGES,
|
|
|
|
MI_MAPPED,
|
|
|
|
MI_SLAB,
|
|
|
|
MI_MAX
|
|
|
|
};
|
|
|
|
|
|
|
|
static void parse_meminfo(unsigned long meminfo[MI_MAX])
|
2002-09-18 03:44:58 +05:30
|
|
|
{
|
2016-04-22 21:39:21 +05:30
|
|
|
static const char fields[] ALIGN1 =
|
2014-07-21 16:44:24 +05:30
|
|
|
"MemTotal\0"
|
|
|
|
"MemFree\0"
|
|
|
|
"MemShared\0"
|
|
|
|
"Shmem\0"
|
|
|
|
"Buffers\0"
|
|
|
|
"Cached\0"
|
|
|
|
"SwapTotal\0"
|
|
|
|
"SwapFree\0"
|
|
|
|
"Dirty\0"
|
|
|
|
"Writeback\0"
|
|
|
|
"AnonPages\0"
|
|
|
|
"Mapped\0"
|
|
|
|
"Slab\0";
|
|
|
|
char buf[60]; /* actual lines we expect are ~30 chars or less */
|
|
|
|
FILE *f;
|
|
|
|
int i;
|
2007-07-16 00:57:48 +05:30
|
|
|
|
2014-07-29 20:30:30 +05:30
|
|
|
memset(meminfo, 0, sizeof(meminfo[0]) * MI_MAX);
|
2014-07-21 16:44:24 +05:30
|
|
|
f = xfopen_for_read("meminfo");
|
|
|
|
while (fgets(buf, sizeof(buf), f) != NULL) {
|
|
|
|
char *c = strchr(buf, ':');
|
|
|
|
if (!c)
|
|
|
|
continue;
|
|
|
|
*c = '\0';
|
|
|
|
i = index_in_strings(fields, buf);
|
|
|
|
if (i >= 0)
|
|
|
|
meminfo[i] = strtoul(c+1, NULL, 10);
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
2002-09-18 03:44:58 +05:30
|
|
|
|
2014-07-21 16:44:24 +05:30
|
|
|
static unsigned long display_header(int scr_width, int *lines_rem_p)
|
|
|
|
{
|
|
|
|
char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
|
|
|
|
char *buf;
|
|
|
|
unsigned long meminfo[MI_MAX];
|
2003-10-12 00:17:20 +05:30
|
|
|
|
2014-07-21 16:44:24 +05:30
|
|
|
parse_meminfo(meminfo);
|
2004-03-15 13:59:22 +05:30
|
|
|
|
2014-07-21 16:44:24 +05:30
|
|
|
/* Output memory info */
|
2008-05-16 03:00:45 +05:30
|
|
|
if (scr_width > (int)sizeof(scrbuf))
|
2006-04-25 04:43:46 +05:30
|
|
|
scr_width = sizeof(scrbuf);
|
|
|
|
snprintf(scrbuf, scr_width,
|
2007-07-16 00:53:38 +05:30
|
|
|
"Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
|
2014-07-21 16:44:24 +05:30
|
|
|
meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
|
|
|
|
meminfo[MI_MEMFREE],
|
|
|
|
meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
|
|
|
|
meminfo[MI_BUFFERS],
|
|
|
|
meminfo[MI_CACHED]);
|
|
|
|
/* Go to top & clear to the end of screen */
|
2017-09-14 02:18:30 +05:30
|
|
|
printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
|
2008-09-25 16:18:06 +05:30
|
|
|
(*lines_rem_p)--;
|
2007-01-11 22:50:00 +05:30
|
|
|
|
2014-07-21 16:44:24 +05:30
|
|
|
/* Display CPU time split as percentage of total time.
|
|
|
|
* This displays either a cumulative line or one line per CPU.
|
2007-08-17 13:59:48 +05:30
|
|
|
*/
|
2008-09-25 16:18:06 +05:30
|
|
|
display_cpus(scr_width, scrbuf, lines_rem_p);
|
2007-06-10 22:41:59 +05:30
|
|
|
|
2014-07-21 16:44:24 +05:30
|
|
|
/* Read load average as a string */
|
|
|
|
buf = stpcpy(scrbuf, "Load average: ");
|
|
|
|
open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: "));
|
|
|
|
scrbuf[scr_width - 1] = '\0';
|
|
|
|
strchrnul(buf, '\n')[0] = '\0';
|
2007-06-10 22:41:59 +05:30
|
|
|
puts(scrbuf);
|
2008-09-25 16:18:06 +05:30
|
|
|
(*lines_rem_p)--;
|
2006-04-25 04:43:46 +05:30
|
|
|
|
2014-07-21 16:44:24 +05:30
|
|
|
return meminfo[MI_MEMTOTAL];
|
2002-09-18 03:44:58 +05:30
|
|
|
}
|
|
|
|
|
2008-09-25 16:18:06 +05:30
|
|
|
static NOINLINE void display_process_list(int lines_rem, int scr_width)
|
2002-09-18 03:44:58 +05:30
|
|
|
{
|
2006-04-25 04:43:46 +05:30
|
|
|
enum {
|
2008-09-25 16:18:06 +05:30
|
|
|
BITS_PER_INT = sizeof(int) * 8
|
2006-04-25 04:43:46 +05:30
|
|
|
};
|
|
|
|
|
2008-09-25 16:18:06 +05:30
|
|
|
top_status_t *s;
|
|
|
|
unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */
|
2007-06-11 22:01:55 +05:30
|
|
|
/* xxx_shift and xxx_scale variables allow us to replace
|
|
|
|
* expensive divides with multiply and shift */
|
|
|
|
unsigned pmem_shift, pmem_scale, pmem_half;
|
2006-11-05 06:08:51 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2009-10-19 19:37:28 +05:30
|
|
|
unsigned tmp_unsigned;
|
2007-06-11 22:01:55 +05:30
|
|
|
unsigned pcpu_shift, pcpu_scale, pcpu_half;
|
2006-11-05 06:08:51 +05:30
|
|
|
unsigned busy_jifs;
|
2008-09-25 16:18:06 +05:30
|
|
|
#endif
|
2006-04-25 04:43:46 +05:30
|
|
|
|
2002-09-18 03:44:58 +05:30
|
|
|
/* what info of the processes is shown */
|
2017-09-14 02:18:30 +05:30
|
|
|
printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width,
|
2011-01-25 17:18:47 +05:30
|
|
|
" PID PPID USER STAT VSZ %VSZ"
|
2009-11-07 08:36:31 +05:30
|
|
|
IF_FEATURE_TOP_SMP_PROCESS(" CPU")
|
|
|
|
IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
|
2008-09-25 16:18:06 +05:30
|
|
|
" COMMAND");
|
|
|
|
lines_rem--;
|
2002-09-18 03:44:58 +05:30
|
|
|
|
2007-07-16 00:55:01 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_DECIMALS
|
2009-09-12 02:56:42 +05:30
|
|
|
# define UPSCALE 1000
|
|
|
|
# define CALC_STAT(name, val) div_t name = div((val), 10)
|
|
|
|
# define SHOW_STAT(name) name.quot, '0'+name.rem
|
|
|
|
# define FMT "%3u.%c"
|
2007-07-16 00:55:01 +05:30
|
|
|
#else
|
2009-09-12 02:56:42 +05:30
|
|
|
# define UPSCALE 100
|
|
|
|
# define CALC_STAT(name, val) unsigned name = (val)
|
|
|
|
# define SHOW_STAT(name) name
|
|
|
|
# define FMT "%4u%%"
|
2007-07-16 00:55:01 +05:30
|
|
|
#endif
|
2006-04-25 04:43:46 +05:30
|
|
|
/*
|
2011-01-25 17:18:47 +05:30
|
|
|
* %VSZ = s->vsz/MemTotal
|
2006-04-25 04:43:46 +05:30
|
|
|
*/
|
2007-06-11 22:01:55 +05:30
|
|
|
pmem_shift = BITS_PER_INT-11;
|
|
|
|
pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
|
2007-02-08 13:51:58 +05:30
|
|
|
/* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
|
2006-04-25 04:43:46 +05:30
|
|
|
while (pmem_scale >= 512) {
|
|
|
|
pmem_scale /= 4;
|
|
|
|
pmem_shift -= 2;
|
|
|
|
}
|
2011-01-25 06:27:31 +05:30
|
|
|
pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
|
2006-11-05 06:08:51 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2008-09-25 16:41:37 +05:30
|
|
|
busy_jifs = cur_jif.busy - prev_jif.busy;
|
2006-11-05 06:08:51 +05:30
|
|
|
/* This happens if there were lots of short-lived processes
|
|
|
|
* between two top updates (e.g. compilation) */
|
|
|
|
if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
|
|
|
|
|
2006-04-25 04:43:46 +05:30
|
|
|
/*
|
|
|
|
* CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
|
|
|
|
* (pcpu is delta of sys+user time between samples)
|
|
|
|
*/
|
2008-09-25 16:41:37 +05:30
|
|
|
/* (cur_jif.xxx - prev_jif.xxx) and s->pcpu are
|
2006-04-25 04:43:46 +05:30
|
|
|
* in 0..~64000 range (HZ*update_interval).
|
|
|
|
* we assume that unsigned is at least 32-bit.
|
|
|
|
*/
|
|
|
|
pcpu_shift = 6;
|
2009-09-12 18:41:50 +05:30
|
|
|
pcpu_scale = UPSCALE*64 * (uint16_t)busy_jifs;
|
|
|
|
if (pcpu_scale == 0)
|
|
|
|
pcpu_scale = 1;
|
2008-09-25 16:41:37 +05:30
|
|
|
while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
|
2006-04-25 04:43:46 +05:30
|
|
|
pcpu_scale *= 4;
|
|
|
|
pcpu_shift += 2;
|
|
|
|
}
|
2009-09-12 18:41:50 +05:30
|
|
|
tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
|
|
|
|
if (tmp_unsigned != 0)
|
|
|
|
pcpu_scale /= tmp_unsigned;
|
2006-04-25 04:43:46 +05:30
|
|
|
/* we want (s->pcpu * pcpu_scale) to never overflow */
|
|
|
|
while (pcpu_scale >= 1024) {
|
|
|
|
pcpu_scale /= 4;
|
|
|
|
pcpu_shift -= 2;
|
|
|
|
}
|
2011-01-25 06:27:31 +05:30
|
|
|
pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
|
2006-04-25 04:43:46 +05:30
|
|
|
/* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
|
|
|
|
#endif
|
2007-06-11 22:01:55 +05:30
|
|
|
|
2008-02-11 17:14:38 +05:30
|
|
|
/* Ok, all preliminary data is ready, go through the list */
|
2008-09-25 16:18:06 +05:30
|
|
|
scr_width += 2; /* account for leading '\n' and trailing NUL */
|
2012-09-25 16:18:46 +05:30
|
|
|
if (lines_rem > ntop - G_scroll_ofs)
|
|
|
|
lines_rem = ntop - G_scroll_ofs;
|
|
|
|
s = top + G_scroll_ofs;
|
2008-09-25 16:18:06 +05:30
|
|
|
while (--lines_rem >= 0) {
|
2018-03-07 08:29:52 +05:30
|
|
|
char vsz_str_buf[8];
|
2007-09-08 22:21:19 +05:30
|
|
|
unsigned col;
|
2018-03-07 08:29:52 +05:30
|
|
|
|
2007-06-11 22:01:55 +05:30
|
|
|
CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
|
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
|
|
|
|
#endif
|
2004-03-15 13:59:22 +05:30
|
|
|
|
2018-03-07 08:29:52 +05:30
|
|
|
smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy");
|
2011-01-25 17:18:47 +05:30
|
|
|
/* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
|
2007-09-08 23:04:05 +05:30
|
|
|
col = snprintf(line_buf, scr_width,
|
2018-03-07 08:29:52 +05:30
|
|
|
"\n" "%5u%6u %-8.8s %s %.5s" FMT
|
2009-11-07 08:36:31 +05:30
|
|
|
IF_FEATURE_TOP_SMP_PROCESS(" %3d")
|
|
|
|
IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
|
2007-06-11 22:01:55 +05:30
|
|
|
" ",
|
|
|
|
s->pid, s->ppid, get_cached_username(s->uid),
|
|
|
|
s->state, vsz_str_buf,
|
|
|
|
SHOW_STAT(pmem)
|
2009-11-07 08:36:31 +05:30
|
|
|
IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
|
|
|
|
IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
|
2007-06-11 22:01:55 +05:30
|
|
|
);
|
2018-03-07 08:29:52 +05:30
|
|
|
if ((int)(scr_width - col) > 1)
|
2009-09-12 03:45:34 +05:30
|
|
|
read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
|
2007-09-08 23:04:05 +05:30
|
|
|
fputs(line_buf, stdout);
|
2006-05-19 18:18:56 +05:30
|
|
|
/* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
|
2008-09-25 16:41:37 +05:30
|
|
|
cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
|
2002-10-01 02:22:10 +05:30
|
|
|
s++;
|
2002-10-22 17:51:15 +05:30
|
|
|
}
|
2006-04-25 04:43:46 +05:30
|
|
|
/* printf(" %d", hist_iterations); */
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
|
2009-11-02 18:49:51 +05:30
|
|
|
fflush_all();
|
2002-09-18 03:44:58 +05:30
|
|
|
}
|
2007-07-16 00:55:01 +05:30
|
|
|
#undef UPSCALE
|
2007-06-11 22:01:55 +05:30
|
|
|
#undef SHOW_STAT
|
|
|
|
#undef CALC_STAT
|
|
|
|
#undef FMT
|
2002-09-18 03:44:58 +05:30
|
|
|
|
2002-10-22 17:51:15 +05:30
|
|
|
static void clearmems(void)
|
2002-09-18 03:44:58 +05:30
|
|
|
{
|
2006-11-05 06:13:51 +05:30
|
|
|
clear_username_cache();
|
2002-10-01 02:22:10 +05:30
|
|
|
free(top);
|
2007-09-08 22:21:19 +05:30
|
|
|
top = NULL;
|
2002-09-18 03:44:58 +05:30
|
|
|
}
|
|
|
|
|
2017-01-11 20:57:12 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_INTERACTIVE
|
2002-10-01 02:22:10 +05:30
|
|
|
static void reset_term(void)
|
2002-09-18 03:44:58 +05:30
|
|
|
{
|
2012-09-26 20:28:25 +05:30
|
|
|
if (!OPT_BATCH_MODE)
|
|
|
|
tcsetattr_stdin_TCSANOW(&initial_settings);
|
2002-10-01 02:22:10 +05:30
|
|
|
}
|
2004-03-15 13:59:22 +05:30
|
|
|
|
2012-09-27 16:50:34 +05:30
|
|
|
static void sig_catcher(int sig)
|
2002-10-01 02:22:10 +05:30
|
|
|
{
|
|
|
|
reset_term();
|
2012-09-26 20:28:25 +05:30
|
|
|
kill_myself_with_sig(sig);
|
2002-09-18 03:44:58 +05:30
|
|
|
}
|
2017-01-11 20:57:12 +05:30
|
|
|
#endif /* FEATURE_TOP_INTERACTIVE */
|
2002-10-01 02:22:10 +05:30
|
|
|
|
2007-09-08 22:51:01 +05:30
|
|
|
/*
|
|
|
|
* TOPMEM support
|
|
|
|
*/
|
2007-09-08 22:21:19 +05:30
|
|
|
|
|
|
|
typedef unsigned long mem_t;
|
|
|
|
|
|
|
|
typedef struct topmem_status_t {
|
|
|
|
unsigned pid;
|
|
|
|
char comm[COMM_LEN];
|
|
|
|
/* vsz doesn't count /dev/xxx mappings except /dev/zero */
|
|
|
|
mem_t vsz ;
|
|
|
|
mem_t vszrw ;
|
|
|
|
mem_t rss ;
|
|
|
|
mem_t rss_sh ;
|
|
|
|
mem_t dirty ;
|
|
|
|
mem_t dirty_sh;
|
|
|
|
mem_t stack ;
|
|
|
|
} topmem_status_t;
|
|
|
|
|
|
|
|
enum { NUM_SORT_FIELD = 7 };
|
|
|
|
|
|
|
|
#define topmem ((topmem_status_t*)top)
|
|
|
|
|
|
|
|
#if ENABLE_FEATURE_TOPMEM
|
2008-09-25 16:41:37 +05:30
|
|
|
|
2007-09-08 22:21:19 +05:30
|
|
|
static int topmem_sort(char *a, char *b)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
mem_t l, r;
|
|
|
|
|
|
|
|
n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
|
|
|
|
l = *(mem_t*)(a + n);
|
|
|
|
r = *(mem_t*)(b + n);
|
2010-07-13 15:43:04 +05:30
|
|
|
if (l == r) {
|
|
|
|
l = ((topmem_status_t*)a)->dirty;
|
|
|
|
r = ((topmem_status_t*)b)->dirty;
|
|
|
|
}
|
2007-09-08 22:21:19 +05:30
|
|
|
/* We want to avoid unsigned->signed and truncation errors */
|
|
|
|
/* l>r: -1, l=r: 0, l<r: 1 */
|
|
|
|
n = (l > r) ? -1 : (l != r);
|
|
|
|
return inverted ? -n : n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* display header info (meminfo / loadavg) */
|
2008-09-25 16:18:06 +05:30
|
|
|
static void display_topmem_header(int scr_width, int *lines_rem_p)
|
2007-09-08 22:21:19 +05:30
|
|
|
{
|
2014-07-21 16:44:24 +05:30
|
|
|
unsigned long meminfo[MI_MAX];
|
|
|
|
|
|
|
|
parse_meminfo(meminfo);
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2010-06-06 21:10:54 +05:30
|
|
|
snprintf(line_buf, LINE_BUF_SIZE,
|
2014-07-21 16:44:24 +05:30
|
|
|
"Mem total:%lu anon:%lu map:%lu free:%lu",
|
|
|
|
meminfo[MI_MEMTOTAL],
|
|
|
|
meminfo[MI_ANONPAGES],
|
|
|
|
meminfo[MI_MAPPED],
|
|
|
|
meminfo[MI_MEMFREE]);
|
2017-09-14 02:18:30 +05:30
|
|
|
printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf);
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2010-06-06 21:10:54 +05:30
|
|
|
snprintf(line_buf, LINE_BUF_SIZE,
|
2014-07-21 16:44:24 +05:30
|
|
|
" slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
|
|
|
|
meminfo[MI_SLAB],
|
|
|
|
meminfo[MI_BUFFERS],
|
|
|
|
meminfo[MI_CACHED],
|
|
|
|
meminfo[MI_DIRTY],
|
|
|
|
meminfo[MI_WRITEBACK]);
|
2010-06-06 21:10:54 +05:30
|
|
|
printf("%.*s\n", scr_width, line_buf);
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2010-06-06 21:10:54 +05:30
|
|
|
snprintf(line_buf, LINE_BUF_SIZE,
|
2014-07-21 16:44:24 +05:30
|
|
|
"Swap total:%lu free:%lu", // TODO: % used?
|
|
|
|
meminfo[MI_SWAPTOTAL],
|
|
|
|
meminfo[MI_SWAPFREE]);
|
2010-06-06 21:10:54 +05:30
|
|
|
printf("%.*s\n", scr_width, line_buf);
|
2008-09-25 16:18:06 +05:30
|
|
|
|
|
|
|
(*lines_rem_p) -= 3;
|
2007-09-08 22:21:19 +05:30
|
|
|
}
|
|
|
|
|
2008-01-06 08:56:53 +05:30
|
|
|
static void ulltoa6_and_space(unsigned long long ul, char buf[6])
|
2007-09-08 22:21:19 +05:30
|
|
|
{
|
2008-01-06 08:56:53 +05:30
|
|
|
/* see http://en.wikipedia.org/wiki/Tera */
|
2013-09-06 16:29:48 +05:30
|
|
|
smart_ulltoa5(ul, buf, " mgtpezy")[0] = ' ';
|
2007-09-08 22:21:19 +05:30
|
|
|
}
|
|
|
|
|
2008-09-25 16:18:06 +05:30
|
|
|
static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
|
2007-09-08 22:21:19 +05:30
|
|
|
{
|
|
|
|
#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
|
|
|
|
#define MIN_WIDTH sizeof(HDR_STR)
|
2012-09-25 16:18:46 +05:30
|
|
|
const topmem_status_t *s = topmem + G_scroll_ofs;
|
2015-10-15 01:59:52 +05:30
|
|
|
char *cp, ch;
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2008-09-25 16:18:06 +05:30
|
|
|
display_topmem_header(scr_width, &lines_rem);
|
2015-10-15 01:59:52 +05:30
|
|
|
|
2007-09-08 23:04:05 +05:30
|
|
|
strcpy(line_buf, HDR_STR " COMMAND");
|
2015-10-15 01:59:52 +05:30
|
|
|
/* Mark the ^FIELD^ we sort by */
|
|
|
|
cp = &line_buf[5 + sort_field * 6];
|
|
|
|
ch = "^_"[inverted];
|
|
|
|
cp[6] = ch;
|
|
|
|
do *cp++ = ch; while (*cp == ' ');
|
|
|
|
|
2017-09-14 02:18:30 +05:30
|
|
|
printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf);
|
2008-09-25 16:18:06 +05:30
|
|
|
lines_rem--;
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2012-09-25 16:18:46 +05:30
|
|
|
if (lines_rem > ntop - G_scroll_ofs)
|
|
|
|
lines_rem = ntop - G_scroll_ofs;
|
2008-09-25 16:18:06 +05:30
|
|
|
while (--lines_rem >= 0) {
|
|
|
|
/* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
|
2008-01-06 08:56:53 +05:30
|
|
|
ulltoa6_and_space(s->pid , &line_buf[0*6]);
|
|
|
|
ulltoa6_and_space(s->vsz , &line_buf[1*6]);
|
|
|
|
ulltoa6_and_space(s->vszrw , &line_buf[2*6]);
|
|
|
|
ulltoa6_and_space(s->rss , &line_buf[3*6]);
|
|
|
|
ulltoa6_and_space(s->rss_sh , &line_buf[4*6]);
|
|
|
|
ulltoa6_and_space(s->dirty , &line_buf[5*6]);
|
|
|
|
ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
|
|
|
|
ulltoa6_and_space(s->stack , &line_buf[7*6]);
|
2007-09-08 23:04:05 +05:30
|
|
|
line_buf[8*6] = '\0';
|
2008-05-16 03:00:45 +05:30
|
|
|
if (scr_width > (int)MIN_WIDTH) {
|
2007-09-08 23:04:05 +05:30
|
|
|
read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
|
2007-09-08 22:21:19 +05:30
|
|
|
}
|
2007-09-08 23:04:05 +05:30
|
|
|
printf("\n""%.*s", scr_width, line_buf);
|
2007-09-08 22:21:19 +05:30
|
|
|
s++;
|
|
|
|
}
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
|
2009-11-02 18:49:51 +05:30
|
|
|
fflush_all();
|
2007-09-08 22:21:19 +05:30
|
|
|
#undef HDR_STR
|
|
|
|
#undef MIN_WIDTH
|
|
|
|
}
|
2008-09-25 16:41:37 +05:30
|
|
|
|
2007-09-08 22:21:19 +05:30
|
|
|
#else
|
2008-09-25 16:18:06 +05:30
|
|
|
void display_topmem_process_list(int lines_rem, int scr_width);
|
2007-09-08 22:21:19 +05:30
|
|
|
int topmem_sort(char *a, char *b);
|
|
|
|
#endif /* TOPMEM */
|
|
|
|
|
2007-09-08 22:51:01 +05:30
|
|
|
/*
|
|
|
|
* end TOPMEM support
|
|
|
|
*/
|
2007-09-08 22:21:19 +05:30
|
|
|
|
|
|
|
enum {
|
|
|
|
TOP_MASK = 0
|
|
|
|
| PSSCAN_PID
|
|
|
|
| PSSCAN_PPID
|
|
|
|
| PSSCAN_VSZ
|
|
|
|
| PSSCAN_STIME
|
|
|
|
| PSSCAN_UTIME
|
|
|
|
| PSSCAN_STATE
|
|
|
|
| PSSCAN_COMM
|
2008-09-25 16:18:06 +05:30
|
|
|
| PSSCAN_CPU
|
2007-09-08 22:21:19 +05:30
|
|
|
| PSSCAN_UIDGID,
|
|
|
|
TOPMEM_MASK = 0
|
|
|
|
| PSSCAN_PID
|
|
|
|
| PSSCAN_SMAPS
|
|
|
|
| PSSCAN_COMM,
|
2018-03-07 09:17:52 +05:30
|
|
|
EXIT_MASK = 0,
|
|
|
|
NO_RESCAN_MASK = (unsigned)-1,
|
2007-09-08 22:21:19 +05:30
|
|
|
};
|
|
|
|
|
2017-01-11 20:57:12 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_INTERACTIVE
|
2018-08-03 21:47:12 +05:30
|
|
|
static unsigned handle_input(unsigned scan_mask, duration_t interval)
|
2011-05-07 00:04:04 +05:30
|
|
|
{
|
2012-09-26 20:28:25 +05:30
|
|
|
if (option_mask32 & OPT_EOF) {
|
|
|
|
/* EOF on stdin ("top </dev/null") */
|
2018-08-03 21:47:12 +05:30
|
|
|
sleep_for_duration(interval);
|
2012-09-26 20:28:25 +05:30
|
|
|
return scan_mask;
|
|
|
|
}
|
|
|
|
|
2011-05-07 00:17:54 +05:30
|
|
|
while (1) {
|
2012-09-21 16:34:37 +05:30
|
|
|
int32_t c;
|
2011-05-07 00:17:54 +05:30
|
|
|
|
2012-09-21 16:34:37 +05:30
|
|
|
c = read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
|
|
|
|
if (c == -1 && errno != EAGAIN) {
|
|
|
|
/* error/EOF */
|
2011-05-07 00:17:54 +05:30
|
|
|
option_mask32 |= OPT_EOF;
|
2012-09-21 16:34:37 +05:30
|
|
|
break;
|
2011-05-07 00:17:54 +05:30
|
|
|
}
|
2012-09-21 16:34:37 +05:30
|
|
|
interval = 0;
|
2011-05-07 00:04:04 +05:30
|
|
|
|
|
|
|
if (c == initial_settings.c_cc[VINTR])
|
|
|
|
return EXIT_MASK;
|
|
|
|
if (c == initial_settings.c_cc[VEOF])
|
|
|
|
return EXIT_MASK;
|
2012-09-21 16:34:37 +05:30
|
|
|
|
|
|
|
if (c == KEYCODE_UP) {
|
2012-09-25 16:18:46 +05:30
|
|
|
G_scroll_ofs--;
|
2012-09-21 16:34:37 +05:30
|
|
|
goto normalize_ofs;
|
|
|
|
}
|
|
|
|
if (c == KEYCODE_DOWN) {
|
2012-09-25 16:18:46 +05:30
|
|
|
G_scroll_ofs++;
|
2012-09-21 16:34:37 +05:30
|
|
|
goto normalize_ofs;
|
|
|
|
}
|
|
|
|
if (c == KEYCODE_HOME) {
|
2012-09-25 16:18:46 +05:30
|
|
|
G_scroll_ofs = 0;
|
2018-03-07 09:17:52 +05:30
|
|
|
goto normalize_ofs;
|
2012-09-21 16:34:37 +05:30
|
|
|
}
|
|
|
|
if (c == KEYCODE_END) {
|
2012-09-25 16:18:46 +05:30
|
|
|
G_scroll_ofs = ntop - G.lines / 2;
|
2012-09-21 16:34:37 +05:30
|
|
|
goto normalize_ofs;
|
|
|
|
}
|
|
|
|
if (c == KEYCODE_PAGEUP) {
|
2012-09-25 16:18:46 +05:30
|
|
|
G_scroll_ofs -= G.lines / 2;
|
2012-09-21 16:34:37 +05:30
|
|
|
goto normalize_ofs;
|
|
|
|
}
|
|
|
|
if (c == KEYCODE_PAGEDOWN) {
|
2012-09-25 16:18:46 +05:30
|
|
|
G_scroll_ofs += G.lines / 2;
|
2012-09-21 16:34:37 +05:30
|
|
|
normalize_ofs:
|
2012-09-25 16:18:46 +05:30
|
|
|
if (G_scroll_ofs >= ntop)
|
|
|
|
G_scroll_ofs = ntop - 1;
|
|
|
|
if (G_scroll_ofs < 0)
|
|
|
|
G_scroll_ofs = 0;
|
2018-03-07 09:17:52 +05:30
|
|
|
return NO_RESCAN_MASK;
|
2012-09-21 16:34:37 +05:30
|
|
|
}
|
|
|
|
|
2011-05-07 00:04:04 +05:30
|
|
|
c |= 0x20; /* lowercase */
|
|
|
|
if (c == 'q')
|
|
|
|
return EXIT_MASK;
|
2011-05-07 00:17:54 +05:30
|
|
|
|
2011-05-07 00:04:04 +05:30
|
|
|
if (c == 'n') {
|
|
|
|
IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
|
|
|
|
sort_function[0] = pid_sort;
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
if (c == 'm') {
|
|
|
|
IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
|
|
|
|
sort_function[0] = mem_sort;
|
|
|
|
# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
sort_function[1] = pcpu_sort;
|
|
|
|
sort_function[2] = time_sort;
|
|
|
|
# endif
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
# if ENABLE_FEATURE_SHOW_THREADS
|
|
|
|
if (c == 'h'
|
2013-01-14 20:27:44 +05:30
|
|
|
IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
|
2011-05-07 00:04:04 +05:30
|
|
|
) {
|
|
|
|
scan_mask ^= PSSCAN_TASKS;
|
2020-06-23 06:43:55 +05:30
|
|
|
# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2019-08-11 19:47:11 +05:30
|
|
|
free(prev_hist);
|
|
|
|
prev_hist = NULL;
|
|
|
|
prev_hist_count = 0;
|
2020-06-23 06:43:55 +05:30
|
|
|
# endif
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
# endif
|
|
|
|
# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
if (c == 'p') {
|
|
|
|
IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
|
|
|
|
sort_function[0] = pcpu_sort;
|
|
|
|
sort_function[1] = mem_sort;
|
|
|
|
sort_function[2] = time_sort;
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
if (c == 't') {
|
|
|
|
IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
|
|
|
|
sort_function[0] = time_sort;
|
|
|
|
sort_function[1] = mem_sort;
|
|
|
|
sort_function[2] = pcpu_sort;
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
# if ENABLE_FEATURE_TOPMEM
|
|
|
|
if (c == 's') {
|
|
|
|
scan_mask = TOPMEM_MASK;
|
2019-08-11 19:47:11 +05:30
|
|
|
sort_field = (sort_field + 1) % NUM_SORT_FIELD;
|
2011-05-07 00:04:04 +05:30
|
|
|
free(prev_hist);
|
|
|
|
prev_hist = NULL;
|
|
|
|
prev_hist_count = 0;
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
# endif
|
2011-05-07 00:17:54 +05:30
|
|
|
if (c == 'r') {
|
2011-05-07 00:04:04 +05:30
|
|
|
inverted ^= 1;
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
|
|
|
}
|
2011-05-07 00:04:04 +05:30
|
|
|
# if ENABLE_FEATURE_TOP_SMP_CPU
|
|
|
|
/* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
|
|
|
|
if (c == 'c' || c == '1') {
|
|
|
|
/* User wants to toggle per cpu <> aggregate */
|
|
|
|
if (smp_cpu_info) {
|
|
|
|
free(cpu_prev_jif);
|
|
|
|
free(cpu_jif);
|
|
|
|
cpu_jif = &cur_jif;
|
|
|
|
cpu_prev_jif = &prev_jif;
|
|
|
|
} else {
|
|
|
|
/* Prepare for xrealloc() */
|
|
|
|
cpu_jif = cpu_prev_jif = NULL;
|
|
|
|
}
|
|
|
|
num_cpus = 0;
|
|
|
|
smp_cpu_info = !smp_cpu_info;
|
|
|
|
get_jiffy_counts();
|
2011-05-07 00:17:54 +05:30
|
|
|
continue;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
# endif
|
|
|
|
# endif
|
2011-05-07 00:17:54 +05:30
|
|
|
break; /* unknown key -> force refresh */
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return scan_mask;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-01-13 20:37:51 +05:30
|
|
|
//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
|
|
|
|
//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
|
|
|
|
//usage:#else
|
|
|
|
//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...)
|
|
|
|
//usage:#endif
|
|
|
|
//usage:#define top_trivial_usage
|
2019-03-26 17:40:21 +05:30
|
|
|
//usage: "[-b"IF_FEATURE_TOPMEM("m")IF_FEATURE_SHOW_THREADS("H")"]"
|
|
|
|
//usage: " [-n COUNT] [-d SECONDS]"
|
2011-01-13 20:37:51 +05:30
|
|
|
//usage:#define top_full_usage "\n\n"
|
|
|
|
//usage: "Provide a view of process activity in real time."
|
|
|
|
//usage: "\n""Read the status of all processes from /proc each SECONDS"
|
|
|
|
//usage: "\n""and display a screenful of them."
|
2016-12-03 16:39:50 +05:30
|
|
|
//usage: "\n"
|
2017-01-11 20:57:12 +05:30
|
|
|
//usage: IF_FEATURE_TOP_INTERACTIVE(
|
2016-12-03 16:39:50 +05:30
|
|
|
//usage: "Keys:"
|
2011-01-13 20:37:51 +05:30
|
|
|
//usage: "\n"" N/M"
|
|
|
|
//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P")
|
|
|
|
//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T")
|
|
|
|
//usage: ": " IF_FEATURE_TOPMEM("show CPU usage, ") "sort by pid/mem"
|
|
|
|
//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
|
|
|
|
//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
|
|
|
|
//usage: IF_FEATURE_TOPMEM(
|
2011-05-07 00:04:04 +05:30
|
|
|
//usage: "\n"" S: show memory"
|
2011-01-13 20:37:51 +05:30
|
|
|
//usage: )
|
2011-05-07 00:04:04 +05:30
|
|
|
//usage: "\n"" R: reverse sort"
|
2011-01-13 20:37:51 +05:30
|
|
|
//usage: IF_SHOW_THREADS_OR_TOP_SMP(
|
|
|
|
//usage: "\n"" "
|
|
|
|
//usage: IF_FEATURE_SHOW_THREADS("H: toggle threads")
|
|
|
|
//usage: IF_FEATURE_SHOW_THREADS(IF_FEATURE_TOP_SMP_CPU(", "))
|
|
|
|
//usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
|
|
|
|
//usage: )
|
|
|
|
//usage: "\n"" Q,^C: exit"
|
2011-05-07 00:04:04 +05:30
|
|
|
//usage: "\n""Options:"
|
2016-12-03 16:39:50 +05:30
|
|
|
//usage: )
|
2011-05-07 00:04:04 +05:30
|
|
|
//usage: "\n"" -b Batch mode"
|
|
|
|
//usage: "\n"" -n N Exit after N iterations"
|
2019-02-27 21:15:39 +05:30
|
|
|
//usage: "\n"" -d SEC Delay between updates"
|
2011-05-07 00:04:04 +05:30
|
|
|
//usage: IF_FEATURE_TOPMEM(
|
|
|
|
//usage: "\n"" -m Same as 's' key"
|
|
|
|
//usage: )
|
2019-03-26 17:40:21 +05:30
|
|
|
//usage: IF_FEATURE_SHOW_THREADS(
|
|
|
|
//usage: "\n"" -H Show threads"
|
|
|
|
//usage: )
|
2011-05-07 00:04:04 +05:30
|
|
|
|
|
|
|
/* Interactive testing:
|
|
|
|
* echo sss | ./busybox top
|
|
|
|
* - shows memory screen
|
|
|
|
* echo sss | ./busybox top -bn1 >mem
|
2011-05-07 00:17:54 +05:30
|
|
|
* - saves memory screen - the *whole* list, not first NROWS processes!
|
|
|
|
* echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
|
|
|
|
* - saves several different screens, and exits
|
2011-05-07 00:04:04 +05:30
|
|
|
*
|
|
|
|
* TODO: -i STRING param as a better alternative?
|
|
|
|
*/
|
2011-01-13 20:37:51 +05:30
|
|
|
|
2007-10-11 15:35:36 +05:30
|
|
|
int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
2008-07-05 14:48:54 +05:30
|
|
|
int top_main(int argc UNUSED_PARAM, char **argv)
|
2002-09-18 03:44:58 +05:30
|
|
|
{
|
2018-08-03 21:47:12 +05:30
|
|
|
duration_t interval;
|
2007-09-08 22:51:01 +05:30
|
|
|
int iterations;
|
2012-09-21 16:34:37 +05:30
|
|
|
unsigned col;
|
2008-09-25 17:12:10 +05:30
|
|
|
char *str_interval, *str_iterations;
|
2009-09-22 03:28:43 +05:30
|
|
|
unsigned scan_mask = TOP_MASK;
|
2002-10-01 02:22:10 +05:30
|
|
|
|
2007-09-08 22:51:01 +05:30
|
|
|
INIT_G();
|
|
|
|
|
2008-03-20 01:08:46 +05:30
|
|
|
interval = 5; /* default update interval is 5 seconds */
|
2007-09-08 22:51:01 +05:30
|
|
|
iterations = 0; /* infinite */
|
2008-09-25 16:18:06 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_SMP_CPU
|
|
|
|
/*num_cpus = 0;*/
|
|
|
|
/*smp_cpu_info = 0;*/ /* to start with show aggregate */
|
2008-09-25 16:41:37 +05:30
|
|
|
cpu_jif = &cur_jif;
|
2008-09-25 16:18:06 +05:30
|
|
|
cpu_prev_jif = &prev_jif;
|
|
|
|
#endif
|
2007-04-19 20:17:11 +05:30
|
|
|
|
2008-03-20 01:08:46 +05:30
|
|
|
/* all args are options; -n NUM */
|
2017-08-04 19:53:42 +05:30
|
|
|
make_all_argv_opts(argv); /* options can be specified w/o dash */
|
2019-03-26 17:40:21 +05:30
|
|
|
col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
|
|
|
|
/* NB: -m and -H are accepted even if not configured */
|
2009-09-12 02:56:42 +05:30
|
|
|
#if ENABLE_FEATURE_TOPMEM
|
|
|
|
if (col & OPT_m) /* -m (busybox specific) */
|
|
|
|
scan_mask = TOPMEM_MASK;
|
|
|
|
#endif
|
2008-09-25 17:12:10 +05:30
|
|
|
if (col & OPT_d) {
|
2017-08-04 19:53:42 +05:30
|
|
|
/* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
|
2008-09-25 17:12:10 +05:30
|
|
|
if (str_interval[0] == '-')
|
|
|
|
str_interval++;
|
2018-08-03 21:47:12 +05:30
|
|
|
interval = parse_duration_str(str_interval);
|
2007-08-29 01:05:34 +05:30
|
|
|
/* Need to limit it to not overflow poll timeout */
|
2018-08-03 21:47:12 +05:30
|
|
|
if (interval > INT_MAX / 1000)
|
|
|
|
interval = INT_MAX / 1000;
|
2008-09-25 17:12:10 +05:30
|
|
|
}
|
|
|
|
if (col & OPT_n) {
|
|
|
|
if (str_iterations[0] == '-')
|
|
|
|
str_iterations++;
|
|
|
|
iterations = xatou(str_iterations);
|
2007-08-29 01:05:34 +05:30
|
|
|
}
|
2019-03-26 17:40:21 +05:30
|
|
|
#if ENABLE_FEATURE_SHOW_THREADS
|
|
|
|
if (col & OPT_H) {
|
|
|
|
scan_mask |= PSSCAN_TASKS;
|
|
|
|
}
|
|
|
|
#endif
|
2002-09-18 03:44:58 +05:30
|
|
|
|
2002-10-22 17:51:15 +05:30
|
|
|
/* change to /proc */
|
2006-08-03 21:11:12 +05:30
|
|
|
xchdir("/proc");
|
2005-07-30 15:12:05 +05:30
|
|
|
|
2006-11-05 06:08:51 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2002-10-01 02:22:10 +05:30
|
|
|
sort_function[0] = pcpu_sort;
|
|
|
|
sort_function[1] = mem_sort;
|
|
|
|
sort_function[2] = time_sort;
|
|
|
|
#else
|
2007-08-29 23:48:08 +05:30
|
|
|
sort_function[0] = mem_sort;
|
2009-09-12 02:56:42 +05:30
|
|
|
#endif
|
2005-07-30 15:12:05 +05:30
|
|
|
|
2012-09-26 20:28:25 +05:30
|
|
|
if (OPT_BATCH_MODE) {
|
|
|
|
option_mask32 |= OPT_EOF;
|
|
|
|
}
|
2017-01-11 20:57:12 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_INTERACTIVE
|
2012-09-26 20:28:25 +05:30
|
|
|
else {
|
2017-01-11 20:47:59 +05:30
|
|
|
/* Turn on unbuffered input; turn off echoing, ^C ^Z etc */
|
|
|
|
set_termios_to_raw(STDIN_FILENO, &initial_settings, TERMIOS_CLEAR_ISIG);
|
2017-08-16 21:15:32 +05:30
|
|
|
die_func = reset_term;
|
2011-05-07 00:04:04 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
bb_signals(BB_FATAL_SIGS, sig_catcher);
|
|
|
|
|
|
|
|
/* Eat initial input, if any */
|
|
|
|
scan_mask = handle_input(scan_mask, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (scan_mask != EXIT_MASK) {
|
2018-03-20 00:30:10 +05:30
|
|
|
IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;)
|
2006-11-05 06:13:51 +05:30
|
|
|
procps_status_t *p = NULL;
|
2002-10-22 17:51:15 +05:30
|
|
|
|
2011-05-07 00:04:04 +05:30
|
|
|
if (OPT_BATCH_MODE) {
|
2012-09-21 16:34:37 +05:30
|
|
|
G.lines = INT_MAX;
|
2011-05-07 00:04:04 +05:30
|
|
|
col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
|
|
|
|
} else {
|
2012-09-21 16:34:37 +05:30
|
|
|
G.lines = 24; /* default */
|
2011-05-07 00:04:04 +05:30
|
|
|
col = 79;
|
|
|
|
/* We output to stdout, we need size of stdout (not stdin)! */
|
2012-09-21 16:34:37 +05:30
|
|
|
get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
|
|
|
|
if (G.lines < 5 || col < 10) {
|
2018-08-03 21:47:12 +05:30
|
|
|
sleep_for_duration(interval);
|
2011-05-07 00:04:04 +05:30
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (col > LINE_BUF_SIZE - 2)
|
|
|
|
col = LINE_BUF_SIZE - 2;
|
|
|
|
}
|
2006-04-25 04:43:46 +05:30
|
|
|
|
|
|
|
/* read process IDs & status for all the processes */
|
2012-09-21 16:34:37 +05:30
|
|
|
ntop = 0;
|
2007-09-08 22:21:19 +05:30
|
|
|
while ((p = procps_scan(p, scan_mask)) != NULL) {
|
|
|
|
int n;
|
2015-10-15 01:59:52 +05:30
|
|
|
|
|
|
|
IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
|
2007-09-08 22:21:19 +05:30
|
|
|
n = ntop;
|
2008-08-04 18:50:36 +05:30
|
|
|
top = xrealloc_vector(top, 6, ntop++);
|
2007-09-08 22:21:19 +05:30
|
|
|
top[n].pid = p->pid;
|
|
|
|
top[n].ppid = p->ppid;
|
|
|
|
top[n].vsz = p->vsz;
|
2006-12-30 23:27:03 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2007-09-08 22:21:19 +05:30
|
|
|
top[n].ticks = p->stime + p->utime;
|
2006-12-30 23:27:03 +05:30
|
|
|
#endif
|
2007-09-08 22:21:19 +05:30
|
|
|
top[n].uid = p->uid;
|
|
|
|
strcpy(top[n].state, p->state);
|
|
|
|
strcpy(top[n].comm, p->comm);
|
2008-09-25 16:18:06 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_SMP_PROCESS
|
|
|
|
top[n].last_seen_on_cpu = p->last_seen_on_cpu;
|
|
|
|
#endif
|
2009-09-20 01:59:42 +05:30
|
|
|
}
|
2007-09-08 22:21:19 +05:30
|
|
|
#if ENABLE_FEATURE_TOPMEM
|
2009-09-20 01:59:42 +05:30
|
|
|
else { /* TOPMEM */
|
2010-08-29 02:50:34 +05:30
|
|
|
if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
|
2007-09-08 22:21:19 +05:30
|
|
|
continue; /* kernel threads are ignored */
|
|
|
|
n = ntop;
|
2008-07-08 10:44:36 +05:30
|
|
|
/* No bug here - top and topmem are the same */
|
2008-08-04 18:50:36 +05:30
|
|
|
top = xrealloc_vector(topmem, 6, ntop++);
|
2007-09-08 22:21:19 +05:30
|
|
|
strcpy(topmem[n].comm, p->comm);
|
|
|
|
topmem[n].pid = p->pid;
|
2010-08-29 02:50:34 +05:30
|
|
|
topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro;
|
|
|
|
topmem[n].vszrw = p->smaps.mapped_rw;
|
|
|
|
topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty;
|
|
|
|
topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
|
|
|
|
topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty;
|
|
|
|
topmem[n].dirty_sh = p->smaps.shared_dirty;
|
|
|
|
topmem[n].stack = p->smaps.stack;
|
2007-09-08 22:21:19 +05:30
|
|
|
}
|
2009-09-20 01:59:42 +05:30
|
|
|
#endif
|
2008-03-20 01:08:46 +05:30
|
|
|
} /* end of "while we read /proc" */
|
2002-10-22 17:51:15 +05:30
|
|
|
if (ntop == 0) {
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_error_msg("no process info in /proc");
|
2008-03-20 01:08:46 +05:30
|
|
|
break;
|
2006-02-14 03:34:27 +05:30
|
|
|
}
|
2007-09-08 22:21:19 +05:30
|
|
|
|
2015-10-15 01:59:52 +05:30
|
|
|
IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
|
2006-11-05 06:08:51 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
2007-09-08 22:21:19 +05:30
|
|
|
if (!prev_hist_count) {
|
|
|
|
do_stats();
|
|
|
|
usleep(100000);
|
|
|
|
clearmems();
|
|
|
|
continue;
|
|
|
|
}
|
2002-10-01 02:22:10 +05:30
|
|
|
do_stats();
|
2008-09-25 16:18:06 +05:30
|
|
|
/* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
|
2007-09-08 22:21:19 +05:30
|
|
|
qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
|
2002-10-01 02:22:10 +05:30
|
|
|
#else
|
2007-09-08 22:21:19 +05:30
|
|
|
qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
|
2009-09-12 02:56:42 +05:30
|
|
|
#endif
|
2008-04-17 23:34:38 +05:30
|
|
|
}
|
|
|
|
#if ENABLE_FEATURE_TOPMEM
|
|
|
|
else { /* TOPMEM */
|
2007-09-08 22:21:19 +05:30
|
|
|
qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
|
2018-03-07 09:17:52 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
IF_FEATURE_TOP_INTERACTIVE(display:)
|
|
|
|
IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
|
|
|
|
display_process_list(G.lines, col);
|
|
|
|
}
|
|
|
|
#if ENABLE_FEATURE_TOPMEM
|
|
|
|
else { /* TOPMEM */
|
2012-09-21 16:34:37 +05:30
|
|
|
display_topmem_process_list(G.lines, col);
|
2015-10-15 01:59:52 +05:30
|
|
|
}
|
2008-04-17 23:34:38 +05:30
|
|
|
#endif
|
2007-08-29 23:48:08 +05:30
|
|
|
if (iterations >= 0 && !--iterations)
|
|
|
|
break;
|
2017-01-11 20:57:12 +05:30
|
|
|
#if !ENABLE_FEATURE_TOP_INTERACTIVE
|
2018-03-07 09:17:52 +05:30
|
|
|
clearmems();
|
2018-08-03 21:47:12 +05:30
|
|
|
sleep_for_duration(interval);
|
2007-08-29 23:48:08 +05:30
|
|
|
#else
|
2018-03-07 09:17:52 +05:30
|
|
|
new_mask = handle_input(scan_mask, interval);
|
|
|
|
if (new_mask == NO_RESCAN_MASK)
|
|
|
|
goto display;
|
|
|
|
scan_mask = new_mask;
|
|
|
|
clearmems();
|
2015-10-15 01:59:52 +05:30
|
|
|
#endif
|
2011-05-07 00:04:04 +05:30
|
|
|
} /* end of "while (not Q)" */
|
2008-03-20 01:08:46 +05:30
|
|
|
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar('\n');
|
2017-01-11 20:57:12 +05:30
|
|
|
#if ENABLE_FEATURE_TOP_INTERACTIVE
|
2008-03-20 01:08:46 +05:30
|
|
|
reset_term();
|
2008-03-20 21:35:02 +05:30
|
|
|
#endif
|
2016-08-19 14:37:31 +05:30
|
|
|
if (ENABLE_FEATURE_CLEAN_UP) {
|
|
|
|
clearmems();
|
|
|
|
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
|
|
|
|
free(prev_hist);
|
|
|
|
#endif
|
|
|
|
}
|
2002-09-18 03:44:58 +05:30
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|