1 #include "cache.h" 2 #include "config.h" 3 #include "json-writer.h" 4 #include "quote.h" 5 #include "run-command.h" 6 #include "sigchain.h" 7 #include "thread-utils.h" 8 #include "version.h" 9 #include "trace2/tr2_cfg.h" 10 #include "trace2/tr2_cmd_name.h" 11 #include "trace2/tr2_dst.h" 12 #include "trace2/tr2_sid.h" 13 #include "trace2/tr2_sysenv.h" 14 #include "trace2/tr2_tgt.h" 15 #include "trace2/tr2_tls.h" 16 17 static int trace2_enabled; 18 19 static int tr2_next_child_id; /* modify under lock */ 20 static int tr2_next_exec_id; /* modify under lock */ 21 static int tr2_next_repo_id = 1; /* modify under lock. zero is reserved */ 22 23 /* 24 * A table of the builtin TRACE2 targets. Each of these may be independently 25 * enabled or disabled. Each TRACE2 API method will try to write an event to 26 * *each* of the enabled targets. 27 */ 28 /* clang-format off */ 29 static struct tr2_tgt *tr2_tgt_builtins[] = 30 { 31 &tr2_tgt_normal, 32 &tr2_tgt_perf, 33 &tr2_tgt_event, 34 NULL 35 }; 36 /* clang-format on */ 37 38 /* clang-format off */ 39 #define for_each_builtin(j, tgt_j) \ 40 for (j = 0, tgt_j = tr2_tgt_builtins[j]; \ 41 tgt_j; \ 42 j++, tgt_j = tr2_tgt_builtins[j]) 43 /* clang-format on */ 44 45 /* clang-format off */ 46 #define for_each_wanted_builtin(j, tgt_j) \ 47 for_each_builtin(j, tgt_j) \ 48 if (tr2_dst_trace_want(tgt_j->pdst)) 49 /* clang-format on */ 50 51 /* 52 * Force (rather than lazily) initialize any of the requested 53 * builtin TRACE2 targets at startup (and before we've seen an 54 * actual TRACE2 event call) so we can see if we need to setup 55 * the TR2 and TLS machinery. 56 * 57 * Return the number of builtin targets enabled. 58 */ 59 static int tr2_tgt_want_builtins(void) 60 { 61 struct tr2_tgt *tgt_j; 62 int j; 63 int sum = 0; 64 65 for_each_builtin (j, tgt_j) 66 if (tgt_j->pfn_init()) 67 sum++; 68 69 return sum; 70 } 71 72 /* 73 * Properly terminate each builtin target. Give each target 74 * a chance to write a summary event and/or flush if necessary 75 * and then close the fd. 76 */ 77 static void tr2_tgt_disable_builtins(void) 78 { 79 struct tr2_tgt *tgt_j; 80 int j; 81 82 for_each_builtin (j, tgt_j) 83 tgt_j->pfn_term(); 84 } 85 86 static int tr2main_exit_code; 87 88 /* 89 * Our atexit routine should run after everything has finished. 90 * 91 * Note that events generated here might not actually appear if 92 * we are writing to fd 1 or 2 and our atexit routine runs after 93 * the pager's atexit routine (since it closes them to shutdown 94 * the pipes). 95 */ 96 static void tr2main_atexit_handler(void) 97 { 98 struct tr2_tgt *tgt_j; 99 int j; 100 uint64_t us_now; 101 uint64_t us_elapsed_absolute; 102 103 us_now = getnanotime() / 1000; 104 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 105 106 /* 107 * Clear any unbalanced regions so that our atexit message 108 * does not appear nested. This improves the appearance of 109 * the trace output if someone calls die(), for example. 110 */ 111 tr2tls_pop_unwind_self(); 112 113 for_each_wanted_builtin (j, tgt_j) 114 if (tgt_j->pfn_atexit) 115 tgt_j->pfn_atexit(us_elapsed_absolute, 116 tr2main_exit_code); 117 118 tr2_tgt_disable_builtins(); 119 120 tr2tls_release(); 121 tr2_sid_release(); 122 tr2_cmd_name_release(); 123 tr2_cfg_free_patterns(); 124 tr2_cfg_free_env_vars(); 125 tr2_sysenv_release(); 126 127 trace2_enabled = 0; 128 } 129 130 static void tr2main_signal_handler(int signo) 131 { 132 struct tr2_tgt *tgt_j; 133 int j; 134 uint64_t us_now; 135 uint64_t us_elapsed_absolute; 136 137 us_now = getnanotime() / 1000; 138 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 139 140 for_each_wanted_builtin (j, tgt_j) 141 if (tgt_j->pfn_signal) 142 tgt_j->pfn_signal(us_elapsed_absolute, signo); 143 144 sigchain_pop(signo); 145 raise(signo); 146 } 147 148 void trace2_initialize_clock(void) 149 { 150 tr2tls_start_process_clock(); 151 } 152 153 void trace2_initialize_fl(const char *file, int line) 154 { 155 struct tr2_tgt *tgt_j; 156 int j; 157 158 if (trace2_enabled) 159 return; 160 161 tr2_sysenv_load(); 162 163 if (!tr2_tgt_want_builtins()) 164 return; 165 trace2_enabled = 1; 166 167 tr2_sid_get(); 168 169 atexit(tr2main_atexit_handler); 170 sigchain_push(SIGPIPE, tr2main_signal_handler); 171 tr2tls_init(); 172 173 /* 174 * Emit 'version' message on each active builtin target. 175 */ 176 for_each_wanted_builtin (j, tgt_j) 177 if (tgt_j->pfn_version_fl) 178 tgt_j->pfn_version_fl(file, line); 179 } 180 181 int trace2_is_enabled(void) 182 { 183 return trace2_enabled; 184 } 185 186 void trace2_cmd_start_fl(const char *file, int line, const char **argv) 187 { 188 struct tr2_tgt *tgt_j; 189 int j; 190 uint64_t us_now; 191 uint64_t us_elapsed_absolute; 192 193 if (!trace2_enabled) 194 return; 195 196 us_now = getnanotime() / 1000; 197 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 198 199 for_each_wanted_builtin (j, tgt_j) 200 if (tgt_j->pfn_start_fl) 201 tgt_j->pfn_start_fl(file, line, us_elapsed_absolute, 202 argv); 203 } 204 205 int trace2_cmd_exit_fl(const char *file, int line, int code) 206 { 207 struct tr2_tgt *tgt_j; 208 int j; 209 uint64_t us_now; 210 uint64_t us_elapsed_absolute; 211 212 code &= 0xff; 213 214 if (!trace2_enabled) 215 return code; 216 217 trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT); 218 219 tr2main_exit_code = code; 220 221 us_now = getnanotime() / 1000; 222 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 223 224 for_each_wanted_builtin (j, tgt_j) 225 if (tgt_j->pfn_exit_fl) 226 tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute, 227 code); 228 229 return code; 230 } 231 232 void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt, 233 va_list ap) 234 { 235 struct tr2_tgt *tgt_j; 236 int j; 237 238 if (!trace2_enabled) 239 return; 240 241 /* 242 * We expect each target function to treat 'ap' as constant 243 * and use va_copy (because an 'ap' can only be walked once). 244 */ 245 for_each_wanted_builtin (j, tgt_j) 246 if (tgt_j->pfn_error_va_fl) 247 tgt_j->pfn_error_va_fl(file, line, fmt, ap); 248 } 249 250 void trace2_cmd_path_fl(const char *file, int line, const char *pathname) 251 { 252 struct tr2_tgt *tgt_j; 253 int j; 254 255 if (!trace2_enabled) 256 return; 257 258 for_each_wanted_builtin (j, tgt_j) 259 if (tgt_j->pfn_command_path_fl) 260 tgt_j->pfn_command_path_fl(file, line, pathname); 261 } 262 263 void trace2_cmd_ancestry_fl(const char *file, int line, const char **parent_names) 264 { 265 struct tr2_tgt *tgt_j; 266 int j; 267 268 if (!trace2_enabled) 269 return; 270 271 for_each_wanted_builtin (j, tgt_j) 272 if (tgt_j->pfn_command_ancestry_fl) 273 tgt_j->pfn_command_ancestry_fl(file, line, parent_names); 274 } 275 276 void trace2_cmd_name_fl(const char *file, int line, const char *name) 277 { 278 struct tr2_tgt *tgt_j; 279 const char *hierarchy; 280 int j; 281 282 if (!trace2_enabled) 283 return; 284 285 tr2_cmd_name_append_hierarchy(name); 286 hierarchy = tr2_cmd_name_get_hierarchy(); 287 288 for_each_wanted_builtin (j, tgt_j) 289 if (tgt_j->pfn_command_name_fl) 290 tgt_j->pfn_command_name_fl(file, line, name, hierarchy); 291 } 292 293 void trace2_cmd_mode_fl(const char *file, int line, const char *mode) 294 { 295 struct tr2_tgt *tgt_j; 296 int j; 297 298 if (!trace2_enabled) 299 return; 300 301 for_each_wanted_builtin (j, tgt_j) 302 if (tgt_j->pfn_command_mode_fl) 303 tgt_j->pfn_command_mode_fl(file, line, mode); 304 } 305 306 void trace2_cmd_alias_fl(const char *file, int line, const char *alias, 307 const char **argv) 308 { 309 struct tr2_tgt *tgt_j; 310 int j; 311 312 if (!trace2_enabled) 313 return; 314 315 for_each_wanted_builtin (j, tgt_j) 316 if (tgt_j->pfn_alias_fl) 317 tgt_j->pfn_alias_fl(file, line, alias, argv); 318 } 319 320 void trace2_cmd_list_config_fl(const char *file, int line) 321 { 322 if (!trace2_enabled) 323 return; 324 325 tr2_cfg_list_config_fl(file, line); 326 } 327 328 void trace2_cmd_list_env_vars_fl(const char *file, int line) 329 { 330 if (!trace2_enabled) 331 return; 332 333 tr2_list_env_vars_fl(file, line); 334 } 335 336 void trace2_cmd_set_config_fl(const char *file, int line, const char *key, 337 const char *value) 338 { 339 if (!trace2_enabled) 340 return; 341 342 tr2_cfg_set_fl(file, line, key, value); 343 } 344 345 void trace2_child_start_fl(const char *file, int line, 346 struct child_process *cmd) 347 { 348 struct tr2_tgt *tgt_j; 349 int j; 350 uint64_t us_now; 351 uint64_t us_elapsed_absolute; 352 353 if (!trace2_enabled) 354 return; 355 356 us_now = getnanotime() / 1000; 357 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 358 359 cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id); 360 cmd->trace2_child_us_start = us_now; 361 362 for_each_wanted_builtin (j, tgt_j) 363 if (tgt_j->pfn_child_start_fl) 364 tgt_j->pfn_child_start_fl(file, line, 365 us_elapsed_absolute, cmd); 366 } 367 368 void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd, 369 int child_exit_code) 370 { 371 struct tr2_tgt *tgt_j; 372 int j; 373 uint64_t us_now; 374 uint64_t us_elapsed_absolute; 375 uint64_t us_elapsed_child; 376 377 if (!trace2_enabled) 378 return; 379 380 us_now = getnanotime() / 1000; 381 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 382 383 if (cmd->trace2_child_us_start) 384 us_elapsed_child = us_now - cmd->trace2_child_us_start; 385 else 386 us_elapsed_child = 0; 387 388 for_each_wanted_builtin (j, tgt_j) 389 if (tgt_j->pfn_child_exit_fl) 390 tgt_j->pfn_child_exit_fl(file, line, 391 us_elapsed_absolute, 392 cmd->trace2_child_id, cmd->pid, 393 child_exit_code, 394 us_elapsed_child); 395 } 396 397 void trace2_child_ready_fl(const char *file, int line, 398 struct child_process *cmd, 399 const char *ready) 400 { 401 struct tr2_tgt *tgt_j; 402 int j; 403 uint64_t us_now; 404 uint64_t us_elapsed_absolute; 405 uint64_t us_elapsed_child; 406 407 if (!trace2_enabled) 408 return; 409 410 us_now = getnanotime() / 1000; 411 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 412 413 if (cmd->trace2_child_us_start) 414 us_elapsed_child = us_now - cmd->trace2_child_us_start; 415 else 416 us_elapsed_child = 0; 417 418 for_each_wanted_builtin (j, tgt_j) 419 if (tgt_j->pfn_child_ready_fl) 420 tgt_j->pfn_child_ready_fl(file, line, 421 us_elapsed_absolute, 422 cmd->trace2_child_id, 423 cmd->pid, 424 ready, 425 us_elapsed_child); 426 } 427 428 int trace2_exec_fl(const char *file, int line, const char *exe, 429 const char **argv) 430 { 431 struct tr2_tgt *tgt_j; 432 int j; 433 int exec_id; 434 uint64_t us_now; 435 uint64_t us_elapsed_absolute; 436 437 if (!trace2_enabled) 438 return -1; 439 440 us_now = getnanotime() / 1000; 441 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 442 443 exec_id = tr2tls_locked_increment(&tr2_next_exec_id); 444 445 for_each_wanted_builtin (j, tgt_j) 446 if (tgt_j->pfn_exec_fl) 447 tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute, 448 exec_id, exe, argv); 449 450 return exec_id; 451 } 452 453 void trace2_exec_result_fl(const char *file, int line, int exec_id, int code) 454 { 455 struct tr2_tgt *tgt_j; 456 int j; 457 uint64_t us_now; 458 uint64_t us_elapsed_absolute; 459 460 if (!trace2_enabled) 461 return; 462 463 us_now = getnanotime() / 1000; 464 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 465 466 for_each_wanted_builtin (j, tgt_j) 467 if (tgt_j->pfn_exec_result_fl) 468 tgt_j->pfn_exec_result_fl( 469 file, line, us_elapsed_absolute, exec_id, code); 470 } 471 472 void trace2_thread_start_fl(const char *file, int line, const char *thread_name) 473 { 474 struct tr2_tgt *tgt_j; 475 int j; 476 uint64_t us_now; 477 uint64_t us_elapsed_absolute; 478 479 if (!trace2_enabled) 480 return; 481 482 if (tr2tls_is_main_thread()) { 483 /* 484 * We should only be called from the new thread's thread-proc, 485 * so this is technically a bug. But in those cases where the 486 * main thread also runs the thread-proc function (or when we 487 * are built with threading disabled), we need to allow it. 488 * 489 * Convert this call to a region-enter so the nesting looks 490 * correct. 491 */ 492 trace2_region_enter_printf_fl(file, line, NULL, NULL, NULL, 493 "thread-proc on main: %s", 494 thread_name); 495 return; 496 } 497 498 us_now = getnanotime() / 1000; 499 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 500 501 tr2tls_create_self(thread_name, us_now); 502 503 for_each_wanted_builtin (j, tgt_j) 504 if (tgt_j->pfn_thread_start_fl) 505 tgt_j->pfn_thread_start_fl(file, line, 506 us_elapsed_absolute); 507 } 508 509 void trace2_thread_exit_fl(const char *file, int line) 510 { 511 struct tr2_tgt *tgt_j; 512 int j; 513 uint64_t us_now; 514 uint64_t us_elapsed_absolute; 515 uint64_t us_elapsed_thread; 516 517 if (!trace2_enabled) 518 return; 519 520 if (tr2tls_is_main_thread()) { 521 /* 522 * We should only be called from the exiting thread's 523 * thread-proc, so this is technically a bug. But in 524 * those cases where the main thread also runs the 525 * thread-proc function (or when we are built with 526 * threading disabled), we need to allow it. 527 * 528 * Convert this call to a region-leave so the nesting 529 * looks correct. 530 */ 531 trace2_region_leave_printf_fl(file, line, NULL, NULL, NULL, 532 "thread-proc on main"); 533 return; 534 } 535 536 us_now = getnanotime() / 1000; 537 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 538 539 /* 540 * Clear any unbalanced regions and then get the relative time 541 * for the outer-most region (which we pushed when the thread 542 * started). This gives us the run time of the thread. 543 */ 544 tr2tls_pop_unwind_self(); 545 us_elapsed_thread = tr2tls_region_elasped_self(us_now); 546 547 for_each_wanted_builtin (j, tgt_j) 548 if (tgt_j->pfn_thread_exit_fl) 549 tgt_j->pfn_thread_exit_fl(file, line, 550 us_elapsed_absolute, 551 us_elapsed_thread); 552 553 tr2tls_unset_self(); 554 } 555 556 void trace2_def_param_fl(const char *file, int line, const char *param, 557 const char *value) 558 { 559 struct tr2_tgt *tgt_j; 560 int j; 561 562 if (!trace2_enabled) 563 return; 564 565 for_each_wanted_builtin (j, tgt_j) 566 if (tgt_j->pfn_param_fl) 567 tgt_j->pfn_param_fl(file, line, param, value); 568 } 569 570 void trace2_def_repo_fl(const char *file, int line, struct repository *repo) 571 { 572 struct tr2_tgt *tgt_j; 573 int j; 574 575 if (!trace2_enabled) 576 return; 577 578 if (repo->trace2_repo_id) 579 return; 580 581 repo->trace2_repo_id = tr2tls_locked_increment(&tr2_next_repo_id); 582 583 for_each_wanted_builtin (j, tgt_j) 584 if (tgt_j->pfn_repo_fl) 585 tgt_j->pfn_repo_fl(file, line, repo); 586 } 587 588 void trace2_region_enter_printf_va_fl(const char *file, int line, 589 const char *category, const char *label, 590 const struct repository *repo, 591 const char *fmt, va_list ap) 592 { 593 struct tr2_tgt *tgt_j; 594 int j; 595 uint64_t us_now; 596 uint64_t us_elapsed_absolute; 597 598 if (!trace2_enabled) 599 return; 600 601 us_now = getnanotime() / 1000; 602 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 603 604 /* 605 * Print the region-enter message at the current nesting 606 * (indentation) level and then push a new level. 607 * 608 * We expect each target function to treat 'ap' as constant 609 * and use va_copy. 610 */ 611 for_each_wanted_builtin (j, tgt_j) 612 if (tgt_j->pfn_region_enter_printf_va_fl) 613 tgt_j->pfn_region_enter_printf_va_fl( 614 file, line, us_elapsed_absolute, category, 615 label, repo, fmt, ap); 616 617 tr2tls_push_self(us_now); 618 } 619 620 void trace2_region_enter_fl(const char *file, int line, const char *category, 621 const char *label, const struct repository *repo, ...) 622 { 623 va_list ap; 624 va_start(ap, repo); 625 trace2_region_enter_printf_va_fl(file, line, category, label, repo, 626 NULL, ap); 627 va_end(ap); 628 629 } 630 631 void trace2_region_enter_printf_fl(const char *file, int line, 632 const char *category, const char *label, 633 const struct repository *repo, 634 const char *fmt, ...) 635 { 636 va_list ap; 637 638 va_start(ap, fmt); 639 trace2_region_enter_printf_va_fl(file, line, category, label, repo, fmt, 640 ap); 641 va_end(ap); 642 } 643 644 #ifndef HAVE_VARIADIC_MACROS 645 void trace2_region_enter_printf(const char *category, const char *label, 646 const struct repository *repo, const char *fmt, 647 ...) 648 { 649 va_list ap; 650 651 va_start(ap, fmt); 652 trace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt, 653 ap); 654 va_end(ap); 655 } 656 #endif 657 658 void trace2_region_leave_printf_va_fl(const char *file, int line, 659 const char *category, const char *label, 660 const struct repository *repo, 661 const char *fmt, va_list ap) 662 { 663 struct tr2_tgt *tgt_j; 664 int j; 665 uint64_t us_now; 666 uint64_t us_elapsed_absolute; 667 uint64_t us_elapsed_region; 668 669 if (!trace2_enabled) 670 return; 671 672 us_now = getnanotime() / 1000; 673 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 674 675 /* 676 * Get the elapsed time in the current region before we 677 * pop it off the stack. Pop the stack. And then print 678 * the perf message at the new (shallower) level so that 679 * it lines up with the corresponding push/enter. 680 */ 681 us_elapsed_region = tr2tls_region_elasped_self(us_now); 682 683 tr2tls_pop_self(); 684 685 /* 686 * We expect each target function to treat 'ap' as constant 687 * and use va_copy. 688 */ 689 for_each_wanted_builtin (j, tgt_j) 690 if (tgt_j->pfn_region_leave_printf_va_fl) 691 tgt_j->pfn_region_leave_printf_va_fl( 692 file, line, us_elapsed_absolute, 693 us_elapsed_region, category, label, repo, fmt, 694 ap); 695 } 696 697 void trace2_region_leave_fl(const char *file, int line, const char *category, 698 const char *label, const struct repository *repo, ...) 699 { 700 va_list ap; 701 va_start(ap, repo); 702 trace2_region_leave_printf_va_fl(file, line, category, label, repo, 703 NULL, ap); 704 va_end(ap); 705 } 706 707 void trace2_region_leave_printf_fl(const char *file, int line, 708 const char *category, const char *label, 709 const struct repository *repo, 710 const char *fmt, ...) 711 { 712 va_list ap; 713 714 va_start(ap, fmt); 715 trace2_region_leave_printf_va_fl(file, line, category, label, repo, fmt, 716 ap); 717 va_end(ap); 718 } 719 720 #ifndef HAVE_VARIADIC_MACROS 721 void trace2_region_leave_printf(const char *category, const char *label, 722 const struct repository *repo, const char *fmt, 723 ...) 724 { 725 va_list ap; 726 727 va_start(ap, fmt); 728 trace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt, 729 ap); 730 va_end(ap); 731 } 732 #endif 733 734 void trace2_data_string_fl(const char *file, int line, const char *category, 735 const struct repository *repo, const char *key, 736 const char *value) 737 { 738 struct tr2_tgt *tgt_j; 739 int j; 740 uint64_t us_now; 741 uint64_t us_elapsed_absolute; 742 uint64_t us_elapsed_region; 743 744 if (!trace2_enabled) 745 return; 746 747 us_now = getnanotime() / 1000; 748 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 749 us_elapsed_region = tr2tls_region_elasped_self(us_now); 750 751 for_each_wanted_builtin (j, tgt_j) 752 if (tgt_j->pfn_data_fl) 753 tgt_j->pfn_data_fl(file, line, us_elapsed_absolute, 754 us_elapsed_region, category, repo, 755 key, value); 756 } 757 758 void trace2_data_intmax_fl(const char *file, int line, const char *category, 759 const struct repository *repo, const char *key, 760 intmax_t value) 761 { 762 struct strbuf buf_string = STRBUF_INIT; 763 764 if (!trace2_enabled) 765 return; 766 767 strbuf_addf(&buf_string, "%" PRIdMAX, value); 768 trace2_data_string_fl(file, line, category, repo, key, buf_string.buf); 769 strbuf_release(&buf_string); 770 } 771 772 void trace2_data_json_fl(const char *file, int line, const char *category, 773 const struct repository *repo, const char *key, 774 const struct json_writer *value) 775 { 776 struct tr2_tgt *tgt_j; 777 int j; 778 uint64_t us_now; 779 uint64_t us_elapsed_absolute; 780 uint64_t us_elapsed_region; 781 782 if (!trace2_enabled) 783 return; 784 785 us_now = getnanotime() / 1000; 786 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 787 us_elapsed_region = tr2tls_region_elasped_self(us_now); 788 789 for_each_wanted_builtin (j, tgt_j) 790 if (tgt_j->pfn_data_json_fl) 791 tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute, 792 us_elapsed_region, category, 793 repo, key, value); 794 } 795 796 void trace2_printf_va_fl(const char *file, int line, const char *fmt, 797 va_list ap) 798 { 799 struct tr2_tgt *tgt_j; 800 int j; 801 uint64_t us_now; 802 uint64_t us_elapsed_absolute; 803 804 if (!trace2_enabled) 805 return; 806 807 us_now = getnanotime() / 1000; 808 us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 809 810 /* 811 * We expect each target function to treat 'ap' as constant 812 * and use va_copy. 813 */ 814 for_each_wanted_builtin (j, tgt_j) 815 if (tgt_j->pfn_printf_va_fl) 816 tgt_j->pfn_printf_va_fl(file, line, us_elapsed_absolute, 817 fmt, ap); 818 } 819 820 void trace2_printf_fl(const char *file, int line, const char *fmt, ...) 821 { 822 va_list ap; 823 824 va_start(ap, fmt); 825 trace2_printf_va_fl(file, line, fmt, ap); 826 va_end(ap); 827 } 828 829 #ifndef HAVE_VARIADIC_MACROS 830 void trace2_printf(const char *fmt, ...) 831 { 832 va_list ap; 833 834 va_start(ap, fmt); 835 trace2_printf_va_fl(NULL, 0, fmt, ap); 836 va_end(ap); 837 } 838 #endif 839 840 const char *trace2_session_id(void) 841 { 842 return tr2_sid_get(); 843 } 844