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, ×_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