Index | Thread | Search

From:
Job Snijders <job@openbsd.org>
Subject:
watch(1): add knob to display command's resource utilization
To:
tech@openbsd.org
Date:
Tue, 24 Jun 2025 10:55:22 +0000

Download raw body.

Thread
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 <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