Merge branch 'ab/config-based-hooks-base' into seen
[git] / trace2.c
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 int trace2_exec_fl(const char *file, int line, const char *exe,
398                    const char **argv)
399 {
400         struct tr2_tgt *tgt_j;
401         int j;
402         int exec_id;
403         uint64_t us_now;
404         uint64_t us_elapsed_absolute;
405
406         if (!trace2_enabled)
407                 return -1;
408
409         us_now = getnanotime() / 1000;
410         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
411
412         exec_id = tr2tls_locked_increment(&tr2_next_exec_id);
413
414         for_each_wanted_builtin (j, tgt_j)
415                 if (tgt_j->pfn_exec_fl)
416                         tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute,
417                                            exec_id, exe, argv);
418
419         return exec_id;
420 }
421
422 void trace2_exec_result_fl(const char *file, int line, int exec_id, int code)
423 {
424         struct tr2_tgt *tgt_j;
425         int j;
426         uint64_t us_now;
427         uint64_t us_elapsed_absolute;
428
429         if (!trace2_enabled)
430                 return;
431
432         us_now = getnanotime() / 1000;
433         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
434
435         for_each_wanted_builtin (j, tgt_j)
436                 if (tgt_j->pfn_exec_result_fl)
437                         tgt_j->pfn_exec_result_fl(
438                                 file, line, us_elapsed_absolute, exec_id, code);
439 }
440
441 void trace2_thread_start_fl(const char *file, int line, const char *thread_name)
442 {
443         struct tr2_tgt *tgt_j;
444         int j;
445         uint64_t us_now;
446         uint64_t us_elapsed_absolute;
447
448         if (!trace2_enabled)
449                 return;
450
451         if (tr2tls_is_main_thread()) {
452                 /*
453                  * We should only be called from the new thread's thread-proc,
454                  * so this is technically a bug.  But in those cases where the
455                  * main thread also runs the thread-proc function (or when we
456                  * are built with threading disabled), we need to allow it.
457                  *
458                  * Convert this call to a region-enter so the nesting looks
459                  * correct.
460                  */
461                 trace2_region_enter_printf_fl(file, line, NULL, NULL, NULL,
462                                               "thread-proc on main: %s",
463                                               thread_name);
464                 return;
465         }
466
467         us_now = getnanotime() / 1000;
468         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
469
470         tr2tls_create_self(thread_name, us_now);
471
472         for_each_wanted_builtin (j, tgt_j)
473                 if (tgt_j->pfn_thread_start_fl)
474                         tgt_j->pfn_thread_start_fl(file, line,
475                                                    us_elapsed_absolute);
476 }
477
478 void trace2_thread_exit_fl(const char *file, int line)
479 {
480         struct tr2_tgt *tgt_j;
481         int j;
482         uint64_t us_now;
483         uint64_t us_elapsed_absolute;
484         uint64_t us_elapsed_thread;
485
486         if (!trace2_enabled)
487                 return;
488
489         if (tr2tls_is_main_thread()) {
490                 /*
491                  * We should only be called from the exiting thread's
492                  * thread-proc, so this is technically a bug.  But in
493                  * those cases where the main thread also runs the
494                  * thread-proc function (or when we are built with
495                  * threading disabled), we need to allow it.
496                  *
497                  * Convert this call to a region-leave so the nesting
498                  * looks correct.
499                  */
500                 trace2_region_leave_printf_fl(file, line, NULL, NULL, NULL,
501                                               "thread-proc on main");
502                 return;
503         }
504
505         us_now = getnanotime() / 1000;
506         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
507
508         /*
509          * Clear any unbalanced regions and then get the relative time
510          * for the outer-most region (which we pushed when the thread
511          * started).  This gives us the run time of the thread.
512          */
513         tr2tls_pop_unwind_self();
514         us_elapsed_thread = tr2tls_region_elasped_self(us_now);
515
516         for_each_wanted_builtin (j, tgt_j)
517                 if (tgt_j->pfn_thread_exit_fl)
518                         tgt_j->pfn_thread_exit_fl(file, line,
519                                                   us_elapsed_absolute,
520                                                   us_elapsed_thread);
521
522         tr2tls_unset_self();
523 }
524
525 void trace2_def_param_fl(const char *file, int line, const char *param,
526                          const char *value)
527 {
528         struct tr2_tgt *tgt_j;
529         int j;
530
531         if (!trace2_enabled)
532                 return;
533
534         for_each_wanted_builtin (j, tgt_j)
535                 if (tgt_j->pfn_param_fl)
536                         tgt_j->pfn_param_fl(file, line, param, value);
537 }
538
539 void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
540 {
541         struct tr2_tgt *tgt_j;
542         int j;
543
544         if (!trace2_enabled)
545                 return;
546
547         if (repo->trace2_repo_id)
548                 return;
549
550         repo->trace2_repo_id = tr2tls_locked_increment(&tr2_next_repo_id);
551
552         for_each_wanted_builtin (j, tgt_j)
553                 if (tgt_j->pfn_repo_fl)
554                         tgt_j->pfn_repo_fl(file, line, repo);
555 }
556
557 void trace2_region_enter_printf_va_fl(const char *file, int line,
558                                       const char *category, const char *label,
559                                       const struct repository *repo,
560                                       const char *fmt, va_list ap)
561 {
562         struct tr2_tgt *tgt_j;
563         int j;
564         uint64_t us_now;
565         uint64_t us_elapsed_absolute;
566
567         if (!trace2_enabled)
568                 return;
569
570         us_now = getnanotime() / 1000;
571         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
572
573         /*
574          * Print the region-enter message at the current nesting
575          * (indentation) level and then push a new level.
576          *
577          * We expect each target function to treat 'ap' as constant
578          * and use va_copy.
579          */
580         for_each_wanted_builtin (j, tgt_j)
581                 if (tgt_j->pfn_region_enter_printf_va_fl)
582                         tgt_j->pfn_region_enter_printf_va_fl(
583                                 file, line, us_elapsed_absolute, category,
584                                 label, repo, fmt, ap);
585
586         tr2tls_push_self(us_now);
587 }
588
589 void trace2_region_enter_fl(const char *file, int line, const char *category,
590                             const char *label, const struct repository *repo, ...)
591 {
592         va_list ap;
593         va_start(ap, repo);
594         trace2_region_enter_printf_va_fl(file, line, category, label, repo,
595                                          NULL, ap);
596         va_end(ap);
597
598 }
599
600 void trace2_region_enter_printf_fl(const char *file, int line,
601                                    const char *category, const char *label,
602                                    const struct repository *repo,
603                                    const char *fmt, ...)
604 {
605         va_list ap;
606
607         va_start(ap, fmt);
608         trace2_region_enter_printf_va_fl(file, line, category, label, repo, fmt,
609                                          ap);
610         va_end(ap);
611 }
612
613 #ifndef HAVE_VARIADIC_MACROS
614 void trace2_region_enter_printf(const char *category, const char *label,
615                                 const struct repository *repo, const char *fmt,
616                                 ...)
617 {
618         va_list ap;
619
620         va_start(ap, fmt);
621         trace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,
622                                          ap);
623         va_end(ap);
624 }
625 #endif
626
627 void trace2_region_leave_printf_va_fl(const char *file, int line,
628                                       const char *category, const char *label,
629                                       const struct repository *repo,
630                                       const char *fmt, va_list ap)
631 {
632         struct tr2_tgt *tgt_j;
633         int j;
634         uint64_t us_now;
635         uint64_t us_elapsed_absolute;
636         uint64_t us_elapsed_region;
637
638         if (!trace2_enabled)
639                 return;
640
641         us_now = getnanotime() / 1000;
642         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
643
644         /*
645          * Get the elapsed time in the current region before we
646          * pop it off the stack.  Pop the stack.  And then print
647          * the perf message at the new (shallower) level so that
648          * it lines up with the corresponding push/enter.
649          */
650         us_elapsed_region = tr2tls_region_elasped_self(us_now);
651
652         tr2tls_pop_self();
653
654         /*
655          * We expect each target function to treat 'ap' as constant
656          * and use va_copy.
657          */
658         for_each_wanted_builtin (j, tgt_j)
659                 if (tgt_j->pfn_region_leave_printf_va_fl)
660                         tgt_j->pfn_region_leave_printf_va_fl(
661                                 file, line, us_elapsed_absolute,
662                                 us_elapsed_region, category, label, repo, fmt,
663                                 ap);
664 }
665
666 void trace2_region_leave_fl(const char *file, int line, const char *category,
667                             const char *label, const struct repository *repo, ...)
668 {
669         va_list ap;
670         va_start(ap, repo);
671         trace2_region_leave_printf_va_fl(file, line, category, label, repo,
672                                          NULL, ap);
673         va_end(ap);
674 }
675
676 void trace2_region_leave_printf_fl(const char *file, int line,
677                                    const char *category, const char *label,
678                                    const struct repository *repo,
679                                    const char *fmt, ...)
680 {
681         va_list ap;
682
683         va_start(ap, fmt);
684         trace2_region_leave_printf_va_fl(file, line, category, label, repo, fmt,
685                                          ap);
686         va_end(ap);
687 }
688
689 #ifndef HAVE_VARIADIC_MACROS
690 void trace2_region_leave_printf(const char *category, const char *label,
691                                 const struct repository *repo, const char *fmt,
692                                 ...)
693 {
694         va_list ap;
695
696         va_start(ap, fmt);
697         trace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,
698                                          ap);
699         va_end(ap);
700 }
701 #endif
702
703 void trace2_data_string_fl(const char *file, int line, const char *category,
704                            const struct repository *repo, const char *key,
705                            const char *value)
706 {
707         struct tr2_tgt *tgt_j;
708         int j;
709         uint64_t us_now;
710         uint64_t us_elapsed_absolute;
711         uint64_t us_elapsed_region;
712
713         if (!trace2_enabled)
714                 return;
715
716         us_now = getnanotime() / 1000;
717         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
718         us_elapsed_region = tr2tls_region_elasped_self(us_now);
719
720         for_each_wanted_builtin (j, tgt_j)
721                 if (tgt_j->pfn_data_fl)
722                         tgt_j->pfn_data_fl(file, line, us_elapsed_absolute,
723                                            us_elapsed_region, category, repo,
724                                            key, value);
725 }
726
727 void trace2_data_intmax_fl(const char *file, int line, const char *category,
728                            const struct repository *repo, const char *key,
729                            intmax_t value)
730 {
731         struct strbuf buf_string = STRBUF_INIT;
732
733         if (!trace2_enabled)
734                 return;
735
736         strbuf_addf(&buf_string, "%" PRIdMAX, value);
737         trace2_data_string_fl(file, line, category, repo, key, buf_string.buf);
738         strbuf_release(&buf_string);
739 }
740
741 void trace2_data_json_fl(const char *file, int line, const char *category,
742                          const struct repository *repo, const char *key,
743                          const struct json_writer *value)
744 {
745         struct tr2_tgt *tgt_j;
746         int j;
747         uint64_t us_now;
748         uint64_t us_elapsed_absolute;
749         uint64_t us_elapsed_region;
750
751         if (!trace2_enabled)
752                 return;
753
754         us_now = getnanotime() / 1000;
755         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
756         us_elapsed_region = tr2tls_region_elasped_self(us_now);
757
758         for_each_wanted_builtin (j, tgt_j)
759                 if (tgt_j->pfn_data_json_fl)
760                         tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute,
761                                                 us_elapsed_region, category,
762                                                 repo, key, value);
763 }
764
765 void trace2_printf_va_fl(const char *file, int line, const char *fmt,
766                          va_list ap)
767 {
768         struct tr2_tgt *tgt_j;
769         int j;
770         uint64_t us_now;
771         uint64_t us_elapsed_absolute;
772
773         if (!trace2_enabled)
774                 return;
775
776         us_now = getnanotime() / 1000;
777         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
778
779         /*
780          * We expect each target function to treat 'ap' as constant
781          * and use va_copy.
782          */
783         for_each_wanted_builtin (j, tgt_j)
784                 if (tgt_j->pfn_printf_va_fl)
785                         tgt_j->pfn_printf_va_fl(file, line, us_elapsed_absolute,
786                                                 fmt, ap);
787 }
788
789 void trace2_printf_fl(const char *file, int line, const char *fmt, ...)
790 {
791         va_list ap;
792
793         va_start(ap, fmt);
794         trace2_printf_va_fl(file, line, fmt, ap);
795         va_end(ap);
796 }
797
798 #ifndef HAVE_VARIADIC_MACROS
799 void trace2_printf(const char *fmt, ...)
800 {
801         va_list ap;
802
803         va_start(ap, fmt);
804         trace2_printf_va_fl(NULL, 0, fmt, ap);
805         va_end(ap);
806 }
807 #endif
808
809 const char *trace2_session_id(void)
810 {
811         return tr2_sid_get();
812 }