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