xref: /netbsd-src/usr.bin/systat/syscall.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /*	$NetBSD: syscall.c,v 1.5 2008/04/29 06:53:04 martin Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by David Laight.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: syscall.c,v 1.5 2008/04/29 06:53:04 martin Exp $");
34 
35 /* System call stats */
36 
37 #include <sys/param.h>
38 #include <sys/user.h>
39 #include <sys/namei.h>
40 #include <sys/sysctl.h>
41 
42 #include <uvm/uvm_extern.h>
43 
44 #include <errno.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <util.h>
48 
49 #include "systat.h"
50 #include "extern.h"
51 #include "drvstats.h"
52 #include "utmpentry.h"
53 #include "vmstat.h"
54 
55 #include <sys/syscall.h>
56 #include <../../sys/kern/syscalls.c>
57 
58 #define nelem(x) (sizeof (x) / sizeof *(x))
59 
60 static struct Info {
61 	struct	 uvmexp_sysctl uvmexp;
62 	struct	 vmtotal Total;
63 	uint64_t counts[SYS_NSYSENT];
64 	uint64_t times[SYS_NSYSENT];
65 } s, s1, s2;
66 
67 static uint64_t irf[SYS_NSYSENT], val[SYS_NSYSENT];
68 static int irf_first = 1;
69 
70 int syscall_sort[SYS_NSYSENT];
71 
72 static	enum sort_order { UNSORTED, NAMES, COUNTS } sort_order = NAMES;
73 
74 #define	SHOW_COUNTS	1
75 #define	SHOW_TIMES	2
76 static int show = SHOW_COUNTS;
77 
78 static void getinfo(struct Info *, int);
79 
80 static	char buf[32];
81 static	float hertz;
82 
83 static size_t counts_mib_len, times_mib_len;
84 static int counts_mib[4], times_mib[4];
85 
86 WINDOW *
87 opensyscall(void)
88 {
89 	return (stdscr);
90 }
91 
92 void
93 closesyscall(WINDOW *w)
94 {
95 
96 	if (w == NULL)
97 		return;
98 	wclear(w);
99 	wrefresh(w);
100 }
101 
102 
103 /*
104  * These constants define where the major pieces are laid out
105  */
106 #define SYSCALLROW	9	/* Below the vmstat header */
107 
108 int
109 initsyscall(void)
110 {
111 	static char name[] = "name";
112 
113 	hertz = stathz ? stathz : hz;
114 
115 	syscall_order(name);
116 
117 	/* drvinit gets number of cpus! */
118 	drvinit(1);
119 
120 	counts_mib_len = nelem(counts_mib);
121 	if (sysctlnametomib("kern.syscalls.counts", counts_mib, &counts_mib_len))
122 		counts_mib_len = 0;
123 
124 	times_mib_len = nelem(times_mib);
125 	if (sysctlnametomib("kern.syscalls.times", times_mib, &times_mib_len))
126 		times_mib_len = 0;
127 
128 	getinfo(&s2, SHOW_COUNTS | SHOW_TIMES);
129 	s1 = s2;
130 
131 	return(1);
132 }
133 
134 void
135 fetchsyscall(void)
136 {
137 	time_t now;
138 
139 	time(&now);
140 	strlcpy(buf, ctime(&now), sizeof(buf));
141 	buf[19] = '\0';
142 	getinfo(&s, show);
143 }
144 
145 void
146 labelsyscall(void)
147 {
148 	labelvmstat_top();
149 }
150 
151 #define MAXFAIL 5
152 
153 static void
154 putuint64(uint64_t v, int row, int col, int width)
155 {
156 	static const char suffix[] = "KMDT";
157 	int len, i;
158 
159 	len = snprintf(buf, sizeof buf, "%" PRIu64, v);
160 	if (len > width) {
161 		i = (len - width) / 3;
162 		if (i >= sizeof suffix) {
163 			memset(buf, '*', width);
164 			len = width;
165 		} else {
166 			len -= (i + 1) * 3;
167 			buf[len++] = suffix[i];
168 		}
169 		buf[len] = 0;
170 	}
171 	mvprintw(row, col, "%*s", width, buf);
172 }
173 
174 static int
175 compare_irf(const void *a, const void *b)
176 {
177 	int ia = *(const int *)a, ib = *(const int *)b;
178 	int64_t delta;
179 
180 	delta = irf[ib] - irf[ia];
181 	return delta ? delta < 0 ? -1 : 1 : 0;
182 }
183 
184 void
185 showsyscall(void)
186 {
187 	int i, ii, l, c;
188 	uint64_t v;
189 	static int failcnt = 0;
190 	static int relabel = 0;
191 	static char pigs[] = "pigs";
192 	uint64_t itime;
193 
194 	if (relabel) {
195 		labelsyscall();
196 		relabel = 0;
197 	}
198 
199 	cpuswap();
200 	if (display_mode == TIME) {
201 		etime = cur.cp_etime;
202 		/* < 5 ticks - ignore this trash */
203 		if ((etime * hertz) < 1.0) {
204 			if (failcnt++ > MAXFAIL)
205 				return;
206 			failcnt = 0;
207 			clear();
208 			mvprintw(2, 10, "The alternate system clock has died!");
209 			mvprintw(3, 10, "Reverting to ``pigs'' display.");
210 			move(CMDLINE, 0);
211 			refresh();
212 			failcnt = 0;
213 			sleep(5);
214 			command(pigs);
215 			return;
216 		}
217 	} else
218 		etime = 1.0;
219 	itime = etime * 100;
220 
221 	failcnt = 0;
222 
223 	show_vmstat_top(&s.Total, &s.uvmexp, &s1.uvmexp);
224 
225 	/* Sort out the values we are going to display */
226 	for (i = 0; i < nelem(s.counts); i++) {
227 		switch (show) {
228 		default:
229 		case SHOW_COUNTS:
230 			v = s.counts[i] - s1.counts[i];
231 			break;
232 		case SHOW_TIMES:
233 			v = s.times[i] - s1.times[i];
234 			break;
235 		case SHOW_COUNTS | SHOW_TIMES:    /* time/count */
236 			v = s.counts[i] - s1.counts[i];
237 			v = v ? (s.times[i] - s1.times[i]) / v : 0;
238 		}
239 
240 		if (display_mode == TIME)
241 		    v = (v * 100 + itime/2) / itime;
242 
243 		val[i] = v;
244 
245 		/*
246 		 * We use an 'infinite response filter' in a vague
247 		 * attempt to stop the data leaping around too much.
248 		 * I suspect there are other/better methods in use.
249 		 */
250 		if (irf_first) {
251 			irf[i] = v;
252 			irf_first = 0;
253 		} else {
254 			irf[i] = irf[i] * 7 / 8 + v;
255 		}
256 	}
257 
258 	if (sort_order == COUNTS) {
259 		/* mergesort() doesn't swap equal values about... */
260 		mergesort(syscall_sort, nelem(syscall_sort),
261 			sizeof syscall_sort[0], compare_irf);
262 	}
263 
264 	l = SYSCALLROW;
265 	c = 0;
266 	move(l, c);
267 	for (ii = 0; ii < nelem(s.counts); ii++) {
268 		i = syscall_sort[ii];
269 		if (val[i] == 0 && irf[i] == 0)
270 			continue;
271 
272 		if (i < nelem(syscallnames)) {
273 			const char *name = syscallnames[i];
274 			while (name[0] == '_')
275 				name++;
276 			if (name[0] == 'c' && !strcmp(name + 1, "ompat_"))
277 				name += 7;
278 			mvprintw(l, c, "%17.17s", name);
279 		} else
280 			mvprintw(l, c, "syscall #%d       ", i);
281 
282 		putuint64(val[i], l, c + 18, 8);
283 		c += 27;
284 		if (c + 26 > COLS) {
285 			c = 0;
286 			l++;
287 			if (l >= LINES - 1)
288 				break;
289 		}
290 	}
291 	if (display_mode == TIME) {
292 		memcpy(s1.counts, s.counts, sizeof s1.counts);
293 		memcpy(s1.times, s.times, sizeof s1.times);
294 	}
295 	while (l < LINES - 1) {
296 	    clrtoeol();
297 	    move(++l, 0);
298 	}
299 }
300 
301 void
302 syscall_boot(char *args)
303 {
304 	memset(&s1, 0, sizeof s1);
305 	display_mode = BOOT;
306 }
307 
308 void
309 syscall_run(char *args)
310 {
311 	s1 = s2;
312 	display_mode = RUN;
313 }
314 
315 void
316 syscall_time(char *args)
317 {
318 	display_mode = TIME;
319 }
320 
321 void
322 syscall_zero(char *args)
323 {
324 	s1 = s;
325 }
326 
327 static int
328 compare_names(const void *a, const void *b)
329 {
330 	const char *name_a = syscallnames[*(const int *)a];
331 	const char *name_b = syscallnames[*(const int *)b];
332 
333 	while (*name_a == '_')
334 		name_a++;
335 	while (*name_b == '_')
336 		name_b++;
337 
338 	return strcmp(name_a, name_b);
339 }
340 
341 void
342 syscall_order(char *args)
343 {
344 	int i, len;
345 
346 	if (args == NULL)
347 		goto usage;
348 
349 	len = strcspn(args, " \t\r\n");
350 
351 	if (args[len + strspn(args + len, " \t\r\n")])
352 		goto usage;
353 
354 	if (memcmp(args, "count", len) == 0)
355 		sort_order = COUNTS;
356 	else if (memcmp(args, "name", len) == 0)
357 		sort_order = NAMES;
358 	else if (memcmp(args, "syscall", len) == 0)
359 		sort_order = UNSORTED;
360 	else
361 		goto usage;
362 
363 	/* Undo all the sorting */
364 	for (i = 0; i < nelem(syscall_sort); i++)
365 		syscall_sort[i] = i;
366 
367 	if (sort_order == NAMES) {
368 		/* Only sort the entries we have names for */
369 		qsort(syscall_sort, nelem(syscallnames), sizeof syscall_sort[0],
370 			compare_names);
371 	}
372 	return;
373 
374     usage:
375 	error("Usage: sort [count|name|syscall]");
376 }
377 
378 void
379 syscall_show(char *args)
380 {
381 	int len;
382 
383 	if (args == NULL)
384 		goto usage;
385 
386 	len = strcspn(args, " \t\r\n");
387 
388 	if (args[len + strspn(args + len, " \t\r\n")])
389 		goto usage;
390 
391 	if (memcmp(args, "counts", len) == 0)
392 		show = SHOW_COUNTS;
393 	else if (memcmp(args, "times", len) == 0)
394 		show = SHOW_TIMES;
395 	else if (memcmp(args, "ratio", len) == 0)
396 		show = SHOW_COUNTS | SHOW_TIMES;
397 	else
398 		goto usage;
399 
400 	memset(&irf, 0, sizeof irf);
401 	irf_first = 1;
402 
403 	return;
404 
405     usage:
406 	error("Usage: show [counts|times|ratio]");
407 }
408 
409 static void
410 getinfo(struct Info *stats, int get_what)
411 {
412 	int mib[2];
413 	size_t size;
414 
415 	cpureadstats();
416 
417 	if (get_what & SHOW_COUNTS) {
418 		size = sizeof stats->counts;
419 		if (!counts_mib_len ||
420 		    sysctl(counts_mib, counts_mib_len, &stats->counts, &size,
421 			    NULL, 0)) {
422 			error("can't get syscall counts: %s\n", strerror(errno));
423 			memset(&stats->counts, 0, sizeof stats->counts);
424 		}
425 	}
426 
427 	if (get_what & SHOW_TIMES) {
428 		size = sizeof stats->times;
429 		if (!times_mib_len ||
430 		    sysctl(times_mib, times_mib_len, &stats->times, &size,
431 			    NULL, 0)) {
432 			error("can't get syscall times: %s\n", strerror(errno));
433 			memset(&stats->times, 0, sizeof stats->times);
434 		}
435 	}
436 
437 	size = sizeof(stats->uvmexp);
438 	mib[0] = CTL_VM;
439 	mib[1] = VM_UVMEXP2;
440 	if (sysctl(mib, 2, &stats->uvmexp, &size, NULL, 0) < 0) {
441 		error("can't get uvmexp: %s\n", strerror(errno));
442 		memset(&stats->uvmexp, 0, sizeof(stats->uvmexp));
443 	}
444 	size = sizeof(stats->Total);
445 	mib[0] = CTL_VM;
446 	mib[1] = VM_METER;
447 	if (sysctl(mib, 2, &stats->Total, &size, NULL, 0) < 0) {
448 		error("Can't get kernel info: %s\n", strerror(errno));
449 		memset(&stats->Total, 0, sizeof(stats->Total));
450 	}
451 }
452