1 /* $NetBSD: syscall.c,v 1.4 2008/02/11 03:51:17 dyoung 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.4 2008/02/11 03:51:17 dyoung 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