--- old/miscutils/whatisup.c 1969-12-31 16:00:00.000000000 -0800 +++ new/miscutils/whatisup.c 2006-07-26 14:52:03.000000000 -0700 @@ -0,0 +1,403 @@ +/* vi: set sw=4 ts=4: */ +/* + * whatisup implementation for busybox + * whatisup command runs the specified program with the given arguments. + * When the command finishes, whatisup writes a message to standard output + * giving timing statistics from /proc/stat, /poc/interrupts and /proc/cpuinfo + * about resource usage while command was executed. + * If program not specified the command waits to be cancelled and then displays + * resource usage during self execution. + * Copyright (C) 2006 by Vladimir Doukhanine + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" +#include + +/* Information on the resources used by a child process. */ +typedef struct { + int waitstatus; + struct rusage ru; +} resource_t; + +#define MAX_INTR 1024 +#define BUFFER_SIZE 132 + +struct interrupts { + unsigned long long num; + char * name; +}; +struct jiffies_and_counts { + unsigned long long user,nice,system,idle,iowait,irq,softirq,steal; + unsigned long long ctxt; +// unsigned long long btime, processes, procs_running, procs_blocked; + struct interrupts * intr; + unsigned long long total; + unsigned long long busy; +}; + +struct cpuinfo { + unsigned int processor; + char model_name[BUFFER_SIZE]; + float cpu_MHz,bogomips, bogomips_total; +}; + +static struct jiffies_and_counts j_c[2]; +static int cpu_n, intr_n; +static struct cpuinfo cpu_info; +static unsigned long user_hz; + +static float get_percents(unsigned long long sample, unsigned long long total) +{ + if (!sample || !total) + return 0.; + else + return (sample * 100.) / total; +} + +static float get_pace(unsigned long long events, unsigned long long total_ticks) +{ + if (!events || !total_ticks) + return 0.; + else + return (events * (float)user_hz) / total_ticks; +} + +static void sig_catcher(int sig ATTRIBUTE_UNUSED) +{ +} + +static void get_interrupts(int n) +{ + int i; + char buf[BUFFER_SIZE]; + static struct jiffies_and_counts *p_j_c; + unsigned long long count; + char *errstr = "failed to read 'interrupts'"; + + p_j_c=&j_c[n]; + i = 0; + FILE* fp = bb_xfopen("/proc/interrupts", "r"); + while (1) { + while (fscanf(fp, "%d:%lld ", &i, &count) < 2) { + if (fgets(buf, sizeof(buf), fp) <= 0) {/* skip line */ + if (!i) + bb_error_msg_and_die(errstr); + else + goto ex; + } + } + if (fgets(buf, sizeof(buf), fp) > 0) { + p_j_c->intr[i+1].name = (char *)malloc(sizeof(buf)); + if (p_j_c->intr[i+1].name != NULL) + strcpy(p_j_c->intr[i+1].name, buf); + } + else + break; + } +ex: + fclose(fp); +} + +static void get_stat(int n) +{ + int i; + char buf[BUFFER_SIZE]; + static struct jiffies_and_counts *p_j_c; + char *errstr = "failed to read 'stat'"; + char fbuf[PAGE_SIZE]; + + intr_n = 0; + p_j_c=&j_c[n]; + FILE* fp = bb_xfopen("/proc/stat", "r"); + setbuf(fp, fbuf); + setvbuf(fp, fbuf, _IONBF, PAGE_SIZE); + if (fscanf(fp, "cpu %lld %lld %lld %lld %lld %lld %lld %lld", \ + &p_j_c->user,&p_j_c->nice,&p_j_c->system,&p_j_c->idle, \ + &p_j_c->iowait,&p_j_c->irq,&p_j_c->softirq,&p_j_c->steal) < 4) + { + bb_error_msg_and_die(errstr); + } + for (i = 0;i < MAX_INTR; i++) { + p_j_c->intr[i].num = 0; + p_j_c->intr[i].name = NULL; + } + i=0; + while (fscanf(fp, "intr %lld", &p_j_c->intr[i].num) < 1) { + if (fgets(buf, sizeof(buf), fp) <= 0) /* skip line */ { + bb_error_msg_and_die(errstr); + break; + } + } + while ( ++i < MAX_INTR ) { + if (fscanf(fp, " %lld", &p_j_c->intr[i].num) < 1) { + break; + } + } + intr_n = i; + if (1==n) + get_interrupts(n); + if (fscanf(fp, "ctxt %lld", &p_j_c->ctxt) < 1 \ + || fgets(buf, sizeof(buf), fp) <= 0) + bb_error_msg_and_die(errstr); +#if 0 + if (fscanf(fp, "btime %lld", &p_j_c->btime) < 1 \ + || fgets(buf, sizeof(buf), fp) <= 0) + bb_error_msg_and_die(errstr); + if (fscanf(fp, "processes %lld", &p_j_c->processes) < 1 \ + || fgets(buf, sizeof(buf), fp) <= 0) + bb_error_msg_and_die(errstr); + if (fscanf(fp, "procs_running %lld", &p_j_c->procs_running) < 1 \ + || fgets(buf, sizeof(buf), fp) <= 0) + bb_error_msg_and_die(errstr); + if (fscanf(fp, "procs_blocked %lld", &p_j_c->procs_blocked) < 1) + bb_error_msg_and_die(errstr); +#endif + fclose(fp); +} + +static void get_print_cpuinfo(FILE * fpo) +{ + char buf[BUFFER_SIZE]; + + cpu_n = 0; + cpu_info.bogomips_total = 0.0; + + FILE* fp = bb_xfopen("/proc/cpuinfo", "r"); + do { + while (fscanf(fp, "processor : %d", &cpu_info.processor) < 1) { + if (fgets(buf, sizeof(buf), fp) <= 0) /* skip line */ { + goto ex; + } + } + while (fscanf(fp, "model name :%c", cpu_info.model_name) < 1) { + if (fgets(buf, sizeof(buf), fp) <= 0) /* skip line */ { + goto ex; + } + } + if (fgets(cpu_info.model_name, sizeof(buf), fp) <= 0) /* model name */ { + goto ex; + } + while (fscanf(fp, "cpu MHz : %f", &cpu_info.cpu_MHz) < 1) { + if (fgets(buf, sizeof(buf), fp) <= 0) /* skip line */ { + goto ex; + } + } + while (fscanf(fp, "bogomips : %f", &cpu_info.bogomips) < 1) { + if (fgets(buf, sizeof(buf), fp) <= 0) /* skip line */ { + goto ex; + } + } + cpu_info.bogomips_total += cpu_info.bogomips; + fprintf(fpo, "\n#%d MHz=%.3f bogomips=%.3f %s", \ + cpu_info.processor, cpu_info.cpu_MHz, cpu_info.bogomips, \ + cpu_info.model_name); + + } while (++cpu_n); +ex: + fclose(fp); + if (cpu_n > 1) + fprintf(fpo, "\ntotal %d cpu, bogomips total=%.3f", \ + cpu_n, cpu_info.bogomips_total); + user_hz = sysconf(_SC_CLK_TCK); + //fprintf(fpo, "\nUSER_HZ=%lu", user_hz); +} + +/* Print ARGV to FP, with each entry in ARGV separated by FILLER. */ +static void fprintargv(FILE * fp, char *const *argv, const char *filler) +{ + char *const *av; + + av = argv; + fputs(*av, fp); + while (*++av) { + fputs(filler, fp); + fputs(*av, fp); + } + if (ferror(fp)) + bb_error_msg_and_die(bb_msg_write_error); +} + +static void report(FILE * fp, char **command ) +{ + int i; + char * fmt = "%7s: %llu\t%.2f\t%.3f%%\n"; + + if (command != NULL && *command != NULL) { + fprintf(fp, "The program: "); + fprintargv(fp, command, " "); + } + get_print_cpuinfo(fp); + for (i = 0; i < 2; i++) + { + j_c[i].total = j_c[i].user + j_c[i].nice + j_c[i].system + j_c[i].idle \ + + j_c[i].iowait + j_c[i].irq + j_c[i].softirq + j_c[i].steal; + /* procps 2.x does not count iowait as busy time */ + j_c[i].busy = j_c[i].total - j_c[i].idle - j_c[i].iowait; + } + fprintf(fp, "Interrupts:\tAverage pace(per sec.)\n"); + j_c[1].total = (long long)j_c[1].total - (long long)j_c[0].total; + j_c[1].busy = (long long)j_c[1].busy - (long long)j_c[0].busy; + for (i = 0; i < intr_n; i++) { + if (j_c[1].intr[i].num) { + j_c[1].intr[i].num = (long long)j_c[1].intr[i].num - (long long)j_c[0].intr[i].num; + if (j_c[1].intr[i].num) { + if (i) { + fprintf(fp, "%3d:\t%llu\t%.3f\t%s", i - 1, j_c[1].intr[i].num, \ + get_pace(j_c[1].intr[i].num, (long long)j_c[1].total), \ + j_c[1].intr[i].name?j_c[1].intr[i].name:"\n"); + free(j_c[1].intr[i].name); + j_c[1].intr[i].name = NULL; + } + else { + fprintf(fp, "Sum:\t%llu\t%.3f\n", j_c[1].intr[i].num, \ + get_pace(j_c[1].intr[i].num, (long long)j_c[1].total)); + } + } + } + } + j_c[1].ctxt = (long long)j_c[1].ctxt - (long long)j_c[0].ctxt; + fprintf(fp, "Context switches:\n\t%llu\t%.3f\n", \ + j_c[1].ctxt, get_pace(j_c[1].ctxt, (long long)j_c[1].total)); + j_c[1].user = (long long)j_c[1].user - (long long)j_c[0].user; + j_c[1].nice = (long long)j_c[1].nice - (long long)j_c[0].nice; + j_c[1].system = (long long)j_c[1].system - (long long)j_c[0].system; + j_c[1].idle = (long long)j_c[1].idle - (long long)j_c[0].idle; + j_c[1].iowait = (long long)j_c[1].iowait - (long long)j_c[0].iowait; + j_c[1].irq = (long long)j_c[1].irq - (long long)j_c[0].irq; + j_c[1].softirq = (long long)j_c[1].softirq - (long long)j_c[0].softirq; + j_c[1].steal = (long long)j_c[1].steal - (long long)j_c[0].steal; + fprintf(fp, "Time: Ticks\tSec.\n"); + fprintf(fp, fmt, "user", \ + j_c[1].user, j_c[1].user/(float)user_hz, get_percents(j_c[1].user, j_c[1].total)); + fprintf(fp, fmt, "nice", \ + j_c[1].nice, j_c[1].nice/(float)user_hz, get_percents(j_c[1].nice, j_c[1].total)); + fprintf(fp, fmt, "system", \ + j_c[1].system, j_c[1].system/(float)user_hz, get_percents(j_c[1].system, j_c[1].total)); + fprintf(fp, fmt, "idle", \ + j_c[1].idle, j_c[1].idle/(float)user_hz, get_percents(j_c[1].idle, j_c[1].total)); + fprintf(fp, fmt, "iowait", \ + j_c[1].iowait, j_c[1].iowait/(float)user_hz, get_percents(j_c[1].iowait, j_c[1].total)); + fprintf(fp, fmt, "irq", \ + j_c[1].irq, j_c[1].irq/(float)user_hz, get_percents(j_c[1].irq, j_c[1].total)); + fprintf(fp, fmt, "softirq", \ + j_c[1].softirq, j_c[1].softirq/(float)user_hz, get_percents(j_c[1].softirq, j_c[1].total)); + fprintf(fp, fmt, "steal", \ + j_c[1].steal, j_c[1].steal/(float)user_hz, get_percents(j_c[1].steal, j_c[1].total)); + fprintf(fp, "%7s: %llu\t%.2f\n", "Total", \ + j_c[1].total, j_c[1].total/(float)user_hz); + fprintf(fp, "%7s: %llu\t%.2f\t%.3f%% (Total-idle-iowait)\n", "Busy", + j_c[1].busy, j_c[1].busy/(float)user_hz, get_percents(j_c[1].busy, j_c[1].total)); +// fprintf(fp, "ctxt=%llu btime=%llu processes=%llu procs_running=%llu procs_blocked=%llu\n", +// j_c[1].ctxt - j_c[0].ctxt, j_c[1].btime, j_c[1].processes - j_c[1].processes, +// j_c[1].procs_running, j_c[1].procs_blocked); + if (ferror(fp)) + bb_error_msg_and_die(bb_msg_write_error); +} + + /* Wait for and fill in data on child process PID. + Return 0 on error, 1 if ok. */ +/* pid_t is short on BSDI, so don't try to promote it. */ +static int resuse_end(pid_t pid, resource_t * resp) +{ + int status; + + pid_t caught; + + /* Ignore signals, but don't ignore the children. When wait3 + returns the child process, set the time the command finished. */ + while ((caught = wait3(&status, 0, &resp->ru)) != pid) { + if (caught == -1) + return 0; + } + resp->waitstatus = status; + return 1; +} + +/* Run command CMD and return statistics on it. + Put the statistics in *RESP. */ +static void run_command(char *const *cmd, resource_t * resp) +{ + pid_t pid; /* Pid of child. */ + __sighandler_t interrupt_signal, quit_signal; + + pid = vfork(); /* Run CMD as child process. */ + if (pid < 0) + bb_error_msg_and_die("cannot fork"); + else if (pid == 0) { /* If child. */ + /* Don't cast execvp arguments; that causes errors on some systems, + versus merely warnings if the cast is left off. */ + execvp(cmd[0], cmd); + bb_error_msg("cannot run %s", cmd[0]); + _exit(errno == ENOENT ? 127 : 126); + } + + /* Have signals kill the child but not self (if possible). */ + interrupt_signal = signal(SIGINT, SIG_IGN); + quit_signal = signal(SIGQUIT, SIG_IGN); + + if (resuse_end(pid, resp) == 0) + bb_error_msg("error waiting for child process"); + + /* Re-enable signals. */ + signal(SIGINT, interrupt_signal); + signal(SIGQUIT, quit_signal); +} + +int whatisup_main(int argc, char **argv) +{ + resource_t res; + + argc--; + argv++; + j_c[0].intr = xmalloc(MAX_INTR * sizeof(struct interrupts)); + j_c[1].intr = xmalloc(MAX_INTR * sizeof(struct interrupts)); + if (!argc) { + fprintf(stderr, "Press Ctrl/C to exit...\n"); + signal(SIGTERM, sig_catcher); + signal(SIGINT, sig_catcher); + get_stat(0); + pause(); + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + } + else { + get_stat(0); + run_command (argv, &res); + } + if (WIFEXITED(res.waitstatus) && WEXITSTATUS(res.waitstatus)) { +#if 0 + fprintf(stderr, "Command exited with non-zero status %d\n", \ + WEXITSTATUS(res.waitstatus)); +#endif + bb_show_usage(); + } + else { + get_stat(1); + report(stderr, argv); + if (WIFSTOPPED(res.waitstatus)) { +#if 0 + fprintf(stderr, "Command stopped by signal %d\n", \ + WSTOPSIG(res.waitstatus)); +#endif + } + else if (WIFSIGNALED(res.waitstatus)) { +#if 0 + fprintf(stderr, "Command terminated by signal %d\n", \ + WTERMSIG(res.waitstatus)); +#endif + } + } + fflush(stderr); + free(j_c[0].intr); + free(j_c[1].intr); + + if (WIFSTOPPED(res.waitstatus)) + exit(WSTOPSIG(res.waitstatus)); + else if (WIFSIGNALED(res.waitstatus)) + exit(WTERMSIG(res.waitstatus)); + else if (WIFEXITED(res.waitstatus)) + exit(WEXITSTATUS(res.waitstatus)); + return 0; +} --- old/miscutils/Config.in 2006-07-13 12:13:56.000000000 -0700 +++ new/miscutils/Config.in 2006-07-24 10:19:50.000000000 -0700 @@ -329,5 +329,16 @@ certain amount of time, the watchdog device assumes the system has hung, and will cause the hardware to reboot. +config CONFIG_WHATISUP + bool "whatisup" + default n + help + The whatisup command runs the specified program with the given arguments. + When the command finishes, whatisup writes a message to standard output + giving timing statistics from /proc/stat, /poc/interrupts and /proc/cpuinfo + about resource usage while command was executed. + If program not specified the command waits to be cancelled and then displays + resource usage during self execution. + endmenu --- old/include/usage.h 2006-07-13 12:13:56.000000000 -0700 +++ new/include/usage.h 2006-07-24 07:50:21.000000000 -0700 @@ -3407,6 +3407,14 @@ "\t-O\tsave to filename ('-' for stdout)\n" \ "\t-Y\tuse proxy ('on' or 'off')" +#define whatisup_trivial_usage \ + "[COMMAND [ARGS...]]" +#define whatisup_full_usage \ + "Runs the program COMMAND with arguments ARGS. When COMMAND finishes,\n" \ + "resource usage while COMMAND was executed is displayed.\n" \ + "With no COMMAND, waits to be cancelled and displays resource usage\n" \ + "during self execution." + #define which_trivial_usage \ "[COMMAND ...]" #define which_full_usage \ --- old/miscutils/Makefile.in 2006-07-13 12:13:56.000000000 -0700 +++ new/miscutils/Makefile.in 2006-07-13 15:28:44.000000000 -0700 @@ -30,6 +30,7 @@ MISCUTILS-$(CONFIG_STRINGS) += strings.o MISCUTILS-$(CONFIG_TIME) += time.o MISCUTILS-$(CONFIG_WATCHDOG) += watchdog.o +MISCUTILS-$(CONFIG_WHATISUP) += whatisup.o ifneq ($(strip $(MISCUTILS-y)),) libraries-y+=$(MISCUTILS_DIR)$(MISCUTILS_AR) --- old/docs/new-applet-HOWTO.txt 2006-07-13 12:14:03.000000000 -0700 +++ new/docs/new-applet-HOWTO.txt 2006-07-24 14:11:45.000000000 -0700 @@ -119,9 +119,7 @@ algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily: /* all programs above here are alphabetically "less than" 'mu' */ - #ifdef CONFIG_MU - APPLET("mu", mu_main, _BB_DIR_USR_BIN, mu_usage) - #endif + USE_MU(APPLET("mu", mu_main, _BB_DIR_USR_BIN, mu_usage)) /* all programs below here are alphabetically "greater than" 'mu' */ @@ -139,10 +137,10 @@ Then create a diff -urN of the files you added and/or modified. Typically: /.c - include/usage.c + include/usage.h include/applets.h /Makefile.in - /config.in + /Config.in and send it to the mailing list: busybox@busybox.net http://busybox.net/mailman/listinfo/busybox --- old/include/applets.h 2006-07-13 12:13:56.000000000 -0700 +++ new/include/applets.h 2006-07-24 14:10:34.000000000 -0700 @@ -296,6 +296,7 @@ USE_WATCHDOG(APPLET(watchdog, _BB_DIR_SBIN, _BB_SUID_NEVER)) USE_WC(APPLET(wc, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_WGET(APPLET(wget, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) +USE_WHATISUP(APPLET(whatisup, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_WHICH(APPLET(which, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_WHO(APPLET(who, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_WHOAMI(APPLET(whoami, _BB_DIR_USR_BIN, _BB_SUID_NEVER))