Merge branch 'ds/sparse-add'
[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_sysenv_release();
125
126         trace2_enabled = 0;
127 }
128
129 static 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
147 void trace2_initialize_clock(void)
148 {
149         tr2tls_start_process_clock();
150 }
151
152 void 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
180 int trace2_is_enabled(void)
181 {
182         return trace2_enabled;
183 }
184
185 void 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
204 int 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
231 void 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
249 void 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
262 void 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
279 void 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
292 void 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
306 void 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
314 void 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
323 void 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
346 void 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
375 int 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
400 void 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
419 void 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
456 void 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
503 void 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
517 void 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
535 void 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
567 void trace2_region_enter_fl(const char *file, int line, const char *category,
568                             const char *label, const struct repository *repo, ...)
569 {
570         va_list ap;
571         va_start(ap, repo);
572         trace2_region_enter_printf_va_fl(file, line, category, label, repo,
573                                          NULL, ap);
574         va_end(ap);
575
576 }
577
578 void trace2_region_enter_printf_fl(const char *file, int line,
579                                    const char *category, const char *label,
580                                    const struct repository *repo,
581                                    const char *fmt, ...)
582 {
583         va_list ap;
584
585         va_start(ap, fmt);
586         trace2_region_enter_printf_va_fl(file, line, category, label, repo, fmt,
587                                          ap);
588         va_end(ap);
589 }
590
591 #ifndef HAVE_VARIADIC_MACROS
592 void trace2_region_enter_printf(const char *category, const char *label,
593                                 const struct repository *repo, const char *fmt,
594                                 ...)
595 {
596         va_list ap;
597
598         va_start(ap, fmt);
599         trace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,
600                                          ap);
601         va_end(ap);
602 }
603 #endif
604
605 void trace2_region_leave_printf_va_fl(const char *file, int line,
606                                       const char *category, const char *label,
607                                       const struct repository *repo,
608                                       const char *fmt, va_list ap)
609 {
610         struct tr2_tgt *tgt_j;
611         int j;
612         uint64_t us_now;
613         uint64_t us_elapsed_absolute;
614         uint64_t us_elapsed_region;
615
616         if (!trace2_enabled)
617                 return;
618
619         us_now = getnanotime() / 1000;
620         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
621
622         /*
623          * Get the elapsed time in the current region before we
624          * pop it off the stack.  Pop the stack.  And then print
625          * the perf message at the new (shallower) level so that
626          * it lines up with the corresponding push/enter.
627          */
628         us_elapsed_region = tr2tls_region_elasped_self(us_now);
629
630         tr2tls_pop_self();
631
632         /*
633          * We expect each target function to treat 'ap' as constant
634          * and use va_copy.
635          */
636         for_each_wanted_builtin (j, tgt_j)
637                 if (tgt_j->pfn_region_leave_printf_va_fl)
638                         tgt_j->pfn_region_leave_printf_va_fl(
639                                 file, line, us_elapsed_absolute,
640                                 us_elapsed_region, category, label, repo, fmt,
641                                 ap);
642 }
643
644 void trace2_region_leave_fl(const char *file, int line, const char *category,
645                             const char *label, const struct repository *repo, ...)
646 {
647         va_list ap;
648         va_start(ap, repo);
649         trace2_region_leave_printf_va_fl(file, line, category, label, repo,
650                                          NULL, ap);
651         va_end(ap);
652 }
653
654 void trace2_region_leave_printf_fl(const char *file, int line,
655                                    const char *category, const char *label,
656                                    const struct repository *repo,
657                                    const char *fmt, ...)
658 {
659         va_list ap;
660
661         va_start(ap, fmt);
662         trace2_region_leave_printf_va_fl(file, line, category, label, repo, fmt,
663                                          ap);
664         va_end(ap);
665 }
666
667 #ifndef HAVE_VARIADIC_MACROS
668 void trace2_region_leave_printf(const char *category, const char *label,
669                                 const struct repository *repo, const char *fmt,
670                                 ...)
671 {
672         va_list ap;
673
674         va_start(ap, fmt);
675         trace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,
676                                          ap);
677         va_end(ap);
678 }
679 #endif
680
681 void trace2_data_string_fl(const char *file, int line, const char *category,
682                            const struct repository *repo, const char *key,
683                            const char *value)
684 {
685         struct tr2_tgt *tgt_j;
686         int j;
687         uint64_t us_now;
688         uint64_t us_elapsed_absolute;
689         uint64_t us_elapsed_region;
690
691         if (!trace2_enabled)
692                 return;
693
694         us_now = getnanotime() / 1000;
695         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
696         us_elapsed_region = tr2tls_region_elasped_self(us_now);
697
698         for_each_wanted_builtin (j, tgt_j)
699                 if (tgt_j->pfn_data_fl)
700                         tgt_j->pfn_data_fl(file, line, us_elapsed_absolute,
701                                            us_elapsed_region, category, repo,
702                                            key, value);
703 }
704
705 void trace2_data_intmax_fl(const char *file, int line, const char *category,
706                            const struct repository *repo, const char *key,
707                            intmax_t value)
708 {
709         struct strbuf buf_string = STRBUF_INIT;
710
711         if (!trace2_enabled)
712                 return;
713
714         strbuf_addf(&buf_string, "%" PRIdMAX, value);
715         trace2_data_string_fl(file, line, category, repo, key, buf_string.buf);
716         strbuf_release(&buf_string);
717 }
718
719 void trace2_data_json_fl(const char *file, int line, const char *category,
720                          const struct repository *repo, const char *key,
721                          const struct json_writer *value)
722 {
723         struct tr2_tgt *tgt_j;
724         int j;
725         uint64_t us_now;
726         uint64_t us_elapsed_absolute;
727         uint64_t us_elapsed_region;
728
729         if (!trace2_enabled)
730                 return;
731
732         us_now = getnanotime() / 1000;
733         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
734         us_elapsed_region = tr2tls_region_elasped_self(us_now);
735
736         for_each_wanted_builtin (j, tgt_j)
737                 if (tgt_j->pfn_data_json_fl)
738                         tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute,
739                                                 us_elapsed_region, category,
740                                                 repo, key, value);
741 }
742
743 void trace2_printf_va_fl(const char *file, int line, const char *fmt,
744                          va_list ap)
745 {
746         struct tr2_tgt *tgt_j;
747         int j;
748         uint64_t us_now;
749         uint64_t us_elapsed_absolute;
750
751         if (!trace2_enabled)
752                 return;
753
754         us_now = getnanotime() / 1000;
755         us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
756
757         /*
758          * We expect each target function to treat 'ap' as constant
759          * and use va_copy.
760          */
761         for_each_wanted_builtin (j, tgt_j)
762                 if (tgt_j->pfn_printf_va_fl)
763                         tgt_j->pfn_printf_va_fl(file, line, us_elapsed_absolute,
764                                                 fmt, ap);
765 }
766
767 void trace2_printf_fl(const char *file, int line, const char *fmt, ...)
768 {
769         va_list ap;
770
771         va_start(ap, fmt);
772         trace2_printf_va_fl(file, line, fmt, ap);
773         va_end(ap);
774 }
775
776 #ifndef HAVE_VARIADIC_MACROS
777 void trace2_printf(const char *fmt, ...)
778 {
779         va_list ap;
780
781         va_start(ap, fmt);
782         trace2_printf_va_fl(NULL, 0, fmt, ap);
783         va_end(ap);
784 }
785 #endif