1 /* $NetBSD: tprof_top.c,v 1.11 2024/02/07 04:20:28 msaitoh Exp $ */ 2 3 /*- 4 * Copyright (c) 2022 Ryo Shimizu 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #ifndef lint 31 __RCSID("$NetBSD: tprof_top.c,v 1.11 2024/02/07 04:20:28 msaitoh Exp $"); 32 #endif /* not lint */ 33 34 #include <sys/param.h> 35 #include <sys/types.h> 36 #include <sys/ioctl.h> 37 #include <sys/rbtree.h> 38 #include <sys/select.h> 39 #include <sys/time.h> 40 41 #include <assert.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <inttypes.h> 46 #include <math.h> 47 #include <signal.h> 48 #include <stdio.h> 49 #include <stdbool.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <term.h> 53 #include <termios.h> 54 #include <unistd.h> 55 #include <util.h> 56 57 #include <dev/tprof/tprof_ioctl.h> 58 #include "tprof.h" 59 #include "ksyms.h" 60 61 #define SAMPLE_MODE_ACCUMULATIVE 0 62 #define SAMPLE_MODE_INSTANTANEOUS 1 63 #define SAMPLE_MODE_NUM 2 64 65 #define LINESTR "-------------------------------------------------------------" 66 #define SYMBOL_LEN 32 /* symbol and event name */ 67 68 struct sample_elm { 69 struct rb_node node; 70 uint64_t addr; 71 const char *name; 72 uint32_t flags; 73 #define SAMPLE_ELM_FLAGS_USER 0x00000001 74 uint32_t num[SAMPLE_MODE_NUM]; 75 uint32_t num_cpu[]; /* [SAMPLE_MODE_NUM][ncpu] */ 76 #define SAMPLE_ELM_NUM_CPU(e, k) \ 77 ((e)->num_cpu + (k) * ncpu) 78 }; 79 80 struct ptrarray { 81 void **pa_ptrs; 82 size_t pa_allocnum; 83 size_t pa_inuse; 84 }; 85 86 static int opt_mode = SAMPLE_MODE_INSTANTANEOUS; 87 static int opt_userland = 0; 88 static int opt_showcounter = 0; 89 90 /* for display */ 91 static char *term; 92 static struct winsize win; 93 static int nontty; 94 static struct termios termios_save; 95 static bool termios_saved; 96 static long top_interval = 1; 97 static bool do_redraw; 98 static u_int nshow; 99 100 /* for profiling and counting samples */ 101 static sig_atomic_t sigalrm; 102 static struct sym **ksyms; 103 static size_t nksyms; 104 static u_int nevent; 105 static const char *eventname[TPROF_MAXCOUNTERS]; 106 static size_t sizeof_sample_elm; 107 static rb_tree_t rb_tree_sample; 108 struct ptrarray sample_list[SAMPLE_MODE_NUM]; 109 static u_int sample_n_kern[SAMPLE_MODE_NUM]; 110 static u_int sample_n_user[SAMPLE_MODE_NUM]; 111 static u_int sample_event_width = 7; 112 static u_int *sample_cpu_width; /* [ncpu] */ 113 static uint32_t *sample_n_kern_per_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */ 114 static uint32_t *sample_n_user_per_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */ 115 static uint64_t *sample_n_per_event[SAMPLE_MODE_NUM]; /* [nevent] */ 116 static uint64_t *sample_n_per_event_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */ 117 118 /* raw event counter */ 119 static uint64_t *counters; /* counters[2][ncpu][nevent] */ 120 static u_int counters_i; 121 122 static void 123 reset_cursor_pos(void) 124 { 125 int i; 126 char *p; 127 128 if (nontty || term == NULL) 129 return; 130 131 printf("\r"); 132 133 /* cursor_up * n */ 134 if ((p = tigetstr("cuu")) != NULL) { 135 putp(tparm(p, win.ws_row - 1, 0, 0, 0, 0, 0, 0, 0, 0)); 136 } else if ((p = tigetstr("cuu1")) != NULL) { 137 for (i = win.ws_row - 1; i > 0; i--) 138 putp(p); 139 } 140 } 141 142 static void 143 clr_to_eol(void) 144 { 145 char *p; 146 147 if (nontty || term == NULL) 148 return; 149 150 if ((p = tigetstr("el")) != NULL) 151 putp(p); 152 } 153 154 /* newline, and clearing to end of line if needed */ 155 static void 156 lim_newline(int *lim) 157 { 158 if (*lim >= 1) 159 clr_to_eol(); 160 161 printf("\n"); 162 *lim = win.ws_col; 163 } 164 165 static int 166 lim_printf(int *lim, const char *fmt, ...) 167 { 168 va_list ap; 169 size_t written; 170 char *p; 171 172 if (*lim <= 0) 173 return 0; 174 175 p = malloc(*lim + 1); 176 if (p == NULL) 177 return -1; 178 179 va_start(ap, fmt); 180 vsnprintf(p, *lim + 1, fmt, ap); 181 va_end(ap); 182 183 written = strlen(p); 184 if (written == 0) { 185 free(p); 186 *lim = 0; 187 return 0; 188 } 189 190 fwrite(p, written, 1, stdout); 191 *lim -= written; 192 193 free(p); 194 return written; 195 } 196 197 static void 198 sigwinch_handler(int signo) 199 { 200 char *p; 201 202 win.ws_col = tigetnum("lines"); 203 win.ws_row = tigetnum("cols"); 204 205 nontty = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win); 206 if (nontty != 0) { 207 nontty = !isatty(STDOUT_FILENO); 208 win.ws_col = 65535; 209 win.ws_row = 65535; 210 } 211 212 if ((p = getenv("LINES")) != NULL) 213 win.ws_row = strtoul(p, NULL, 0); 214 if ((p = getenv("COLUMNS")) != NULL) 215 win.ws_col = strtoul(p, NULL, 0); 216 217 do_redraw = true; 218 } 219 220 static void 221 tty_setup(void) 222 { 223 struct termios termios; 224 225 term = getenv("TERM"); 226 if (term != NULL) 227 setupterm(term, 0, NULL); 228 229 sigwinch_handler(0); 230 231 if (tcgetattr(STDOUT_FILENO, &termios_save) == 0) { 232 termios_saved = true; 233 234 /* stty cbreak */ 235 termios = termios_save; 236 termios.c_iflag |= BRKINT|IXON|IMAXBEL; 237 termios.c_oflag |= OPOST; 238 termios.c_lflag |= ISIG|IEXTEN; 239 termios.c_lflag &= ~(ICANON|ECHO); 240 tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios); 241 } 242 } 243 244 static void 245 tty_restore(void) 246 { 247 if (termios_saved) { 248 tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios_save); 249 termios_saved = false; 250 } 251 } 252 253 static void 254 sigtstp_handler(int signo) 255 { 256 tty_restore(); 257 258 signal(SIGWINCH, SIG_DFL); 259 signal(SIGINT, SIG_DFL); 260 signal(SIGQUIT, SIG_DFL); 261 signal(SIGTERM, SIG_DFL); 262 signal(SIGTSTP, SIG_DFL); 263 kill(0, SIGTSTP); 264 nshow = 0; 265 } 266 267 static void 268 sigalrm_handler(int signo) 269 { 270 sigalrm = 1; 271 } 272 273 __dead static void 274 die(int signo) 275 { 276 tty_restore(); 277 printf("\n"); 278 279 exit(EXIT_SUCCESS); 280 } 281 282 __dead static void 283 die_errc(int status, int code, const char *fmt, ...) 284 { 285 va_list ap; 286 287 tty_restore(); 288 289 va_start(ap, fmt); 290 if (code == 0) 291 verrx(status, fmt, ap); 292 else 293 verrc(status, code, fmt, ap); 294 va_end(ap); 295 } 296 297 static void 298 ptrarray_push(struct ptrarray *ptrarray, void *ptr) 299 { 300 int error; 301 302 if (ptrarray->pa_inuse >= ptrarray->pa_allocnum) { 303 /* increase buffer */ 304 ptrarray->pa_allocnum += 1024; 305 error = reallocarr(&ptrarray->pa_ptrs, ptrarray->pa_allocnum, 306 sizeof(*ptrarray->pa_ptrs)); 307 if (error != 0) 308 die_errc(EXIT_FAILURE, error, "rellocarr failed"); 309 } 310 ptrarray->pa_ptrs[ptrarray->pa_inuse++] = ptr; 311 } 312 313 static void 314 ptrarray_iterate(struct ptrarray *ptrarray, void (*ifunc)(void *)) 315 { 316 size_t i; 317 318 for (i = 0; i < ptrarray->pa_inuse; i++) { 319 (*ifunc)(ptrarray->pa_ptrs[i]); 320 } 321 } 322 323 static void 324 ptrarray_clear(struct ptrarray *ptrarray) 325 { 326 ptrarray->pa_inuse = 0; 327 } 328 329 static int 330 sample_compare_key(void *ctx, const void *n1, const void *keyp) 331 { 332 const struct sample_elm *a1 = n1; 333 const struct sample_elm *a2 = (const struct sample_elm *)keyp; 334 return a1->addr - a2->addr; 335 } 336 337 static signed int 338 sample_compare_nodes(void *ctx, const void *n1, const void *n2) 339 { 340 const struct addr *a2 = n2; 341 return sample_compare_key(ctx, n1, a2); 342 } 343 344 static const rb_tree_ops_t sample_ops = { 345 .rbto_compare_nodes = sample_compare_nodes, 346 .rbto_compare_key = sample_compare_key 347 }; 348 349 static u_int 350 n_align(u_int n, u_int align) 351 { 352 return (n + align - 1) / align * align; 353 } 354 355 static void 356 sample_init(void) 357 { 358 const struct sample_elm *e; 359 int l, mode, n; 360 u_int size; 361 char buf[16]; 362 363 size = sizeof(struct sample_elm) + 364 sizeof(e->num_cpu[0]) * SAMPLE_MODE_NUM * ncpu; 365 sizeof_sample_elm = n_align(size, __alignof(struct sample_elm)); 366 367 sample_cpu_width = ecalloc(1, sizeof(*sample_cpu_width) * ncpu); 368 for (n = 0; n < ncpu; n++) { 369 sample_cpu_width[n] = 5; 370 l = snprintf(buf, sizeof(buf), "CPU%d", n); 371 if (sample_cpu_width[n] < (u_int)l) 372 sample_cpu_width[n] = l; 373 } 374 375 for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) { 376 sample_n_kern_per_cpu[mode] = ecalloc(1, 377 sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu); 378 sample_n_user_per_cpu[mode] = ecalloc(1, 379 sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu); 380 sample_n_per_event[mode] = ecalloc(1, 381 sizeof(typeof(*sample_n_per_event[mode])) * nevent); 382 sample_n_per_event_cpu[mode] = ecalloc(1, 383 sizeof(typeof(*sample_n_per_event_cpu[mode])) * 384 nevent * ncpu); 385 } 386 } 387 388 static void 389 sample_clear_instantaneous(void *arg) 390 { 391 struct sample_elm *e = (void *)arg; 392 393 e->num[SAMPLE_MODE_INSTANTANEOUS] = 0; 394 memset(SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS), 395 0, sizeof(e->num_cpu[0]) * ncpu); 396 } 397 398 static void 399 sample_reset(bool reset_accumulative) 400 { 401 int mode; 402 403 for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) { 404 if (mode == SAMPLE_MODE_ACCUMULATIVE && !reset_accumulative) 405 continue; 406 407 sample_n_kern[mode] = 0; 408 sample_n_user[mode] = 0; 409 memset(sample_n_kern_per_cpu[mode], 0, 410 sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu); 411 memset(sample_n_user_per_cpu[mode], 0, 412 sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu); 413 memset(sample_n_per_event[mode], 0, 414 sizeof(typeof(*sample_n_per_event[mode])) * nevent); 415 memset(sample_n_per_event_cpu[mode], 0, 416 sizeof(typeof(*sample_n_per_event_cpu[mode])) * 417 nevent * ncpu); 418 } 419 420 if (reset_accumulative) { 421 rb_tree_init(&rb_tree_sample, &sample_ops); 422 ptrarray_iterate(&sample_list[SAMPLE_MODE_ACCUMULATIVE], free); 423 ptrarray_clear(&sample_list[SAMPLE_MODE_ACCUMULATIVE]); 424 ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]); 425 } else { 426 ptrarray_iterate(&sample_list[SAMPLE_MODE_INSTANTANEOUS], 427 sample_clear_instantaneous); 428 ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]); 429 } 430 } 431 432 static int __unused 433 sample_sortfunc_accumulative(const void *a, const void *b) 434 { 435 struct sample_elm * const *ea = a; 436 struct sample_elm * const *eb = b; 437 return (*eb)->num[SAMPLE_MODE_ACCUMULATIVE] - 438 (*ea)->num[SAMPLE_MODE_ACCUMULATIVE]; 439 } 440 441 static int 442 sample_sortfunc_instantaneous(const void *a, const void *b) 443 { 444 struct sample_elm * const *ea = a; 445 struct sample_elm * const *eb = b; 446 return (*eb)->num[SAMPLE_MODE_INSTANTANEOUS] - 447 (*ea)->num[SAMPLE_MODE_INSTANTANEOUS]; 448 } 449 450 static void 451 sample_sort_accumulative(void) 452 { 453 qsort(sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_ptrs, 454 sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_inuse, 455 sizeof(struct sample_elm *), sample_sortfunc_accumulative); 456 } 457 458 static void 459 sample_sort_instantaneous(void) 460 { 461 qsort(sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_ptrs, 462 sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_inuse, 463 sizeof(struct sample_elm *), sample_sortfunc_instantaneous); 464 } 465 466 static void 467 sample_collect(tprof_sample_t *s) 468 { 469 struct sample_elm *e, *o; 470 const char *name; 471 size_t symid; 472 uint64_t addr, offset; 473 uint32_t flags = 0; 474 uint32_t eventid, cpuid; 475 int mode; 476 477 eventid = __SHIFTOUT(s->s_flags, TPROF_SAMPLE_COUNTER_MASK); 478 cpuid = s->s_cpuid; 479 480 if (eventid >= nevent) /* unknown event from tprof? */ 481 return; 482 483 for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) { 484 sample_n_per_event[mode][eventid]++; 485 sample_n_per_event_cpu[mode][nevent * cpuid + eventid]++; 486 } 487 488 if ((s->s_flags & TPROF_SAMPLE_INKERNEL) == 0) { 489 sample_n_user[SAMPLE_MODE_ACCUMULATIVE]++; 490 sample_n_user[SAMPLE_MODE_INSTANTANEOUS]++; 491 sample_n_user_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++; 492 sample_n_user_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++; 493 494 name = NULL; 495 addr = s->s_pid; /* XXX */ 496 flags |= SAMPLE_ELM_FLAGS_USER; 497 498 if (!opt_userland) 499 return; 500 } else { 501 sample_n_kern[SAMPLE_MODE_ACCUMULATIVE]++; 502 sample_n_kern[SAMPLE_MODE_INSTANTANEOUS]++; 503 sample_n_kern_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++; 504 sample_n_kern_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++; 505 506 name = ksymlookup(s->s_pc, &offset, &symid); 507 if (name != NULL) { 508 addr = ksyms[symid]->value; 509 } else { 510 addr = s->s_pc; 511 } 512 } 513 514 e = ecalloc(1, sizeof_sample_elm); 515 e->addr = addr; 516 e->name = name; 517 e->flags = flags; 518 e->num[SAMPLE_MODE_ACCUMULATIVE] = 1; 519 e->num[SAMPLE_MODE_INSTANTANEOUS] = 1; 520 SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_ACCUMULATIVE)[cpuid] = 1; 521 SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS)[cpuid] = 1; 522 o = rb_tree_insert_node(&rb_tree_sample, e); 523 if (o == e) { 524 /* new symbol. add to list for sort */ 525 ptrarray_push(&sample_list[SAMPLE_MODE_ACCUMULATIVE], o); 526 ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS], o); 527 } else { 528 /* already exists */ 529 free(e); 530 531 o->num[SAMPLE_MODE_ACCUMULATIVE]++; 532 if (o->num[SAMPLE_MODE_INSTANTANEOUS]++ == 0) { 533 /* new instantaneous symbols. add to list for sort */ 534 ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS], 535 o); 536 } 537 SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_ACCUMULATIVE)[cpuid]++; 538 SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_INSTANTANEOUS)[cpuid]++; 539 } 540 } 541 542 static void 543 show_tprof_stat(int *lim) 544 { 545 static struct tprof_stat tsbuf[2], *ts0, *ts; 546 static u_int ts_i = 0; 547 static int tprofstat_width[6]; 548 int ret, l; 549 char tmpbuf[128]; 550 551 ts0 = &tsbuf[ts_i++ & 1]; 552 ts = &tsbuf[ts_i & 1]; 553 ret = ioctl(devfd, TPROF_IOC_GETSTAT, ts); 554 if (ret == -1) 555 die_errc(EXIT_FAILURE, errno, "TPROF_IOC_GETSTAT"); 556 557 #define TS_PRINT(idx, label, _m) \ 558 do { \ 559 __CTASSERT(idx < __arraycount(tprofstat_width)); \ 560 lim_printf(lim, "%s", label); \ 561 l = snprintf(tmpbuf, sizeof(tmpbuf), "%"PRIu64, ts->_m);\ 562 if (ts->_m != ts0->_m) \ 563 l += snprintf(tmpbuf + l, sizeof(tmpbuf) - l, \ 564 "(+%"PRIu64")", ts->_m - ts0->_m); \ 565 assert(l < (int)sizeof(tmpbuf)); \ 566 if (tprofstat_width[idx] < l) \ 567 tprofstat_width[idx] = l; \ 568 lim_printf(lim, "%-*.*s ", tprofstat_width[idx], \ 569 tprofstat_width[idx], tmpbuf); \ 570 } while (0) 571 lim_printf(lim, "tprof "); 572 TS_PRINT(0, "sample:", ts_sample); 573 TS_PRINT(1, "overflow:", ts_overflow); 574 TS_PRINT(2, "buf:", ts_buf); 575 TS_PRINT(3, "emptybuf:", ts_emptybuf); 576 TS_PRINT(4, "dropbuf:", ts_dropbuf); 577 TS_PRINT(5, "dropbuf_sample:", ts_dropbuf_sample); 578 } 579 580 static void 581 show_timestamp(void) 582 { 583 struct timeval tv; 584 gettimeofday(&tv, NULL); 585 printf("%-8.8s", &(ctime((time_t *)&tv.tv_sec)[11])); 586 } 587 588 static void 589 show_counters_alloc(void) 590 { 591 size_t sz = 2 * ncpu * nevent * sizeof(*counters); 592 counters = ecalloc(1, sz); 593 } 594 595 static void 596 show_counters(int *lim) 597 { 598 tprof_counts_t countsbuf; 599 uint64_t *cn[2], *c0, *c; 600 u_int i; 601 int n, ret; 602 603 cn[0] = counters; 604 cn[1] = counters + ncpu * nevent; 605 c0 = cn[counters_i++ & 1]; 606 c = cn[counters_i & 1]; 607 608 for (n = 0; n < ncpu; n++) { 609 countsbuf.c_cpu = n; 610 ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &countsbuf); 611 if (ret == -1) 612 die_errc(EXIT_FAILURE, errno, "TPROF_IOC_GETCOUNTS"); 613 614 for (i = 0; i < nevent; i++) 615 c[n * nevent + i] = countsbuf.c_count[i]; 616 } 617 618 if (do_redraw) { 619 lim_printf(lim, "%-22s", "Event counter (delta)"); 620 for (n = 0; n < ncpu; n++) { 621 char cpuname[16]; 622 snprintf(cpuname, sizeof(cpuname), "CPU%u", n); 623 lim_printf(lim, "%11s", cpuname); 624 } 625 lim_newline(lim); 626 } else { 627 printf("\n"); 628 } 629 630 for (i = 0; i < nevent; i++) { 631 lim_printf(lim, "%-22.22s", eventname[i]); 632 for (n = 0; n < ncpu; n++) { 633 lim_printf(lim, "%11"PRIu64, 634 c[n * nevent + i] - c0[n * nevent + i]); 635 } 636 lim_newline(lim); 637 } 638 lim_newline(lim); 639 } 640 641 static void 642 show_count_per_event(int *lim) 643 { 644 u_int i, nsample_total; 645 int n, l; 646 char buf[32]; 647 648 nsample_total = sample_n_kern[opt_mode] + sample_n_user[opt_mode]; 649 if (nsample_total == 0) 650 nsample_total = 1; 651 652 /* calc width in advance */ 653 for (i = 0; i < nevent; i++) { 654 l = snprintf(buf, sizeof(buf), "%"PRIu64, 655 sample_n_per_event[opt_mode][i]); 656 if (sample_event_width < (u_int)l) { 657 sample_event_width = l; 658 do_redraw = true; 659 } 660 } 661 for (n = 0; n < ncpu; n++) { 662 uint64_t sum = 0; 663 for (i = 0; i < nevent; i++) 664 sum += sample_n_per_event_cpu[opt_mode][nevent * n + i]; 665 l = snprintf(buf, sizeof(buf), "%"PRIu64, sum); 666 if (sample_cpu_width[n] < (u_int)l) { 667 sample_cpu_width[n] = l; 668 do_redraw = true; 669 } 670 } 671 672 if (do_redraw) { 673 lim_printf(lim, " Rate %*s %-*s", 674 sample_event_width, "Sample#", 675 SYMBOL_LEN, "Eventname"); 676 for (n = 0; n < ncpu; n++) { 677 snprintf(buf, sizeof(buf), "CPU%d", n); 678 lim_printf(lim, " %*s", sample_cpu_width[n], buf); 679 } 680 lim_newline(lim); 681 682 lim_printf(lim, "------ %*.*s %*.*s", 683 sample_event_width, sample_event_width, LINESTR, 684 SYMBOL_LEN, SYMBOL_LEN, LINESTR); 685 for (n = 0; n < ncpu; n++) { 686 lim_printf(lim, " %*.*s", 687 sample_cpu_width[n], sample_cpu_width[n], LINESTR); 688 } 689 lim_newline(lim); 690 } else { 691 printf("\n\n"); 692 } 693 694 for (i = 0; i < nevent; i++) { 695 if (sample_n_per_event[opt_mode][i] >= nsample_total) { 696 lim_printf(lim, "%5.1f%%", 100.0 * 697 sample_n_per_event[opt_mode][i] / nsample_total); 698 } else { 699 lim_printf(lim, "%5.2f%%", 100.0 * 700 sample_n_per_event[opt_mode][i] / nsample_total); 701 } 702 lim_printf(lim, " %*"PRIu64" ", sample_event_width, 703 sample_n_per_event[opt_mode][i]); 704 705 lim_printf(lim, "%-32.32s", eventname[i]); 706 for (n = 0; n < ncpu; n++) { 707 lim_printf(lim, " %*"PRIu64, sample_cpu_width[n], 708 sample_n_per_event_cpu[opt_mode][nevent * n + i]); 709 } 710 lim_newline(lim); 711 } 712 } 713 714 static void 715 sample_show(void) 716 { 717 struct sample_elm *e; 718 struct ptrarray *samples; 719 u_int nsample_total; 720 int i, l, lim, n, ndisp; 721 char namebuf[32]; 722 const char *name; 723 724 if (nshow++ == 0) { 725 printf("\n"); 726 if (!nontty) { 727 signal(SIGWINCH, sigwinch_handler); 728 signal(SIGINT, die); 729 signal(SIGQUIT, die); 730 signal(SIGTERM, die); 731 signal(SIGTSTP, sigtstp_handler); 732 733 tty_setup(); 734 } 735 } else { 736 reset_cursor_pos(); 737 } 738 739 int margin_lines = 7; 740 741 margin_lines += 3 + nevent; /* show_counter_per_event() */ 742 743 if (opt_mode == SAMPLE_MODE_INSTANTANEOUS) 744 sample_sort_instantaneous(); 745 else 746 sample_sort_accumulative(); 747 samples = &sample_list[opt_mode]; 748 749 if (opt_showcounter) 750 margin_lines += 2 + nevent; 751 if (opt_userland) 752 margin_lines += 1; 753 754 ndisp = samples->pa_inuse; 755 if (!nontty && ndisp > (win.ws_row - margin_lines)) 756 ndisp = win.ws_row - margin_lines; 757 758 lim = win.ws_col; 759 if (opt_mode == SAMPLE_MODE_ACCUMULATIVE) 760 lim_printf(&lim, "[Accumulative mode] "); 761 show_tprof_stat(&lim); 762 763 if (lim >= 16) { 764 l = win.ws_col - lim; 765 if (!nontty) { 766 clr_to_eol(); 767 for (; l <= win.ws_col - 17; l = ((l + 8) & -8)) 768 printf("\t"); 769 } 770 show_timestamp(); 771 } 772 lim_newline(&lim); 773 lim_newline(&lim); 774 775 if (opt_showcounter) 776 show_counters(&lim); 777 778 show_count_per_event(&lim); 779 lim_newline(&lim); 780 781 if (do_redraw) { 782 lim_printf(&lim, " Rate %*s %-*s", 783 sample_event_width, "Sample#", 784 SYMBOL_LEN, "Symbol"); 785 for (n = 0; n < ncpu; n++) { 786 snprintf(namebuf, sizeof(namebuf), "CPU%d", n); 787 lim_printf(&lim, " %*s", sample_cpu_width[n], namebuf); 788 } 789 lim_newline(&lim); 790 791 lim_printf(&lim, "------ %*.*s %*.*s", 792 sample_event_width, sample_event_width, LINESTR, 793 SYMBOL_LEN, SYMBOL_LEN, LINESTR); 794 for (n = 0; n < ncpu; n++) { 795 lim_printf(&lim, " %*.*s", sample_cpu_width[n], 796 sample_cpu_width[n], LINESTR); 797 } 798 lim_newline(&lim); 799 } else { 800 printf("\n\n"); 801 } 802 803 for (i = 0; i < ndisp; i++) { 804 e = (struct sample_elm *)samples->pa_ptrs[i]; 805 name = e->name; 806 if (name == NULL) { 807 if (e->flags & SAMPLE_ELM_FLAGS_USER) { 808 snprintf(namebuf, sizeof(namebuf), 809 "<PID:%"PRIu64">", e->addr); 810 } else { 811 snprintf(namebuf, sizeof(namebuf), 812 "0x%016"PRIx64, e->addr); 813 } 814 name = namebuf; 815 } 816 817 nsample_total = sample_n_kern[opt_mode]; 818 if (opt_userland) 819 nsample_total += sample_n_user[opt_mode]; 820 /* 821 * even when only kernel mode events are configured, 822 * interrupts may still occur in the user mode state. 823 */ 824 if (nsample_total == 0) 825 nsample_total = 1; 826 827 if (e->num[opt_mode] >= nsample_total) { 828 lim_printf(&lim, "%5.1f%%", 100.0 * 829 e->num[opt_mode] / nsample_total); 830 } else { 831 lim_printf(&lim, "%5.2f%%", 100.0 * 832 e->num[opt_mode] / nsample_total); 833 } 834 lim_printf(&lim, " %*u %-32.32s", sample_event_width, 835 e->num[opt_mode], name); 836 837 for (n = 0; n < ncpu; n++) { 838 if (SAMPLE_ELM_NUM_CPU(e, opt_mode)[n] == 0) { 839 lim_printf(&lim, " %*s", sample_cpu_width[n], 840 "."); 841 } else { 842 lim_printf(&lim, " %*u", sample_cpu_width[n], 843 SAMPLE_ELM_NUM_CPU(e, opt_mode)[n]); 844 } 845 } 846 lim_newline(&lim); 847 } 848 849 if ((u_int)ndisp != samples->pa_inuse) { 850 lim_printf(&lim, " : %*s (more %zu symbols omitted)", 851 sample_event_width, ":", samples->pa_inuse - ndisp); 852 lim_newline(&lim); 853 } else if (!nontty) { 854 for (i = ndisp; i <= win.ws_row - margin_lines; i++) { 855 printf("~"); 856 lim_newline(&lim); 857 } 858 } 859 860 if (do_redraw) { 861 lim_printf(&lim, "------ %*.*s %*.*s", 862 sample_event_width, sample_event_width, LINESTR, 863 SYMBOL_LEN, SYMBOL_LEN, LINESTR); 864 for (n = 0; n < ncpu; n++) { 865 lim_printf(&lim, " %*.*s", 866 sample_cpu_width[n], sample_cpu_width[n], LINESTR); 867 } 868 lim_newline(&lim); 869 } else { 870 printf("\n"); 871 } 872 873 lim_printf(&lim, "Total %*u %-32.32s", 874 sample_event_width, sample_n_kern[opt_mode], "in-kernel"); 875 for (n = 0; n < ncpu; n++) { 876 lim_printf(&lim, " %*u", sample_cpu_width[n], 877 sample_n_kern_per_cpu[opt_mode][n]); 878 } 879 880 if (opt_userland) { 881 lim_newline(&lim); 882 lim_printf(&lim, " %*u %-32.32s", 883 sample_event_width, sample_n_user[opt_mode], "userland"); 884 for (n = 0; n < ncpu; n++) { 885 lim_printf(&lim, " %*u", sample_cpu_width[n], 886 sample_n_user_per_cpu[opt_mode][n]); 887 } 888 } 889 890 if (nontty) 891 printf("\n"); 892 else 893 clr_to_eol(); 894 } 895 896 __dead static void 897 tprof_top_usage(void) 898 { 899 fprintf(stderr, "%s top [-acu] [-e name[,scale] [-e ...]]" 900 " [-i interval]\n", getprogname()); 901 exit(EXIT_FAILURE); 902 } 903 904 __dead void 905 tprof_top(int argc, char **argv) 906 { 907 tprof_param_t params[TPROF_MAXCOUNTERS]; 908 struct itimerval it; 909 ssize_t tprof_bufsize, len; 910 u_int i; 911 int ch, ret; 912 char *tprof_buf, *p, *errmsg; 913 bool noinput = false; 914 915 memset(params, 0, sizeof(params)); 916 nevent = 0; 917 918 while ((ch = getopt(argc, argv, "ace:i:L:u")) != -1) { 919 switch (ch) { 920 case 'a': 921 opt_mode = SAMPLE_MODE_ACCUMULATIVE; 922 break; 923 case 'c': 924 opt_showcounter = 1; 925 break; 926 case 'e': 927 if (tprof_parse_event(¶ms[nevent], optarg, 928 TPROF_PARSE_EVENT_F_ALLOWSCALE, 929 &eventname[nevent], &errmsg) != 0) { 930 die_errc(EXIT_FAILURE, 0, "%s", errmsg); 931 } 932 nevent++; 933 if (nevent > __arraycount(params) || 934 nevent > ncounters) 935 die_errc(EXIT_FAILURE, 0, 936 "Too many events. Only a maximum of %d " 937 "counters can be used.", ncounters); 938 break; 939 case 'i': 940 top_interval = strtol(optarg, &p, 10); 941 if (*p != '\0' || top_interval <= 0) 942 die_errc(EXIT_FAILURE, 0, 943 "Bad/invalid interval: %s", optarg); 944 break; 945 case 'u': 946 opt_userland = 1; 947 break; 948 default: 949 tprof_top_usage(); 950 } 951 } 952 argc -= optind; 953 argv += optind; 954 955 if (argc != 0) 956 tprof_top_usage(); 957 958 if (nevent == 0) { 959 const char *defaultevent = tprof_cycle_event_name(); 960 if (defaultevent == NULL) 961 die_errc(EXIT_FAILURE, 0, "cpu not supported"); 962 963 tprof_event_lookup(defaultevent, ¶ms[nevent]); 964 eventname[nevent] = defaultevent; 965 nevent++; 966 } 967 968 sample_init(); 969 show_counters_alloc(); 970 971 for (i = 0; i < nevent; i++) { 972 params[i].p_counter = i; 973 params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE; 974 if (opt_userland) 975 params[i].p_flags |= TPROF_PARAM_USER; 976 ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, ¶ms[i]); 977 if (ret == -1) 978 die_errc(EXIT_FAILURE, errno, 979 "TPROF_IOC_CONFIGURE_EVENT: %s", eventname[i]); 980 } 981 982 tprof_countermask_t mask = TPROF_COUNTERMASK_ALL; 983 ret = ioctl(devfd, TPROF_IOC_START, &mask); 984 if (ret == -1) 985 die_errc(EXIT_FAILURE, errno, "TPROF_IOC_START"); 986 987 ksyms = ksymload(&nksyms); 988 989 signal(SIGALRM, sigalrm_handler); 990 991 it.it_interval.tv_sec = it.it_value.tv_sec = top_interval; 992 it.it_interval.tv_usec = it.it_value.tv_usec = 0; 993 setitimer(ITIMER_REAL, &it, NULL); 994 995 sample_reset(true); 996 printf("collecting samples..."); 997 fflush(stdout); 998 999 tprof_bufsize = sizeof(tprof_sample_t) * 1024 * 32; 1000 tprof_buf = emalloc(tprof_bufsize); 1001 do { 1002 bool force_update = false; 1003 1004 while (sigalrm == 0 && !force_update) { 1005 fd_set r; 1006 int nfound; 1007 char c; 1008 1009 FD_ZERO(&r); 1010 if (!noinput) 1011 FD_SET(STDIN_FILENO, &r); 1012 FD_SET(devfd, &r); 1013 nfound = select(devfd + 1, &r, NULL, NULL, NULL); 1014 if (nfound == -1) { 1015 if (errno == EINTR) 1016 break; 1017 die_errc(EXIT_FAILURE, errno, "select"); 1018 } 1019 1020 if (FD_ISSET(STDIN_FILENO, &r)) { 1021 len = read(STDIN_FILENO, &c, 1); 1022 if (len <= 0) { 1023 noinput = true; 1024 continue; 1025 } 1026 switch (c) { 1027 case 0x0c: /* ^L */ 1028 do_redraw = true; 1029 break; 1030 case 'a': 1031 /* toggle mode */ 1032 opt_mode = (opt_mode + 1) % 1033 SAMPLE_MODE_NUM; 1034 do_redraw = true; 1035 break; 1036 case 'c': 1037 /* toggle mode */ 1038 opt_showcounter ^= 1; 1039 do_redraw = true; 1040 break; 1041 case 'q': 1042 goto done; 1043 case 'z': 1044 sample_reset(true); 1045 break; 1046 default: 1047 continue; 1048 } 1049 force_update = true; 1050 } 1051 1052 if (FD_ISSET(devfd, &r)) { 1053 len = read(devfd, tprof_buf, tprof_bufsize); 1054 if (len == -1 && errno != EINTR) 1055 die_errc(EXIT_FAILURE, errno, "read"); 1056 if (len > 0) { 1057 tprof_sample_t *s = 1058 (tprof_sample_t *)tprof_buf; 1059 while (s < 1060 (tprof_sample_t *)(tprof_buf + len)) 1061 sample_collect(s++); 1062 } 1063 } 1064 } 1065 sigalrm = 0; 1066 1067 /* update screen */ 1068 sample_show(); 1069 fflush(stdout); 1070 do_redraw = false; 1071 if (force_update) 1072 continue; 1073 1074 sample_reset(false); 1075 1076 } while (!nontty); 1077 1078 done: 1079 die(0); 1080 } 1081