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