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