xref: /netbsd/usr.bin/systat/syscall.c (revision 6550d01e)
1 /*	$NetBSD: syscall.c,v 1.7 2009/10/21 13:56:36 wiz 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.7 2009/10/21 13:56:36 wiz 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 >= (int)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 			clear();
207 			mvprintw(2, 10, "The alternate system clock has died!");
208 			mvprintw(3, 10, "Reverting to ``pigs'' display.");
209 			move(CMDLINE, 0);
210 			refresh();
211 			failcnt = 0;
212 			sleep(5);
213 			command(pigs);
214 			return;
215 		}
216 	} else
217 		etime = 1.0;
218 	itime = etime * 100;
219 
220 	failcnt = 0;
221 
222 	show_vmstat_top(&s.Total, &s.uvmexp, &s1.uvmexp);
223 
224 	/* Sort out the values we are going to display */
225 	for (i = 0; i < (int)nelem(s.counts); i++) {
226 		switch (show) {
227 		default:
228 		case SHOW_COUNTS:
229 			v = s.counts[i] - s1.counts[i];
230 			break;
231 		case SHOW_TIMES:
232 			v = s.times[i] - s1.times[i];
233 			break;
234 		case SHOW_COUNTS | SHOW_TIMES:    /* time/count */
235 			v = s.counts[i] - s1.counts[i];
236 			v = v ? (s.times[i] - s1.times[i]) / v : 0;
237 		}
238 
239 		if (display_mode == TIME)
240 		    v = (v * 100 + itime/2) / itime;
241 
242 		val[i] = v;
243 
244 		/*
245 		 * We use an 'infinite response filter' in a vague
246 		 * attempt to stop the data leaping around too much.
247 		 * I suspect there are other/better methods in use.
248 		 */
249 		if (irf_first) {
250 			irf[i] = v;
251 			irf_first = 0;
252 		} else {
253 			irf[i] = irf[i] * 7 / 8 + v;
254 		}
255 	}
256 
257 	if (sort_order == COUNTS) {
258 		/* mergesort() doesn't swap equal values about... */
259 		mergesort(syscall_sort, nelem(syscall_sort),
260 			sizeof syscall_sort[0], compare_irf);
261 	}
262 
263 	l = SYSCALLROW;
264 	c = 0;
265 	move(l, c);
266 	for (ii = 0; ii < (int)nelem(s.counts); ii++) {
267 		i = syscall_sort[ii];
268 		if (val[i] == 0 && irf[i] == 0)
269 			continue;
270 
271 		if (i < (int)nelem(syscallnames)) {
272 			const char *name = syscallnames[i];
273 			while (name[0] == '_')
274 				name++;
275 			if (name[0] == 'c' && !strcmp(name + 1, "ompat_"))
276 				name += 7;
277 			mvprintw(l, c, "%17.17s", name);
278 		} else
279 			mvprintw(l, c, "syscall #%d       ", i);
280 
281 		putuint64(val[i], l, c + 18, 8);
282 		c += 27;
283 		if (c + 26 > COLS) {
284 			c = 0;
285 			l++;
286 			if (l >= LINES - 1)
287 				break;
288 		}
289 	}
290 	if (display_mode == TIME) {
291 		memcpy(s1.counts, s.counts, sizeof s1.counts);
292 		memcpy(s1.times, s.times, sizeof s1.times);
293 	}
294 	while (l < LINES - 1) {
295 	    clrtoeol();
296 	    move(++l, 0);
297 	}
298 }
299 
300 void
301 syscall_boot(char *args)
302 {
303 	memset(&s1, 0, sizeof s1);
304 	display_mode = BOOT;
305 }
306 
307 void
308 syscall_run(char *args)
309 {
310 	s1 = s2;
311 	display_mode = RUN;
312 }
313 
314 void
315 syscall_time(char *args)
316 {
317 	display_mode = TIME;
318 }
319 
320 void
321 syscall_zero(char *args)
322 {
323 	s1 = s;
324 }
325 
326 static int
327 compare_names(const void *a, const void *b)
328 {
329 	const char *name_a = syscallnames[*(const int *)a];
330 	const char *name_b = syscallnames[*(const int *)b];
331 
332 	while (*name_a == '_')
333 		name_a++;
334 	while (*name_b == '_')
335 		name_b++;
336 
337 	return strcmp(name_a, name_b);
338 }
339 
340 void
341 syscall_order(char *args)
342 {
343 	int i, len;
344 
345 	if (args == NULL)
346 		goto usage;
347 
348 	len = strcspn(args, " \t\r\n");
349 
350 	if (args[len + strspn(args + len, " \t\r\n")])
351 		goto usage;
352 
353 	if (memcmp(args, "count", len) == 0)
354 		sort_order = COUNTS;
355 	else if (memcmp(args, "name", len) == 0)
356 		sort_order = NAMES;
357 	else if (memcmp(args, "syscall", len) == 0)
358 		sort_order = UNSORTED;
359 	else
360 		goto usage;
361 
362 	/* Undo all the sorting */
363 	for (i = 0; i < (int)nelem(syscall_sort); i++)
364 		syscall_sort[i] = i;
365 
366 	if (sort_order == NAMES) {
367 		/* Only sort the entries we have names for */
368 		qsort(syscall_sort, nelem(syscallnames), sizeof syscall_sort[0],
369 			compare_names);
370 	}
371 	return;
372 
373     usage:
374 	error("Usage: sort [count|name|syscall]");
375 }
376 
377 void
378 syscall_show(char *args)
379 {
380 	int len;
381 
382 	if (args == NULL)
383 		goto usage;
384 
385 	len = strcspn(args, " \t\r\n");
386 
387 	if (args[len + strspn(args + len, " \t\r\n")])
388 		goto usage;
389 
390 	if (memcmp(args, "counts", len) == 0)
391 		show = SHOW_COUNTS;
392 	else if (memcmp(args, "times", len) == 0)
393 		show = SHOW_TIMES;
394 	else if (memcmp(args, "ratio", len) == 0)
395 		show = SHOW_COUNTS | SHOW_TIMES;
396 	else
397 		goto usage;
398 
399 	memset(&irf, 0, sizeof irf);
400 	irf_first = 1;
401 
402 	return;
403 
404     usage:
405 	error("Usage: show [counts|times|ratio]");
406 }
407 
408 static void
409 getinfo(struct Info *stats, int get_what)
410 {
411 	int mib[2];
412 	size_t size;
413 
414 	cpureadstats();
415 
416 	if (get_what & SHOW_COUNTS) {
417 		size = sizeof stats->counts;
418 		if (!counts_mib_len ||
419 		    sysctl(counts_mib, counts_mib_len, &stats->counts, &size,
420 			    NULL, 0)) {
421 			error("can't get syscall counts: %s\n", strerror(errno));
422 			memset(&stats->counts, 0, sizeof stats->counts);
423 		}
424 	}
425 
426 	if (get_what & SHOW_TIMES) {
427 		size = sizeof stats->times;
428 		if (!times_mib_len ||
429 		    sysctl(times_mib, times_mib_len, &stats->times, &size,
430 			    NULL, 0)) {
431 			error("can't get syscall times: %s\n", strerror(errno));
432 			memset(&stats->times, 0, sizeof stats->times);
433 		}
434 	}
435 
436 	size = sizeof(stats->uvmexp);
437 	mib[0] = CTL_VM;
438 	mib[1] = VM_UVMEXP2;
439 	if (sysctl(mib, 2, &stats->uvmexp, &size, NULL, 0) < 0) {
440 		error("can't get uvmexp: %s\n", strerror(errno));
441 		memset(&stats->uvmexp, 0, sizeof(stats->uvmexp));
442 	}
443 	size = sizeof(stats->Total);
444 	mib[0] = CTL_VM;
445 	mib[1] = VM_METER;
446 	if (sysctl(mib, 2, &stats->Total, &size, NULL, 0) < 0) {
447 		error("Can't get kernel info: %s\n", strerror(errno));
448 		memset(&stats->Total, 0, sizeof(stats->Total));
449 	}
450 }
451