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