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