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