Index | Thread | Search

From:
Stuart Henderson <stu@spacehopper.org>
Subject:
Re: watch(1): add knob to display command's resource utilization
To:
Job Snijders <job@openbsd.org>
Cc:
tech@openbsd.org
Date:
Tue, 24 Jun 2025 14:11:05 +0100

Download raw body.

Thread
does this really make sense for watch(1), why not just run time?

iwatch '\time -l md5 -t 2>&1'


On 2025/06/24 10:55, Job Snijders wrote:
> 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 

trailing whitespace btw.

>  .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 <job@openbsd.org>
>   * Copyright (c) 2000, 2001 Internet Initiative Japan Inc.
>   * All rights reserved.
>   *
> @@ -20,6 +21,9 @@
>  
>  #include <sys/types.h>
>  #include <sys/ioctl.h>
> +#include <sys/resource.h>
> +#include <sys/time.h>
> +#include <sys/sysctl.h>
>  #include <sys/wait.h>
>  
>  #include <curses.h>
> @@ -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
>