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