1 /* $OpenBSD: btrace.c,v 1.46 2021/09/02 17:21:39 jasper Exp $ */ 2 3 /* 4 * Copyright (c) 2019 - 2021 Martin Pieuchot <mpi@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/ioctl.h> 20 #include <sys/exec_elf.h> 21 #include <sys/syscall.h> 22 #include <sys/queue.h> 23 24 #include <assert.h> 25 #include <err.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <limits.h> 29 #include <locale.h> 30 #include <signal.h> 31 #include <stdarg.h> 32 #include <stdbool.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <time.h> 37 #include <unistd.h> 38 39 #include <dev/dt/dtvar.h> 40 41 #include "btrace.h" 42 #include "bt_parser.h" 43 44 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 45 #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 46 47 /* 48 * Maximum number of operands an arithmetic operation can have. This 49 * is necessary to stop infinite recursion when evaluating expressions. 50 */ 51 #define __MAXOPERANDS 5 52 53 #define __PATH_DEVDT "/dev/dt" 54 55 __dead void usage(void); 56 char *read_btfile(const char *); 57 58 /* 59 * Retrieve & parse probe information. 60 */ 61 void dtpi_cache(int); 62 void dtpi_print_list(void); 63 char *dtpi_func(struct dtioc_probe_info *); 64 int dtpi_is_unit(const char *); 65 struct dtioc_probe_info *dtpi_get_by_value(const char *, const char *, 66 const char *); 67 68 /* 69 * Main loop and rule evaluation. 70 */ 71 void rules_do(int); 72 void rules_setup(int); 73 void rules_apply(struct dt_evt *); 74 void rules_teardown(int); 75 void rule_eval(struct bt_rule *, struct dt_evt *); 76 void rule_printmaps(struct bt_rule *); 77 78 /* 79 * Language builtins & functions. 80 */ 81 uint64_t builtin_nsecs(struct dt_evt *); 82 const char *builtin_kstack(struct dt_evt *); 83 const char *builtin_arg(struct dt_evt *, enum bt_argtype); 84 void stmt_eval(struct bt_stmt *, struct dt_evt *); 85 void stmt_bucketize(struct bt_stmt *, struct dt_evt *); 86 void stmt_clear(struct bt_stmt *); 87 void stmt_delete(struct bt_stmt *, struct dt_evt *); 88 void stmt_insert(struct bt_stmt *, struct dt_evt *); 89 void stmt_print(struct bt_stmt *, struct dt_evt *); 90 void stmt_store(struct bt_stmt *, struct dt_evt *); 91 bool stmt_test(struct bt_stmt *, struct dt_evt *); 92 void stmt_time(struct bt_stmt *, struct dt_evt *); 93 void stmt_zero(struct bt_stmt *); 94 struct bt_arg *ba_read(struct bt_arg *); 95 const char *ba2hash(struct bt_arg *, struct dt_evt *); 96 long baexpr2long(struct bt_arg *, struct dt_evt *); 97 const char *ba2bucket(struct bt_arg *, struct bt_arg *, 98 struct dt_evt *, long *); 99 int ba2dtflags(struct bt_arg *); 100 101 /* 102 * Debug routines. 103 */ 104 __dead void xabort(const char *, ...); 105 void debug(const char *, ...); 106 void debugx(const char *, ...); 107 const char *debug_rule_name(struct bt_rule *); 108 void debug_dump_term(struct bt_arg *); 109 void debug_dump_expr(struct bt_arg *); 110 void debug_dump_filter(struct bt_rule *); 111 112 struct dtioc_probe_info *dt_dtpis; /* array of available probes */ 113 size_t dt_ndtpi; /* # of elements in the array */ 114 115 struct dt_evt bt_devt; /* fake event for BEGIN/END */ 116 uint64_t bt_filtered; /* # of events filtered out */ 117 118 int vargs[1]; 119 int verbose = 0; 120 volatile sig_atomic_t quit_pending; 121 122 static void 123 signal_handler(int sig) 124 { 125 quit_pending = sig; 126 } 127 128 129 int 130 main(int argc, char *argv[]) 131 { 132 int fd = -1, ch, error = 0; 133 const char *filename = NULL, *btscript = NULL; 134 const char *errstr; 135 int showprobes = 0, noaction = 0; 136 137 setlocale(LC_ALL, ""); 138 139 #if notyet 140 if (pledge("stdio rpath", NULL) == -1) 141 err(1, "pledge"); 142 #endif 143 144 while ((ch = getopt(argc, argv, "e:lnp:v")) != -1) { 145 switch (ch) { 146 case 'e': 147 btscript = optarg; 148 break; 149 case 'l': 150 showprobes = 1; 151 break; 152 case 'n': 153 noaction = 1; 154 break; 155 case 'v': 156 verbose++; 157 break; 158 default: 159 usage(); 160 } 161 } 162 163 argc -= optind; 164 argv += optind; 165 166 if (argc > 0) { 167 if (btscript != NULL) 168 usage(); 169 170 filename = argv[0]; 171 btscript = read_btfile(filename); 172 argc--; 173 argv++; 174 } 175 176 if (argc == 1) { 177 vargs[0] = strtonum(*argv, 1, INT_MAX, &errstr); 178 if (errstr != NULL) 179 errx(1, "invalid argument %s: %s", *argv, errstr); 180 argc--; 181 argv++; 182 } 183 184 if (argc != 0 || (btscript == NULL && !showprobes)) 185 usage(); 186 187 if (btscript != NULL) { 188 error = btparse(btscript, strlen(btscript), filename, 1); 189 if (error) 190 return error; 191 } 192 193 if (noaction) 194 return error; 195 196 if (showprobes || g_nprobes > 0) { 197 fd = open(__PATH_DEVDT, O_RDONLY); 198 if (fd == -1) 199 err(1, "could not open %s", __PATH_DEVDT); 200 } 201 202 if (showprobes) { 203 dtpi_cache(fd); 204 dtpi_print_list(); 205 } 206 207 if (!TAILQ_EMPTY(&g_rules)) 208 rules_do(fd); 209 210 if (fd != -1) 211 close(fd); 212 213 return error; 214 } 215 216 __dead void 217 usage(void) 218 { 219 fprintf(stderr, "usage: %s [-lnv] [-e program | file]\n", 220 getprogname()); 221 exit(1); 222 } 223 224 char * 225 read_btfile(const char *filename) 226 { 227 static char fcontent[BUFSIZ]; 228 long offset; 229 FILE *fp; 230 231 fp = fopen(filename, "r"); 232 if (fp == NULL) 233 err(1, "can't open '%s'", filename); 234 235 if (fread(fcontent, sizeof(fcontent) - 1, 1, fp) == 0 && errno != 0) 236 err(1, "can't read '%s'", filename); 237 238 fseek(fp, 0, SEEK_END); 239 offset = ftell(fp); 240 if ((size_t)offset >= sizeof(fcontent)) 241 errx(1, "couldn't read all of '%s'", filename); 242 243 fclose(fp); 244 return fcontent; 245 } 246 247 void 248 dtpi_cache(int fd) 249 { 250 struct dtioc_probe dtpr; 251 252 if (dt_dtpis != NULL) 253 return; 254 255 memset(&dtpr, 0, sizeof(dtpr)); 256 if (ioctl(fd, DTIOCGPLIST, &dtpr)) 257 err(1, "DTIOCGPLIST"); 258 259 dt_ndtpi = (dtpr.dtpr_size / sizeof(*dt_dtpis)); 260 dt_dtpis = reallocarray(NULL, dt_ndtpi, sizeof(*dt_dtpis)); 261 if (dt_dtpis == NULL) 262 err(1, "malloc"); 263 264 dtpr.dtpr_probes = dt_dtpis; 265 if (ioctl(fd, DTIOCGPLIST, &dtpr)) 266 err(1, "DTIOCGPLIST"); 267 } 268 269 void 270 dtpi_print_list(void) 271 { 272 struct dtioc_probe_info *dtpi; 273 size_t i; 274 275 dtpi = dt_dtpis; 276 for (i = 0; i < dt_ndtpi; i++, dtpi++) { 277 printf("%s:%s:%s\n", dtpi->dtpi_prov, dtpi_func(dtpi), 278 dtpi->dtpi_name); 279 } 280 } 281 282 char * 283 dtpi_func(struct dtioc_probe_info *dtpi) 284 { 285 char *sysnb, func[DTNAMESIZE]; 286 const char *errstr; 287 int idx; 288 289 if (strncmp(dtpi->dtpi_prov, "syscall", DTNAMESIZE)) 290 return dtpi->dtpi_func; 291 292 /* Translate syscall names */ 293 strlcpy(func, dtpi->dtpi_func, sizeof(func)); 294 sysnb = func; 295 if (strsep(&sysnb, "%") == NULL) 296 return dtpi->dtpi_func; 297 298 idx = strtonum(sysnb, 1, SYS_MAXSYSCALL, &errstr); 299 if (errstr != NULL) 300 return dtpi->dtpi_func; 301 302 return syscallnames[idx]; 303 } 304 305 int 306 dtpi_is_unit(const char *unit) 307 { 308 return !strncmp("hz", unit, sizeof("hz")); 309 } 310 311 struct dtioc_probe_info * 312 dtpi_get_by_value(const char *prov, const char *func, const char *name) 313 { 314 struct dtioc_probe_info *dtpi; 315 size_t i; 316 317 dtpi = dt_dtpis; 318 for (i = 0; i < dt_ndtpi; i++, dtpi++) { 319 if (prov != NULL && 320 strncmp(prov, dtpi->dtpi_prov, DTNAMESIZE)) 321 continue; 322 323 if (func != NULL) { 324 if (dtpi_is_unit(func)) 325 return dtpi; 326 327 if (strncmp(func, dtpi_func(dtpi), DTNAMESIZE)) 328 continue; 329 } 330 331 if (strncmp(name, dtpi->dtpi_name, DTNAMESIZE)) 332 continue; 333 334 debug("matched probe %s:%s:%s\n", dtpi->dtpi_prov, 335 dtpi_func(dtpi), dtpi->dtpi_name); 336 return dtpi; 337 } 338 339 return NULL; 340 } 341 342 void 343 rules_do(int fd) 344 { 345 struct sigaction sa; 346 347 memset(&sa, 0, sizeof(sa)); 348 sigemptyset(&sa.sa_mask); 349 sa.sa_flags = 0; 350 sa.sa_handler = signal_handler; 351 if (sigaction(SIGINT, &sa, NULL)) 352 err(1, "sigaction"); 353 354 rules_setup(fd); 355 356 while (!quit_pending && g_nprobes > 0) { 357 static struct dt_evt devtbuf[64]; 358 ssize_t rlen; 359 size_t i; 360 361 rlen = read(fd, devtbuf, sizeof(devtbuf) - 1); 362 if (rlen == -1) { 363 if (errno == EINTR && quit_pending) { 364 printf("\n"); 365 break; 366 } 367 err(1, "read"); 368 } 369 370 if ((rlen % sizeof(struct dt_evt)) != 0) 371 err(1, "incorrect read"); 372 373 for (i = 0; i < rlen / sizeof(struct dt_evt); i++) 374 rules_apply(&devtbuf[i]); 375 } 376 377 rules_teardown(fd); 378 379 if (verbose && fd != -1) { 380 struct dtioc_stat dtst; 381 382 memset(&dtst, 0, sizeof(dtst)); 383 if (ioctl(fd, DTIOCGSTATS, &dtst)) 384 warn("DTIOCGSTATS"); 385 386 printf("%llu events read\n", dtst.dtst_readevt); 387 printf("%llu events dropped\n", dtst.dtst_dropevt); 388 printf("%llu events filtered\n", bt_filtered); 389 } 390 } 391 392 void 393 rules_setup(int fd) 394 { 395 struct dtioc_probe_info *dtpi; 396 struct dtioc_req *dtrq; 397 struct bt_rule *r, *rbegin = NULL; 398 struct bt_probe *bp; 399 struct bt_stmt *bs; 400 int dokstack = 0, on = 1; 401 402 TAILQ_FOREACH(r, &g_rules, br_next) { 403 debug("parsed probe '%s'", debug_rule_name(r)); 404 debug_dump_filter(r); 405 406 if (r->br_type != B_RT_PROBE) { 407 if (r->br_type == B_RT_BEGIN) 408 rbegin = r; 409 continue; 410 } 411 412 bp = r->br_probe; 413 dtpi_cache(fd); 414 dtpi = dtpi_get_by_value(bp->bp_prov, bp->bp_func, bp->bp_name); 415 if (dtpi == NULL) { 416 errx(1, "probe '%s:%s:%s' not found", bp->bp_prov, 417 bp->bp_func, bp->bp_name); 418 } 419 420 dtrq = calloc(1, sizeof(*dtrq)); 421 if (dtrq == NULL) 422 err(1, "dtrq: 1alloc"); 423 424 r->br_pbn = dtpi->dtpi_pbn; 425 dtrq->dtrq_pbn = dtpi->dtpi_pbn; 426 dtrq->dtrq_rate = r->br_probe->bp_rate; 427 428 SLIST_FOREACH(bs, &r->br_action, bs_next) { 429 struct bt_arg *ba; 430 431 SLIST_FOREACH(ba, &bs->bs_args, ba_next) 432 dtrq->dtrq_evtflags |= ba2dtflags(ba); 433 434 /* Also check the value for map/hist insertion */ 435 switch (bs->bs_act) { 436 case B_AC_BUCKETIZE: 437 case B_AC_INSERT: 438 ba = (struct bt_arg *)bs->bs_var; 439 dtrq->dtrq_evtflags |= ba2dtflags(ba); 440 break; 441 default: 442 break; 443 } 444 } 445 446 if (dtrq->dtrq_evtflags & DTEVT_KSTACK) 447 dokstack = 1; 448 r->br_cookie = dtrq; 449 } 450 451 if (dokstack) 452 kelf_open(); 453 454 /* Initialize "fake" event for BEGIN/END */ 455 bt_devt.dtev_pbn = -1; 456 memcpy(&bt_devt.dtev_comm, getprogname(), sizeof(bt_devt.dtev_comm)); 457 bt_devt.dtev_pid = getpid(); 458 bt_devt.dtev_tid = getthrid(); 459 clock_gettime(CLOCK_REALTIME, &bt_devt.dtev_tsp); 460 461 if (rbegin) 462 rule_eval(rbegin, &bt_devt); 463 464 /* Enable all probes */ 465 TAILQ_FOREACH(r, &g_rules, br_next) { 466 if (r->br_type != B_RT_PROBE) 467 continue; 468 469 dtrq = r->br_cookie; 470 if (ioctl(fd, DTIOCPRBENABLE, dtrq)) 471 err(1, "DTIOCPRBENABLE"); 472 } 473 474 if (g_nprobes > 0) { 475 if (ioctl(fd, DTIOCRECORD, &on)) 476 err(1, "DTIOCRECORD"); 477 } 478 } 479 480 void 481 rules_apply(struct dt_evt *dtev) 482 { 483 struct bt_rule *r; 484 485 TAILQ_FOREACH(r, &g_rules, br_next) { 486 if (r->br_type != B_RT_PROBE || r->br_pbn != dtev->dtev_pbn) 487 continue; 488 489 rule_eval(r, dtev); 490 } 491 } 492 493 void 494 rules_teardown(int fd) 495 { 496 struct dtioc_req *dtrq; 497 struct bt_rule *r, *rend = NULL; 498 int dokstack = 0, off = 0; 499 500 if (g_nprobes > 0) { 501 if (ioctl(fd, DTIOCRECORD, &off)) 502 err(1, "DTIOCRECORD"); 503 } 504 505 TAILQ_FOREACH(r, &g_rules, br_next) { 506 if (r->br_type != B_RT_PROBE) { 507 if (r->br_type == B_RT_END) 508 rend = r; 509 continue; 510 } 511 512 dtrq = r->br_cookie; 513 if (dtrq->dtrq_evtflags & DTEVT_KSTACK) 514 dokstack = 1; 515 } 516 517 if (dokstack) 518 kelf_close(); 519 520 /* Update "fake" event for BEGIN/END */ 521 clock_gettime(CLOCK_REALTIME, &bt_devt.dtev_tsp); 522 523 if (rend) 524 rule_eval(rend, &bt_devt); 525 526 /* Print non-empty map & hist */ 527 TAILQ_FOREACH(r, &g_rules, br_next) 528 rule_printmaps(r); 529 } 530 531 void 532 rule_eval(struct bt_rule *r, struct dt_evt *dtev) 533 { 534 struct bt_stmt *bs; 535 536 debug("eval rule '%s'", debug_rule_name(r)); 537 debug_dump_filter(r); 538 539 if (r->br_filter != NULL && r->br_filter->bf_condition != NULL) { 540 if (stmt_test(r->br_filter->bf_condition, dtev) == false) { 541 bt_filtered++; 542 return; 543 } 544 } 545 546 SLIST_FOREACH(bs, &r->br_action, bs_next) { 547 if ((bs->bs_act == B_AC_TEST) && stmt_test(bs, dtev) == true) 548 stmt_eval((struct bt_stmt *)bs->bs_var, dtev); 549 550 stmt_eval(bs, dtev); 551 } 552 } 553 554 void 555 rule_printmaps(struct bt_rule *r) 556 { 557 struct bt_stmt *bs; 558 559 SLIST_FOREACH(bs, &r->br_action, bs_next) { 560 struct bt_arg *ba; 561 562 SLIST_FOREACH(ba, &bs->bs_args, ba_next) { 563 struct bt_var *bv = ba->ba_value; 564 struct map *map; 565 566 if (ba->ba_type != B_AT_MAP && ba->ba_type != B_AT_HIST) 567 continue; 568 569 map = (struct map *)bv->bv_value; 570 if (map == NULL) 571 continue; 572 573 if (ba->ba_type == B_AT_MAP) 574 map_print(map, SIZE_T_MAX, bv_name(bv)); 575 else 576 hist_print((struct hist *)map, bv_name(bv)); 577 map_clear(map); 578 bv->bv_value = NULL; 579 } 580 } 581 } 582 583 time_t 584 builtin_gettime(struct dt_evt *dtev) 585 { 586 struct timespec ts; 587 588 if (dtev == NULL) { 589 clock_gettime(CLOCK_REALTIME, &ts); 590 return ts.tv_sec; 591 } 592 593 return dtev->dtev_tsp.tv_sec; 594 } 595 596 static inline uint64_t 597 TIMESPEC_TO_NSEC(struct timespec *ts) 598 { 599 return (ts->tv_sec * 1000000000L + ts->tv_nsec); 600 } 601 602 uint64_t 603 builtin_nsecs(struct dt_evt *dtev) 604 { 605 struct timespec ts; 606 607 if (dtev == NULL) { 608 clock_gettime(CLOCK_REALTIME, &ts); 609 return TIMESPEC_TO_NSEC(&ts); 610 } 611 612 return TIMESPEC_TO_NSEC(&dtev->dtev_tsp); 613 } 614 615 const char * 616 builtin_stack(struct dt_evt *dtev, int kernel) 617 { 618 struct stacktrace *st = &dtev->dtev_kstack; 619 static char buf[4096], *bp; 620 size_t i; 621 int sz; 622 623 if (!kernel) 624 return ""; 625 if (st->st_count == 0) 626 return "\nuserland\n"; 627 628 buf[0] = '\0'; 629 bp = buf; 630 sz = sizeof(buf); 631 for (i = 0; i < st->st_count; i++) { 632 int l; 633 634 l = kelf_snprintsym(bp, sz - 1, st->st_pc[i]); 635 if (l < 0) 636 break; 637 if (l >= sz - 1) { 638 bp += sz - 1; 639 sz = 1; 640 break; 641 } 642 bp += l; 643 sz -= l; 644 } 645 snprintf(bp, sz, "\nkernel\n"); 646 647 return buf; 648 } 649 650 const char * 651 builtin_arg(struct dt_evt *dtev, enum bt_argtype dat) 652 { 653 static char buf[sizeof("18446744073709551615")]; /* UINT64_MAX */ 654 655 snprintf(buf, sizeof(buf), "%lu", 656 dtev->dtev_args[dat - B_AT_BI_ARG0]); 657 658 return buf; 659 } 660 661 void 662 stmt_eval(struct bt_stmt *bs, struct dt_evt *dtev) 663 { 664 switch (bs->bs_act) { 665 case B_AC_BUCKETIZE: 666 stmt_bucketize(bs, dtev); 667 break; 668 case B_AC_CLEAR: 669 stmt_clear(bs); 670 break; 671 case B_AC_DELETE: 672 stmt_delete(bs, dtev); 673 break; 674 case B_AC_EXIT: 675 exit(0); 676 break; 677 case B_AC_INSERT: 678 stmt_insert(bs, dtev); 679 break; 680 case B_AC_PRINT: 681 stmt_print(bs, dtev); 682 break; 683 case B_AC_PRINTF: 684 stmt_printf(bs, dtev); 685 break; 686 case B_AC_STORE: 687 stmt_store(bs, dtev); 688 break; 689 case B_AC_TEST: 690 /* done before */ 691 break; 692 case B_AC_TIME: 693 stmt_time(bs, dtev); 694 break; 695 case B_AC_ZERO: 696 stmt_zero(bs); 697 break; 698 default: 699 xabort("no handler for action type %d", bs->bs_act); 700 } 701 } 702 703 /* 704 * Increment a bucket: { @h = hist(v); } or { @h = lhist(v, min, max, step); } 705 * 706 * In this case 'h' is represented by `bv' and '(min, max, step)' by `brange'. 707 */ 708 void 709 stmt_bucketize(struct bt_stmt *bs, struct dt_evt *dtev) 710 { 711 struct bt_arg *brange, *bhist = SLIST_FIRST(&bs->bs_args); 712 struct bt_arg *bval = (struct bt_arg *)bs->bs_var; 713 struct bt_var *bv = bhist->ba_value; 714 const char *bucket; 715 long step = 0; 716 717 assert(bhist->ba_type == B_AT_HIST); 718 assert(SLIST_NEXT(bval, ba_next) == NULL); 719 720 brange = bhist->ba_key; 721 bucket = ba2bucket(bval, brange, dtev, &step); 722 if (bucket == NULL) { 723 debug("hist=%p '%s' value=%lu out of range\n", bv->bv_value, 724 bv_name(bv), ba2long(bval, dtev)); 725 return; 726 } 727 debug("hist=%p '%s' increment bucket '%s'\n", bv->bv_value, 728 bv_name(bv), bucket); 729 730 bv->bv_value = (struct bt_arg *) 731 hist_increment((struct hist *)bv->bv_value, bucket, step); 732 bv->bv_type = B_VT_HIST; 733 } 734 735 736 /* 737 * Empty a map: { clear(@map); } 738 */ 739 void 740 stmt_clear(struct bt_stmt *bs) 741 { 742 struct bt_arg *ba = SLIST_FIRST(&bs->bs_args); 743 struct bt_var *bv = ba->ba_value; 744 745 assert(bs->bs_var == NULL); 746 assert(ba->ba_type == B_AT_VAR); 747 748 map_clear((struct map *)bv->bv_value); 749 bv->bv_value = NULL; 750 751 debug("map=%p '%s' clear\n", bv->bv_value, bv_name(bv)); 752 } 753 754 /* 755 * Map delete: { delete(@map[key]); } 756 * 757 * In this case 'map' is represented by `bv' and 'key' by `bkey'. 758 */ 759 void 760 stmt_delete(struct bt_stmt *bs, struct dt_evt *dtev) 761 { 762 struct bt_arg *bkey, *bmap = SLIST_FIRST(&bs->bs_args); 763 struct bt_var *bv = bmap->ba_value; 764 const char *hash; 765 766 assert(bmap->ba_type == B_AT_MAP); 767 assert(bs->bs_var == NULL); 768 769 bkey = bmap->ba_key; 770 hash = ba2hash(bkey, dtev); 771 debug("map=%p '%s' delete key=%p '%s'\n", bv->bv_value, bv_name(bv), 772 bkey, hash); 773 774 map_delete((struct map *)bv->bv_value, hash); 775 } 776 777 /* 778 * Map insert: { @map[key] = 42; } 779 * 780 * In this case 'map' is represented by `bv', 'key' by `bkey' and 781 * '42' by `bval'. 782 */ 783 void 784 stmt_insert(struct bt_stmt *bs, struct dt_evt *dtev) 785 { 786 struct bt_arg *bkey, *bmap = SLIST_FIRST(&bs->bs_args); 787 struct bt_arg *bval = (struct bt_arg *)bs->bs_var; 788 struct bt_var *bv = bmap->ba_value; 789 const char *hash; 790 791 assert(bmap->ba_type == B_AT_MAP); 792 assert(SLIST_NEXT(bval, ba_next) == NULL); 793 794 bkey = bmap->ba_key; 795 hash = ba2hash(bkey, dtev); 796 debug("map=%p '%s' insert key=%p '%s' bval=%p\n", bv->bv_value, 797 bv_name(bv), bkey, hash, bval); 798 799 bv->bv_value = (struct bt_arg *)map_insert((struct map *)bv->bv_value, 800 hash, bval, dtev); 801 bv->bv_type = B_VT_MAP; 802 } 803 804 /* 805 * Print variables: { print(890); print(@map[, 8]); print(comm); } 806 * 807 * In this case the global variable 'map' is pointed at by `ba' 808 * and '8' is represented by `btop'. 809 */ 810 void 811 stmt_print(struct bt_stmt *bs, struct dt_evt *dtev) 812 { 813 struct bt_arg *btop, *ba = SLIST_FIRST(&bs->bs_args); 814 struct bt_var *bv = ba->ba_value; 815 size_t top = SIZE_T_MAX; 816 817 assert(bs->bs_var == NULL); 818 819 /* Parse optional `top' argument. */ 820 btop = SLIST_NEXT(ba, ba_next); 821 if (btop != NULL) { 822 assert(SLIST_NEXT(btop, ba_next) == NULL); 823 top = ba2long(btop, dtev); 824 } 825 826 /* Static argument. */ 827 if (ba->ba_type != B_AT_VAR) { 828 assert(btop == NULL); 829 printf("%s\n", ba2str(ba, dtev)); 830 return; 831 } 832 833 debug("map=%p '%s' print (top=%d)\n", bv->bv_value, bv_name(bv), top); 834 835 /* Empty? */ 836 if (bv->bv_value == NULL) 837 return; 838 839 if (bv->bv_type == B_VT_MAP) 840 map_print((struct map *)bv->bv_value, top, bv_name(bv)); 841 else if (bv->bv_type == B_VT_HIST) 842 hist_print((struct hist *)bv->bv_value, bv_name(bv)); 843 else 844 printf("%s\n", ba2str(ba, dtev)); 845 } 846 847 /* 848 * Variable store: { @var = 3; } 849 * 850 * In this case '3' is represented by `ba', the argument of a STORE 851 * action. 852 * 853 * If the argument depends of the value of an event (builtin) or is 854 * the result of an operation, its evaluation is stored in a new `ba'. 855 */ 856 void 857 stmt_store(struct bt_stmt *bs, struct dt_evt *dtev) 858 { 859 struct bt_arg *ba = SLIST_FIRST(&bs->bs_args); 860 struct bt_var *bv = bs->bs_var; 861 862 assert(SLIST_NEXT(ba, ba_next) == NULL); 863 864 switch (ba->ba_type) { 865 case B_AT_STR: 866 bv->bv_value = ba; 867 bv->bv_type = B_VT_STR; 868 break; 869 case B_AT_LONG: 870 bv->bv_value = ba; 871 bv->bv_type = B_VT_LONG; 872 break; 873 case B_AT_BI_NSECS: 874 bv->bv_value = ba_new(builtin_nsecs(dtev), B_AT_LONG); 875 bv->bv_type = B_VT_LONG; 876 break; 877 case B_AT_OP_PLUS ... B_AT_OP_LOR: 878 bv->bv_value = ba_new(ba2long(ba, dtev), B_AT_LONG); 879 bv->bv_type = B_VT_LONG; 880 break; 881 default: 882 xabort("store not implemented for type %d", ba->ba_type); 883 } 884 885 debug("bv=%p var '%s' store (%p)\n", bv, bv_name(bv), bv->bv_value); 886 } 887 888 /* 889 * Expression test: { if (expr) stmt; } 890 */ 891 bool 892 stmt_test(struct bt_stmt *bs, struct dt_evt *dtev) 893 { 894 struct bt_arg *ba; 895 896 if (bs == NULL) 897 return true; 898 899 ba = SLIST_FIRST(&bs->bs_args); 900 901 return baexpr2long(ba, dtev) != 0; 902 } 903 904 /* 905 * Print time: { time("%H:%M:%S"); } 906 */ 907 void 908 stmt_time(struct bt_stmt *bs, struct dt_evt *dtev) 909 { 910 struct bt_arg *ba = SLIST_FIRST(&bs->bs_args); 911 time_t time; 912 struct tm *tm; 913 char buf[64]; 914 915 assert(bs->bs_var == NULL); 916 assert(ba->ba_type == B_AT_STR); 917 assert(strlen(ba2str(ba, dtev)) < (sizeof(buf) - 1)); 918 919 time = builtin_gettime(dtev); 920 tm = localtime(&time); 921 strftime(buf, sizeof(buf), ba2str(ba, dtev), tm); 922 printf("%s", buf); 923 } 924 925 /* 926 * Set entries to 0: { zero(@map); } 927 */ 928 void 929 stmt_zero(struct bt_stmt *bs) 930 { 931 struct bt_arg *ba = SLIST_FIRST(&bs->bs_args); 932 struct bt_var *bv = ba->ba_value; 933 934 assert(bs->bs_var == NULL); 935 assert(ba->ba_type == B_AT_VAR); 936 937 map_zero((struct map *)bv->bv_value); 938 939 debug("map=%p '%s' zero\n", bv->bv_value, bv_name(bv)); 940 } 941 942 struct bt_arg * 943 ba_read(struct bt_arg *ba) 944 { 945 struct bt_var *bv = ba->ba_value; 946 947 assert(ba->ba_type == B_AT_VAR); 948 949 debug("bv=%p read '%s' (%p)\n", bv, bv_name(bv), bv->bv_value); 950 951 /* Handle map/hist access after clear(). */ 952 if (bv->bv_value == NULL) 953 return &g_nullba; 954 955 return bv->bv_value; 956 } 957 958 const char * 959 ba2hash(struct bt_arg *ba, struct dt_evt *dtev) 960 { 961 static char buf[KLEN]; 962 char *hash; 963 int l, len; 964 965 buf[0] = '\0'; 966 l = snprintf(buf, sizeof(buf), "%s", ba2str(ba, dtev)); 967 if (l < 0 || (size_t)l > sizeof(buf)) { 968 warn("string too long %d > %lu", l, sizeof(buf)); 969 return buf; 970 } 971 972 len = 0; 973 while ((ba = SLIST_NEXT(ba, ba_next)) != NULL) { 974 len += l; 975 hash = buf + len; 976 977 l = snprintf(hash, sizeof(buf) - len, ", %s", ba2str(ba, dtev)); 978 if (l < 0 || (size_t)l > (sizeof(buf) - len)) { 979 warn("hash too long %d > %lu", l + len, sizeof(buf)); 980 break; 981 } 982 } 983 984 return buf; 985 } 986 987 static unsigned long 988 next_pow2(unsigned long x) 989 { 990 size_t i; 991 992 x--; 993 for (i = 0; i < (sizeof(x) * 8) - 1; i++) 994 x |= (x >> 1); 995 996 return x + 1; 997 } 998 999 /* 1000 * Return the ceiling value the interval holding `ba' or NULL if it is 1001 * out of the (min, max) values. 1002 */ 1003 const char * 1004 ba2bucket(struct bt_arg *ba, struct bt_arg *brange, struct dt_evt *dtev, 1005 long *pstep) 1006 { 1007 static char buf[KLEN]; 1008 long val, bucket; 1009 int l; 1010 1011 val = ba2long(ba, dtev); 1012 if (brange == NULL) 1013 bucket = next_pow2(val); 1014 else { 1015 long min, max, step; 1016 1017 assert(brange->ba_type == B_AT_LONG); 1018 min = ba2long(brange, NULL); 1019 1020 brange = SLIST_NEXT(brange, ba_next); 1021 assert(brange->ba_type == B_AT_LONG); 1022 max = ba2long(brange, NULL); 1023 1024 if ((val < min) || (val > max)) 1025 return NULL; 1026 1027 brange = SLIST_NEXT(brange, ba_next); 1028 assert(brange->ba_type == B_AT_LONG); 1029 step = ba2long(brange, NULL); 1030 1031 bucket = ((val / step) + 1) * step; 1032 *pstep = step; 1033 } 1034 1035 buf[0] = '\0'; 1036 l = snprintf(buf, sizeof(buf), "%lu", bucket); 1037 if (l < 0 || (size_t)l > sizeof(buf)) { 1038 warn("string too long %d > %lu", l, sizeof(buf)); 1039 return buf; 1040 } 1041 1042 return buf; 1043 } 1044 1045 /* 1046 * Evaluate the operation encoded in `ba' and return its result. 1047 */ 1048 long 1049 baexpr2long(struct bt_arg *ba, struct dt_evt *dtev) 1050 { 1051 static long recursions; 1052 struct bt_arg *lhs, *rhs; 1053 long lval, rval, result; 1054 1055 if (++recursions >= __MAXOPERANDS) 1056 errx(1, "too many operands (>%d) in expression", __MAXOPERANDS); 1057 1058 lhs = ba->ba_value; 1059 rhs = SLIST_NEXT(lhs, ba_next); 1060 1061 lval = ba2long(lhs, dtev); 1062 if (rhs == NULL) { 1063 rval = 0; 1064 } else { 1065 assert(SLIST_NEXT(rhs, ba_next) == NULL); 1066 rval = ba2long(rhs, dtev); 1067 } 1068 1069 switch (ba->ba_type) { 1070 case B_AT_OP_PLUS: 1071 result = lval + rval; 1072 break; 1073 case B_AT_OP_MINUS: 1074 result = lval - rval; 1075 break; 1076 case B_AT_OP_MULT: 1077 result = lval * rval; 1078 break; 1079 case B_AT_OP_DIVIDE: 1080 result = lval / rval; 1081 break; 1082 case B_AT_OP_BAND: 1083 result = lval & rval; 1084 break; 1085 case B_AT_OP_XOR: 1086 result = lval ^ rval; 1087 break; 1088 case B_AT_OP_BOR: 1089 result = lval | rval; 1090 break; 1091 case B_AT_OP_EQ: 1092 result = (lval == rval); 1093 break; 1094 case B_AT_OP_NE: 1095 result = (lval != rval); 1096 break; 1097 case B_AT_OP_LE: 1098 result = (lval <= rval); 1099 break; 1100 case B_AT_OP_LT: 1101 result = (lval < rval); 1102 break; 1103 case B_AT_OP_GE: 1104 result = (lval >= rval); 1105 break; 1106 case B_AT_OP_GT: 1107 result = (lval > rval); 1108 break; 1109 case B_AT_OP_LAND: 1110 result = (lval && rval); 1111 break; 1112 case B_AT_OP_LOR: 1113 result = (lval || rval); 1114 break; 1115 default: 1116 xabort("unsuported operation %d", ba->ba_type); 1117 } 1118 1119 debug("ba=%p eval '%ld %s %ld = %d'\n", ba, lval, ba_name(ba), 1120 rval, result); 1121 1122 --recursions; 1123 1124 return result; 1125 } 1126 1127 const char * 1128 ba_name(struct bt_arg *ba) 1129 { 1130 switch (ba->ba_type) { 1131 case B_AT_VAR: 1132 case B_AT_MAP: 1133 break; 1134 case B_AT_BI_PID: 1135 return "pid"; 1136 case B_AT_BI_TID: 1137 return "tid"; 1138 case B_AT_BI_COMM: 1139 return "comm"; 1140 case B_AT_BI_CPU: 1141 return "cpu"; 1142 case B_AT_BI_NSECS: 1143 return "nsecs"; 1144 case B_AT_BI_KSTACK: 1145 return "kstack"; 1146 case B_AT_BI_USTACK: 1147 return "ustack"; 1148 case B_AT_BI_ARG0: 1149 return "arg0"; 1150 case B_AT_BI_ARG1: 1151 return "arg1"; 1152 case B_AT_BI_ARG2: 1153 return "arg2"; 1154 case B_AT_BI_ARG3: 1155 return "arg3"; 1156 case B_AT_BI_ARG4: 1157 return "arg4"; 1158 case B_AT_BI_ARG5: 1159 return "arg5"; 1160 case B_AT_BI_ARG6: 1161 return "arg6"; 1162 case B_AT_BI_ARG7: 1163 return "arg7"; 1164 case B_AT_BI_ARG8: 1165 return "arg8"; 1166 case B_AT_BI_ARG9: 1167 return "arg9"; 1168 case B_AT_BI_ARGS: 1169 return "args"; 1170 case B_AT_BI_RETVAL: 1171 return "retval"; 1172 case B_AT_OP_PLUS: 1173 return "+"; 1174 case B_AT_OP_MINUS: 1175 return "-"; 1176 case B_AT_OP_MULT: 1177 return "*"; 1178 case B_AT_OP_DIVIDE: 1179 return "/"; 1180 case B_AT_OP_BAND: 1181 return "&"; 1182 case B_AT_OP_XOR: 1183 return "^"; 1184 case B_AT_OP_BOR: 1185 return "|"; 1186 case B_AT_OP_EQ: 1187 return "=="; 1188 case B_AT_OP_NE: 1189 return "!="; 1190 case B_AT_OP_LE: 1191 return "<="; 1192 case B_AT_OP_LT: 1193 return "<"; 1194 case B_AT_OP_GE: 1195 return ">="; 1196 case B_AT_OP_GT: 1197 return ">"; 1198 case B_AT_OP_LAND: 1199 return "&&"; 1200 case B_AT_OP_LOR: 1201 return "||"; 1202 default: 1203 xabort("unsuported type %d", ba->ba_type); 1204 } 1205 1206 assert(ba->ba_type == B_AT_VAR || ba->ba_type == B_AT_MAP); 1207 1208 static char buf[64]; 1209 size_t sz; 1210 int l; 1211 1212 buf[0] = '@'; 1213 buf[1] = '\0'; 1214 sz = sizeof(buf) - 1; 1215 l = snprintf(buf+1, sz, "%s", bv_name(ba->ba_value)); 1216 if (l < 0 || (size_t)l > sz) { 1217 warn("string too long %d > %zu", l, sz); 1218 return buf; 1219 } 1220 1221 if (ba->ba_type == B_AT_MAP) { 1222 sz -= l; 1223 l = snprintf(buf+1+l, sz, "[%s]", ba_name(ba->ba_key)); 1224 if (l < 0 || (size_t)l > sz) { 1225 warn("string too long %d > %zu", l, sz); 1226 return buf; 1227 } 1228 } 1229 1230 return buf; 1231 } 1232 1233 /* 1234 * Return the representation of `ba' as long. 1235 */ 1236 long 1237 ba2long(struct bt_arg *ba, struct dt_evt *dtev) 1238 { 1239 struct bt_var *bv; 1240 long val; 1241 1242 switch (ba->ba_type) { 1243 case B_AT_LONG: 1244 val = (long)ba->ba_value; 1245 break; 1246 case B_AT_VAR: 1247 ba = ba_read(ba); 1248 val = (long)ba->ba_value; 1249 break; 1250 case B_AT_MAP: 1251 bv = ba->ba_value; 1252 /* Unitialized map */ 1253 if (bv->bv_value == NULL) 1254 return 0; 1255 val = ba2long(map_get((struct map *)bv->bv_value, 1256 ba2str(ba->ba_key, dtev)), dtev); 1257 break; 1258 case B_AT_BI_PID: 1259 val = dtev->dtev_pid; 1260 break; 1261 case B_AT_BI_TID: 1262 val = dtev->dtev_tid; 1263 break; 1264 case B_AT_BI_CPU: 1265 val = dtev->dtev_cpu; 1266 break; 1267 case B_AT_BI_NSECS: 1268 val = builtin_nsecs(dtev); 1269 break; 1270 case B_AT_BI_ARG0 ... B_AT_BI_ARG9: 1271 val = dtev->dtev_args[ba->ba_type - B_AT_BI_ARG0]; 1272 break; 1273 case B_AT_BI_RETVAL: 1274 val = dtev->dtev_retval[0]; 1275 break; 1276 case B_AT_OP_PLUS ... B_AT_OP_LOR: 1277 val = baexpr2long(ba, dtev); 1278 break; 1279 default: 1280 xabort("no long conversion for type %d", ba->ba_type); 1281 } 1282 1283 return val; 1284 } 1285 1286 /* 1287 * Return the representation of `ba' as string. 1288 */ 1289 const char * 1290 ba2str(struct bt_arg *ba, struct dt_evt *dtev) 1291 { 1292 static char buf[sizeof("18446744073709551615")]; /* UINT64_MAX */ 1293 struct bt_var *bv; 1294 const char *str; 1295 1296 buf[0] = '\0'; 1297 switch (ba->ba_type) { 1298 case B_AT_STR: 1299 str = (const char *)ba->ba_value; 1300 break; 1301 case B_AT_LONG: 1302 snprintf(buf, sizeof(buf), "%ld",(long)ba->ba_value); 1303 str = buf; 1304 break; 1305 case B_AT_BI_KSTACK: 1306 str = builtin_stack(dtev, 1); 1307 break; 1308 case B_AT_BI_USTACK: 1309 str = builtin_stack(dtev, 0); 1310 break; 1311 case B_AT_BI_COMM: 1312 str = dtev->dtev_comm; 1313 break; 1314 case B_AT_BI_CPU: 1315 snprintf(buf, sizeof(buf), "%u", dtev->dtev_cpu); 1316 str = buf; 1317 break; 1318 case B_AT_BI_PID: 1319 snprintf(buf, sizeof(buf), "%d", dtev->dtev_pid); 1320 str = buf; 1321 break; 1322 case B_AT_BI_TID: 1323 snprintf(buf, sizeof(buf), "%d", dtev->dtev_tid); 1324 str = buf; 1325 break; 1326 case B_AT_BI_NSECS: 1327 snprintf(buf, sizeof(buf), "%llu", builtin_nsecs(dtev)); 1328 str = buf; 1329 break; 1330 case B_AT_BI_ARG0 ... B_AT_BI_ARG9: 1331 str = builtin_arg(dtev, ba->ba_type); 1332 break; 1333 case B_AT_BI_RETVAL: 1334 snprintf(buf, sizeof(buf), "%ld", (long)dtev->dtev_retval[0]); 1335 str = buf; 1336 break; 1337 case B_AT_MAP: 1338 bv = ba->ba_value; 1339 str = ba2str(map_get((struct map *)bv->bv_value, 1340 ba2str(ba->ba_key, dtev)), dtev); 1341 break; 1342 case B_AT_VAR: 1343 str = ba2str(ba_read(ba), dtev); 1344 break; 1345 case B_AT_OP_PLUS ... B_AT_OP_LOR: 1346 snprintf(buf, sizeof(buf), "%ld", ba2long(ba, dtev)); 1347 str = buf; 1348 break; 1349 case B_AT_MF_COUNT: 1350 case B_AT_MF_MAX: 1351 case B_AT_MF_MIN: 1352 case B_AT_MF_SUM: 1353 assert(0); 1354 break; 1355 default: 1356 xabort("no string conversion for type %d", ba->ba_type); 1357 } 1358 1359 return str; 1360 } 1361 1362 /* 1363 * Return dt(4) flags indicating which data should be recorded by the 1364 * kernel, if any, for a given `ba'. 1365 */ 1366 int 1367 ba2dtflags(struct bt_arg *ba) 1368 { 1369 struct bt_arg *bval; 1370 int flags = 0; 1371 1372 do { 1373 if (ba->ba_type == B_AT_MAP) 1374 bval = ba->ba_key; 1375 else 1376 bval = ba; 1377 1378 switch (bval->ba_type) { 1379 case B_AT_STR: 1380 case B_AT_LONG: 1381 case B_AT_VAR: 1382 case B_AT_HIST: 1383 break; 1384 case B_AT_BI_KSTACK: 1385 flags |= DTEVT_KSTACK; 1386 break; 1387 case B_AT_BI_USTACK: 1388 flags |= DTEVT_USTACK; 1389 break; 1390 case B_AT_BI_COMM: 1391 flags |= DTEVT_EXECNAME; 1392 break; 1393 case B_AT_BI_CPU: 1394 case B_AT_BI_PID: 1395 case B_AT_BI_TID: 1396 case B_AT_BI_NSECS: 1397 break; 1398 case B_AT_BI_ARG0 ... B_AT_BI_ARG9: 1399 flags |= DTEVT_FUNCARGS; 1400 break; 1401 case B_AT_BI_RETVAL: 1402 break; 1403 case B_AT_MF_COUNT: 1404 case B_AT_MF_MAX: 1405 case B_AT_MF_MIN: 1406 case B_AT_MF_SUM: 1407 case B_AT_OP_PLUS ... B_AT_OP_LOR: 1408 break; 1409 default: 1410 xabort("invalid argument type %d", bval->ba_type); 1411 } 1412 } while ((ba = SLIST_NEXT(ba, ba_next)) != NULL); 1413 1414 return flags; 1415 } 1416 1417 long 1418 bacmp(struct bt_arg *a, struct bt_arg *b) 1419 { 1420 assert(a->ba_type == b->ba_type); 1421 assert(a->ba_type == B_AT_LONG); 1422 1423 return ba2long(a, NULL) - ba2long(b, NULL); 1424 } 1425 1426 __dead void 1427 xabort(const char *fmt, ...) 1428 { 1429 va_list ap; 1430 1431 va_start(ap, fmt); 1432 vfprintf(stderr, fmt, ap); 1433 va_end(ap); 1434 1435 fprintf(stderr, "\n"); 1436 abort(); 1437 } 1438 1439 void 1440 debug(const char *fmt, ...) 1441 { 1442 va_list ap; 1443 1444 if (verbose < 2) 1445 return; 1446 1447 fprintf(stderr, "debug: "); 1448 1449 va_start(ap, fmt); 1450 vfprintf(stderr, fmt, ap); 1451 va_end(ap); 1452 } 1453 1454 void 1455 debugx(const char *fmt, ...) 1456 { 1457 va_list ap; 1458 1459 if (verbose < 2) 1460 return; 1461 1462 va_start(ap, fmt); 1463 vfprintf(stderr, fmt, ap); 1464 va_end(ap); 1465 } 1466 1467 void 1468 debug_dump_term(struct bt_arg *ba) 1469 { 1470 switch (ba->ba_type) { 1471 case B_AT_LONG: 1472 debugx("%s", ba2str(ba, NULL)); 1473 break; 1474 case B_AT_OP_PLUS ... B_AT_OP_LOR: 1475 debug_dump_expr(ba); 1476 break; 1477 default: 1478 debugx("%s", ba_name(ba)); 1479 } 1480 } 1481 1482 void 1483 debug_dump_expr(struct bt_arg *ba) 1484 { 1485 struct bt_arg *lhs, *rhs; 1486 1487 lhs = ba->ba_value; 1488 rhs = SLIST_NEXT(lhs, ba_next); 1489 1490 /* Left */ 1491 debug_dump_term(lhs); 1492 1493 /* Right */ 1494 if (rhs != NULL) { 1495 debugx(" %s ", ba_name(ba)); 1496 debug_dump_term(rhs); 1497 } else { 1498 if (ba->ba_type != B_AT_OP_NE) 1499 debugx(" %s NULL", ba_name(ba)); 1500 } 1501 } 1502 1503 void 1504 debug_dump_filter(struct bt_rule *r) 1505 { 1506 struct bt_stmt *bs; 1507 1508 if (r->br_filter == NULL) { 1509 debugx("\n"); 1510 return; 1511 } 1512 1513 bs = r->br_filter->bf_condition; 1514 1515 debugx(" /"); 1516 debug_dump_expr(SLIST_FIRST(&bs->bs_args)); 1517 debugx("/\n"); 1518 } 1519 1520 const char * 1521 debug_rule_name(struct bt_rule *r) 1522 { 1523 struct bt_probe *bp = r->br_probe; 1524 static char buf[64]; 1525 1526 if (r->br_type == B_RT_BEGIN) 1527 return "BEGIN"; 1528 1529 if (r->br_type == B_RT_END) 1530 return "END"; 1531 1532 assert(r->br_type == B_RT_PROBE); 1533 1534 if (r->br_probe->bp_rate) { 1535 snprintf(buf, sizeof(buf), "%s:%s:%u", bp->bp_prov, 1536 bp->bp_unit, bp->bp_rate); 1537 } else { 1538 snprintf(buf, sizeof(buf), "%s:%s:%s", bp->bp_prov, 1539 bp->bp_unit, bp->bp_name); 1540 } 1541 1542 return buf; 1543 } 1544