From: Job Snijders Subject: watch(1): add knob to display command's resource utilization To: tech@openbsd.org Date: Tue, 24 Jun 2025 10:55:22 +0000 The watch(1) utility is a process instrument panel. Introduce a new 'r' interactive command to display the last command execution's resource utilisation, kinda like time(1) does. It refreshes as new information becomes available. OK? Index: watch.1 =================================================================== RCS file: /cvs/src/usr.bin/watch/watch.1,v diff -u -p -r1.13 watch.1 --- watch.1 24 May 2025 09:49:14 -0000 1.13 +++ watch.1 24 Jun 2025 10:47:04 -0000 @@ -96,6 +96,8 @@ Scroll up one line. Scroll right half a screen. .It Ic l Highlight changed lines. +.It Ic r +Display information about resource utilization. .It Ic s Change the update interval. .It Ic p @@ -110,7 +112,8 @@ Quit .El .Sh SEE ALSO .Xr sh 1 , -.Xr execl 3 +.Xr execl 3 , +.Xr getrusage 2 .Sh HISTORY .Nm was first published in 1991 and has been available since Index: watch.c =================================================================== RCS file: /cvs/src/usr.bin/watch/watch.c,v diff -u -p -r1.30 watch.c --- watch.c 24 Jun 2025 00:51:04 -0000 1.30 +++ watch.c 24 Jun 2025 10:47:04 -0000 @@ -1,5 +1,6 @@ /* $OpenBSD: watch.c,v 1.30 2025/06/24 00:51:04 job Exp $ */ /* + * Copyright (c) 2025 Job Snijders * Copyright (c) 2000, 2001 Internet Initiative Japan Inc. * All rights reserved. * @@ -20,6 +21,9 @@ #include #include +#include +#include +#include #include #include @@ -73,9 +77,12 @@ int start_line = 0, start_column = 0; /* int pause_on_error = 0; int paused = 0; int want_update; +int show_rusage = 0; +struct rusage prev_ru, ru; int last_exitcode = 0; time_t lastupdate; int xflag = 0; +struct timespec prev_start, start, prev_stop, stop; #define addwch(_x) addnwstr(&(_x), 1); #define WCWIDTH(_x) ((wcwidth((_x)) > 0)? wcwidth((_x)) : 1) @@ -105,6 +112,7 @@ struct event ev_timer; int display(BUFFER *, BUFFER *, highlight_mode_t); kbd_result_t kbd_command(int); void show_help(void); +void print_rusage(void); void untabify(wchar_t *, int); void on_signal(int, short, void *); void on_sigchild(int, short, void *); @@ -286,6 +294,11 @@ display(BUFFER * cur, BUFFER * prev, hig move(1, 1); + if (show_rusage) { + print_rusage(); + return 1; + } + if (!prev || (cur == prev)) hm = HIGHLIGHT_NONE; @@ -407,6 +420,10 @@ start_child() if (pipe(fds) == -1) err(1, "pipe()"); + (void)memset(&ru, 0, sizeof(struct rusage)); + + clock_gettime(CLOCK_MONOTONIC, &start); + child->pid = vfork(); if (child->pid == -1) err(1, "vfork"); @@ -469,13 +486,18 @@ on_sigchild(int sig, short event, void * int st; do { - pid = waitpid(WAIT_ANY, &st, 0); + pid = wait4(WAIT_ANY, &st, 0, &ru); } while (pid == -1 && errno == EINTR); if (!running_child || running_child->pid != pid) return; /* Remember update time */ time(&lastupdate); + clock_gettime(CLOCK_MONOTONIC, &stop); + prev_start = start; + prev_stop = stop; + + prev_ru = ru; if (WIFEXITED(st)) last_exitcode = WEXITSTATUS(st); @@ -529,6 +551,11 @@ kbd_command(int ch) { char buf[10]; + if (show_rusage) { + show_rusage = 0; + return (RSLT_REDRAW); + } + switch (ch) { case '?': @@ -630,6 +657,10 @@ kbd_command(int ch) return (RSLT_REDRAW); } + case 'r': + show_rusage = 1; + return (RSLT_REDRAW); + case 's': move(1, 0); @@ -698,6 +729,7 @@ show_help(void) "l - highlight changed lines\n" "w - highlight changed words\n" "p - toggle pause / resume\n" + "r - show information about resource utilization\n" "s - change the update interval\n" "h | ? - show this message\n" "q - quit\n\n"); @@ -743,6 +775,60 @@ untabify(wchar_t *buf, int maxlen) } } *p = L'\0'; +} + +void +print_rusage(void) +{ + int hz; + long ticks; + int mib[2]; + struct clockinfo clkinfo; + struct timespec elapsed; + size_t size; + + mib[0] = CTL_KERN; + mib[1] = KERN_CLOCKRATE; + size = sizeof(clkinfo); + if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) + err(1, "sysctl"); + hz = clkinfo.hz; + ticks = hz * (prev_ru.ru_utime.tv_sec + prev_ru.ru_stime.tv_sec) + + hz * (prev_ru.ru_utime.tv_usec + prev_ru.ru_stime.tv_usec) + / 1000000; + + timespecsub(&prev_stop, &prev_start, &elapsed); + + printw("\n%7lld.%02ld %s\n", (long long)elapsed.tv_sec, + elapsed.tv_nsec / 10000000, "real"); + printw("%7lld.%02ld %s\n", (long long)prev_ru.ru_utime.tv_sec, + prev_ru.ru_utime.tv_usec / 10000, "user"); + printw("%7lld.%02ld %s\n", (long long)prev_ru.ru_stime.tv_sec, + prev_ru.ru_stime.tv_usec / 10000, "sys"); + + printw("%10ld %s\n", prev_ru.ru_maxrss, "maximum resident set size"); + printw("%10ld %s\n", ticks ? prev_ru.ru_ixrss / ticks : 0, + "average shared memory size"); + printw("%10ld %s\n", ticks ? prev_ru.ru_idrss / ticks : 0, + "average unshared data size"); + printw("%10ld %s\n", ticks ? prev_ru.ru_isrss / ticks : 0, + "average unshared stack size"); + printw("%10ld %s\n", prev_ru.ru_minflt, "minor page faults"); + printw("%10ld %s\n", prev_ru.ru_majflt, "major page faults"); + printw("%10ld %s\n", prev_ru.ru_nswap, "swaps"); + printw("%10ld %s\n", prev_ru.ru_inblock, "block input operations"); + printw("%10ld %s\n", prev_ru.ru_oublock, "block output operations"); + printw("%10ld %s\n", prev_ru.ru_msgsnd, "messages sent"); + printw("%10ld %s\n", prev_ru.ru_msgrcv, "messages received"); + printw("%10ld %s\n", prev_ru.ru_nsignals, "signals received"); + printw("%10ld %s\n", prev_ru.ru_nvcsw, "voluntary context switches"); + printw("%10ld %s\n", prev_ru.ru_nivcsw, + "involuntary context switches"); + + standout(); + printw("\nHit any key to continue."); + standend(); + refresh(); } void