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