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