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