trace2.con commit trace2: report peak memory usage of the process (26c6f25)
   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
  17static int trace2_enabled;
  18
  19static int tr2_next_child_id; /* modify under lock */
  20static int tr2_next_exec_id; /* modify under lock */
  21static 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 */
  29static 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 */
  59static 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 */
  77static 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
  86static 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 */
  96static 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_sysenv_release();
 125
 126        trace2_enabled = 0;
 127}
 128
 129static void tr2main_signal_handler(int signo)
 130{
 131        struct tr2_tgt *tgt_j;
 132        int j;
 133        uint64_t us_now;
 134        uint64_t us_elapsed_absolute;
 135
 136        us_now = getnanotime() / 1000;
 137        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 138
 139        for_each_wanted_builtin (j, tgt_j)
 140                if (tgt_j->pfn_signal)
 141                        tgt_j->pfn_signal(us_elapsed_absolute, signo);
 142
 143        sigchain_pop(signo);
 144        raise(signo);
 145}
 146
 147void trace2_initialize_clock(void)
 148{
 149        tr2tls_start_process_clock();
 150}
 151
 152void trace2_initialize_fl(const char *file, int line)
 153{
 154        struct tr2_tgt *tgt_j;
 155        int j;
 156
 157        if (trace2_enabled)
 158                return;
 159
 160        tr2_sysenv_load();
 161
 162        if (!tr2_tgt_want_builtins())
 163                return;
 164        trace2_enabled = 1;
 165
 166        tr2_sid_get();
 167
 168        atexit(tr2main_atexit_handler);
 169        sigchain_push(SIGPIPE, tr2main_signal_handler);
 170        tr2tls_init();
 171
 172        /*
 173         * Emit 'version' message on each active builtin target.
 174         */
 175        for_each_wanted_builtin (j, tgt_j)
 176                if (tgt_j->pfn_version_fl)
 177                        tgt_j->pfn_version_fl(file, line);
 178}
 179
 180int trace2_is_enabled(void)
 181{
 182        return trace2_enabled;
 183}
 184
 185void trace2_cmd_start_fl(const char *file, int line, const char **argv)
 186{
 187        struct tr2_tgt *tgt_j;
 188        int j;
 189        uint64_t us_now;
 190        uint64_t us_elapsed_absolute;
 191
 192        if (!trace2_enabled)
 193                return;
 194
 195        us_now = getnanotime() / 1000;
 196        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 197
 198        for_each_wanted_builtin (j, tgt_j)
 199                if (tgt_j->pfn_start_fl)
 200                        tgt_j->pfn_start_fl(file, line, us_elapsed_absolute,
 201                                            argv);
 202}
 203
 204int trace2_cmd_exit_fl(const char *file, int line, int code)
 205{
 206        struct tr2_tgt *tgt_j;
 207        int j;
 208        uint64_t us_now;
 209        uint64_t us_elapsed_absolute;
 210
 211        code &= 0xff;
 212
 213        if (!trace2_enabled)
 214                return code;
 215
 216        trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT);
 217
 218        tr2main_exit_code = code;
 219
 220        us_now = getnanotime() / 1000;
 221        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 222
 223        for_each_wanted_builtin (j, tgt_j)
 224                if (tgt_j->pfn_exit_fl)
 225                        tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute,
 226                                           code);
 227
 228        return code;
 229}
 230
 231void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt,
 232                            va_list ap)
 233{
 234        struct tr2_tgt *tgt_j;
 235        int j;
 236
 237        if (!trace2_enabled)
 238                return;
 239
 240        /*
 241         * We expect each target function to treat 'ap' as constant
 242         * and use va_copy (because an 'ap' can only be walked once).
 243         */
 244        for_each_wanted_builtin (j, tgt_j)
 245                if (tgt_j->pfn_error_va_fl)
 246                        tgt_j->pfn_error_va_fl(file, line, fmt, ap);
 247}
 248
 249void trace2_cmd_path_fl(const char *file, int line, const char *pathname)
 250{
 251        struct tr2_tgt *tgt_j;
 252        int j;
 253
 254        if (!trace2_enabled)
 255                return;
 256
 257        for_each_wanted_builtin (j, tgt_j)
 258                if (tgt_j->pfn_command_path_fl)
 259                        tgt_j->pfn_command_path_fl(file, line, pathname);
 260}
 261
 262void trace2_cmd_name_fl(const char *file, int line, const char *name)
 263{
 264        struct tr2_tgt *tgt_j;
 265        const char *hierarchy;
 266        int j;
 267
 268        if (!trace2_enabled)
 269                return;
 270
 271        tr2_cmd_name_append_hierarchy(name);
 272        hierarchy = tr2_cmd_name_get_hierarchy();
 273
 274        for_each_wanted_builtin (j, tgt_j)
 275                if (tgt_j->pfn_command_name_fl)
 276                        tgt_j->pfn_command_name_fl(file, line, name, hierarchy);
 277}
 278
 279void trace2_cmd_mode_fl(const char *file, int line, const char *mode)
 280{
 281        struct tr2_tgt *tgt_j;
 282        int j;
 283
 284        if (!trace2_enabled)
 285                return;
 286
 287        for_each_wanted_builtin (j, tgt_j)
 288                if (tgt_j->pfn_command_mode_fl)
 289                        tgt_j->pfn_command_mode_fl(file, line, mode);
 290}
 291
 292void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
 293                         const char **argv)
 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_alias_fl)
 303                        tgt_j->pfn_alias_fl(file, line, alias, argv);
 304}
 305
 306void trace2_cmd_list_config_fl(const char *file, int line)
 307{
 308        if (!trace2_enabled)
 309                return;
 310
 311        tr2_cfg_list_config_fl(file, line);
 312}
 313
 314void trace2_cmd_set_config_fl(const char *file, int line, const char *key,
 315                              const char *value)
 316{
 317        if (!trace2_enabled)
 318                return;
 319
 320        tr2_cfg_set_fl(file, line, key, value);
 321}
 322
 323void trace2_child_start_fl(const char *file, int line,
 324                           struct child_process *cmd)
 325{
 326        struct tr2_tgt *tgt_j;
 327        int j;
 328        uint64_t us_now;
 329        uint64_t us_elapsed_absolute;
 330
 331        if (!trace2_enabled)
 332                return;
 333
 334        us_now = getnanotime() / 1000;
 335        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 336
 337        cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id);
 338        cmd->trace2_child_us_start = us_now;
 339
 340        for_each_wanted_builtin (j, tgt_j)
 341                if (tgt_j->pfn_child_start_fl)
 342                        tgt_j->pfn_child_start_fl(file, line,
 343                                                  us_elapsed_absolute, cmd);
 344}
 345
 346void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
 347                          int child_exit_code)
 348{
 349        struct tr2_tgt *tgt_j;
 350        int j;
 351        uint64_t us_now;
 352        uint64_t us_elapsed_absolute;
 353        uint64_t us_elapsed_child;
 354
 355        if (!trace2_enabled)
 356                return;
 357
 358        us_now = getnanotime() / 1000;
 359        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 360
 361        if (cmd->trace2_child_us_start)
 362                us_elapsed_child = us_now - cmd->trace2_child_us_start;
 363        else
 364                us_elapsed_child = 0;
 365
 366        for_each_wanted_builtin (j, tgt_j)
 367                if (tgt_j->pfn_child_exit_fl)
 368                        tgt_j->pfn_child_exit_fl(file, line,
 369                                                 us_elapsed_absolute,
 370                                                 cmd->trace2_child_id, cmd->pid,
 371                                                 child_exit_code,
 372                                                 us_elapsed_child);
 373}
 374
 375int trace2_exec_fl(const char *file, int line, const char *exe,
 376                   const char **argv)
 377{
 378        struct tr2_tgt *tgt_j;
 379        int j;
 380        int exec_id;
 381        uint64_t us_now;
 382        uint64_t us_elapsed_absolute;
 383
 384        if (!trace2_enabled)
 385                return -1;
 386
 387        us_now = getnanotime() / 1000;
 388        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 389
 390        exec_id = tr2tls_locked_increment(&tr2_next_exec_id);
 391
 392        for_each_wanted_builtin (j, tgt_j)
 393                if (tgt_j->pfn_exec_fl)
 394                        tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute,
 395                                           exec_id, exe, argv);
 396
 397        return exec_id;
 398}
 399
 400void trace2_exec_result_fl(const char *file, int line, int exec_id, int code)
 401{
 402        struct tr2_tgt *tgt_j;
 403        int j;
 404        uint64_t us_now;
 405        uint64_t us_elapsed_absolute;
 406
 407        if (!trace2_enabled)
 408                return;
 409
 410        us_now = getnanotime() / 1000;
 411        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 412
 413        for_each_wanted_builtin (j, tgt_j)
 414                if (tgt_j->pfn_exec_result_fl)
 415                        tgt_j->pfn_exec_result_fl(
 416                                file, line, us_elapsed_absolute, exec_id, code);
 417}
 418
 419void trace2_thread_start_fl(const char *file, int line, const char *thread_name)
 420{
 421        struct tr2_tgt *tgt_j;
 422        int j;
 423        uint64_t us_now;
 424        uint64_t us_elapsed_absolute;
 425
 426        if (!trace2_enabled)
 427                return;
 428
 429        if (tr2tls_is_main_thread()) {
 430                /*
 431                 * We should only be called from the new thread's thread-proc,
 432                 * so this is technically a bug.  But in those cases where the
 433                 * main thread also runs the thread-proc function (or when we
 434                 * are built with threading disabled), we need to allow it.
 435                 *
 436                 * Convert this call to a region-enter so the nesting looks
 437                 * correct.
 438                 */
 439                trace2_region_enter_printf_fl(file, line, NULL, NULL, NULL,
 440                                              "thread-proc on main: %s",
 441                                              thread_name);
 442                return;
 443        }
 444
 445        us_now = getnanotime() / 1000;
 446        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 447
 448        tr2tls_create_self(thread_name, us_now);
 449
 450        for_each_wanted_builtin (j, tgt_j)
 451                if (tgt_j->pfn_thread_start_fl)
 452                        tgt_j->pfn_thread_start_fl(file, line,
 453                                                   us_elapsed_absolute);
 454}
 455
 456void trace2_thread_exit_fl(const char *file, int line)
 457{
 458        struct tr2_tgt *tgt_j;
 459        int j;
 460        uint64_t us_now;
 461        uint64_t us_elapsed_absolute;
 462        uint64_t us_elapsed_thread;
 463
 464        if (!trace2_enabled)
 465                return;
 466
 467        if (tr2tls_is_main_thread()) {
 468                /*
 469                 * We should only be called from the exiting thread's
 470                 * thread-proc, so this is technically a bug.  But in
 471                 * those cases where the main thread also runs the
 472                 * thread-proc function (or when we are built with
 473                 * threading disabled), we need to allow it.
 474                 *
 475                 * Convert this call to a region-leave so the nesting
 476                 * looks correct.
 477                 */
 478                trace2_region_leave_printf_fl(file, line, NULL, NULL, NULL,
 479                                              "thread-proc on main");
 480                return;
 481        }
 482
 483        us_now = getnanotime() / 1000;
 484        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 485
 486        /*
 487         * Clear any unbalanced regions and then get the relative time
 488         * for the outer-most region (which we pushed when the thread
 489         * started).  This gives us the run time of the thread.
 490         */
 491        tr2tls_pop_unwind_self();
 492        us_elapsed_thread = tr2tls_region_elasped_self(us_now);
 493
 494        for_each_wanted_builtin (j, tgt_j)
 495                if (tgt_j->pfn_thread_exit_fl)
 496                        tgt_j->pfn_thread_exit_fl(file, line,
 497                                                  us_elapsed_absolute,
 498                                                  us_elapsed_thread);
 499
 500        tr2tls_unset_self();
 501}
 502
 503void trace2_def_param_fl(const char *file, int line, const char *param,
 504                         const char *value)
 505{
 506        struct tr2_tgt *tgt_j;
 507        int j;
 508
 509        if (!trace2_enabled)
 510                return;
 511
 512        for_each_wanted_builtin (j, tgt_j)
 513                if (tgt_j->pfn_param_fl)
 514                        tgt_j->pfn_param_fl(file, line, param, value);
 515}
 516
 517void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
 518{
 519        struct tr2_tgt *tgt_j;
 520        int j;
 521
 522        if (!trace2_enabled)
 523                return;
 524
 525        if (repo->trace2_repo_id)
 526                return;
 527
 528        repo->trace2_repo_id = tr2tls_locked_increment(&tr2_next_repo_id);
 529
 530        for_each_wanted_builtin (j, tgt_j)
 531                if (tgt_j->pfn_repo_fl)
 532                        tgt_j->pfn_repo_fl(file, line, repo);
 533}
 534
 535void trace2_region_enter_printf_va_fl(const char *file, int line,
 536                                      const char *category, const char *label,
 537                                      const struct repository *repo,
 538                                      const char *fmt, va_list ap)
 539{
 540        struct tr2_tgt *tgt_j;
 541        int j;
 542        uint64_t us_now;
 543        uint64_t us_elapsed_absolute;
 544
 545        if (!trace2_enabled)
 546                return;
 547
 548        us_now = getnanotime() / 1000;
 549        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 550
 551        /*
 552         * Print the region-enter message at the current nesting
 553         * (indentation) level and then push a new level.
 554         *
 555         * We expect each target function to treat 'ap' as constant
 556         * and use va_copy.
 557         */
 558        for_each_wanted_builtin (j, tgt_j)
 559                if (tgt_j->pfn_region_enter_printf_va_fl)
 560                        tgt_j->pfn_region_enter_printf_va_fl(
 561                                file, line, us_elapsed_absolute, category,
 562                                label, repo, fmt, ap);
 563
 564        tr2tls_push_self(us_now);
 565}
 566
 567void trace2_region_enter_fl(const char *file, int line, const char *category,
 568                            const char *label, const struct repository *repo)
 569{
 570        trace2_region_enter_printf_va_fl(file, line, category, label, repo,
 571                                         NULL, NULL);
 572}
 573
 574void trace2_region_enter_printf_fl(const char *file, int line,
 575                                   const char *category, const char *label,
 576                                   const struct repository *repo,
 577                                   const char *fmt, ...)
 578{
 579        va_list ap;
 580
 581        va_start(ap, fmt);
 582        trace2_region_enter_printf_va_fl(file, line, category, label, repo, fmt,
 583                                         ap);
 584        va_end(ap);
 585}
 586
 587#ifndef HAVE_VARIADIC_MACROS
 588void trace2_region_enter_printf(const char *category, const char *label,
 589                                const struct repository *repo, const char *fmt,
 590                                ...)
 591{
 592        va_list ap;
 593
 594        va_start(ap, fmt);
 595        trace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,
 596                                         ap);
 597        va_end(ap);
 598}
 599#endif
 600
 601void trace2_region_leave_printf_va_fl(const char *file, int line,
 602                                      const char *category, const char *label,
 603                                      const struct repository *repo,
 604                                      const char *fmt, va_list ap)
 605{
 606        struct tr2_tgt *tgt_j;
 607        int j;
 608        uint64_t us_now;
 609        uint64_t us_elapsed_absolute;
 610        uint64_t us_elapsed_region;
 611
 612        if (!trace2_enabled)
 613                return;
 614
 615        us_now = getnanotime() / 1000;
 616        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 617
 618        /*
 619         * Get the elapsed time in the current region before we
 620         * pop it off the stack.  Pop the stack.  And then print
 621         * the perf message at the new (shallower) level so that
 622         * it lines up with the corresponding push/enter.
 623         */
 624        us_elapsed_region = tr2tls_region_elasped_self(us_now);
 625
 626        tr2tls_pop_self();
 627
 628        /*
 629         * We expect each target function to treat 'ap' as constant
 630         * and use va_copy.
 631         */
 632        for_each_wanted_builtin (j, tgt_j)
 633                if (tgt_j->pfn_region_leave_printf_va_fl)
 634                        tgt_j->pfn_region_leave_printf_va_fl(
 635                                file, line, us_elapsed_absolute,
 636                                us_elapsed_region, category, label, repo, fmt,
 637                                ap);
 638}
 639
 640void trace2_region_leave_fl(const char *file, int line, const char *category,
 641                            const char *label, const struct repository *repo)
 642{
 643        trace2_region_leave_printf_va_fl(file, line, category, label, repo,
 644                                         NULL, NULL);
 645}
 646
 647void trace2_region_leave_printf_fl(const char *file, int line,
 648                                   const char *category, const char *label,
 649                                   const struct repository *repo,
 650                                   const char *fmt, ...)
 651{
 652        va_list ap;
 653
 654        va_start(ap, fmt);
 655        trace2_region_leave_printf_va_fl(file, line, category, label, repo, fmt,
 656                                         ap);
 657        va_end(ap);
 658}
 659
 660#ifndef HAVE_VARIADIC_MACROS
 661void trace2_region_leave_printf(const char *category, const char *label,
 662                                const struct repository *repo, const char *fmt,
 663                                ...)
 664{
 665        va_list ap;
 666
 667        va_start(ap, fmt);
 668        trace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,
 669                                         ap);
 670        va_end(ap);
 671}
 672#endif
 673
 674void trace2_data_string_fl(const char *file, int line, const char *category,
 675                           const struct repository *repo, const char *key,
 676                           const char *value)
 677{
 678        struct tr2_tgt *tgt_j;
 679        int j;
 680        uint64_t us_now;
 681        uint64_t us_elapsed_absolute;
 682        uint64_t us_elapsed_region;
 683
 684        if (!trace2_enabled)
 685                return;
 686
 687        us_now = getnanotime() / 1000;
 688        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 689        us_elapsed_region = tr2tls_region_elasped_self(us_now);
 690
 691        for_each_wanted_builtin (j, tgt_j)
 692                if (tgt_j->pfn_data_fl)
 693                        tgt_j->pfn_data_fl(file, line, us_elapsed_absolute,
 694                                           us_elapsed_region, category, repo,
 695                                           key, value);
 696}
 697
 698void trace2_data_intmax_fl(const char *file, int line, const char *category,
 699                           const struct repository *repo, const char *key,
 700                           intmax_t value)
 701{
 702        struct strbuf buf_string = STRBUF_INIT;
 703
 704        if (!trace2_enabled)
 705                return;
 706
 707        strbuf_addf(&buf_string, "%" PRIdMAX, value);
 708        trace2_data_string_fl(file, line, category, repo, key, buf_string.buf);
 709        strbuf_release(&buf_string);
 710}
 711
 712void trace2_data_json_fl(const char *file, int line, const char *category,
 713                         const struct repository *repo, const char *key,
 714                         const struct json_writer *value)
 715{
 716        struct tr2_tgt *tgt_j;
 717        int j;
 718        uint64_t us_now;
 719        uint64_t us_elapsed_absolute;
 720        uint64_t us_elapsed_region;
 721
 722        if (!trace2_enabled)
 723                return;
 724
 725        us_now = getnanotime() / 1000;
 726        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 727        us_elapsed_region = tr2tls_region_elasped_self(us_now);
 728
 729        for_each_wanted_builtin (j, tgt_j)
 730                if (tgt_j->pfn_data_fl)
 731                        tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute,
 732                                                us_elapsed_region, category,
 733                                                repo, key, value);
 734}
 735
 736void trace2_printf_va_fl(const char *file, int line, const char *fmt,
 737                         va_list ap)
 738{
 739        struct tr2_tgt *tgt_j;
 740        int j;
 741        uint64_t us_now;
 742        uint64_t us_elapsed_absolute;
 743
 744        if (!trace2_enabled)
 745                return;
 746
 747        us_now = getnanotime() / 1000;
 748        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 749
 750        /*
 751         * We expect each target function to treat 'ap' as constant
 752         * and use va_copy.
 753         */
 754        for_each_wanted_builtin (j, tgt_j)
 755                if (tgt_j->pfn_printf_va_fl)
 756                        tgt_j->pfn_printf_va_fl(file, line, us_elapsed_absolute,
 757                                                fmt, ap);
 758}
 759
 760void trace2_printf_fl(const char *file, int line, const char *fmt, ...)
 761{
 762        va_list ap;
 763
 764        va_start(ap, fmt);
 765        trace2_printf_va_fl(file, line, fmt, ap);
 766        va_end(ap);
 767}
 768
 769#ifndef HAVE_VARIADIC_MACROS
 770void trace2_printf(const char *fmt, ...)
 771{
 772        va_list ap;
 773
 774        va_start(ap, fmt);
 775        trace2_printf_va_fl(NULL, 0, fmt, ap);
 776        va_end(ap);
 777}
 778#endif