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