Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Use standard __func__ instead of __FUNCTION__.
[simgrid.git] / src / xbt / log.c
1 /* log - a generic logging facility in the spirit of log4j                  */
2
3 /* Copyright (c) 2004-2018. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 #include <stdarg.h>
10 #include <ctype.h>
11 #include <stdio.h>              /* snprintf */
12 #include <stdlib.h>             /* snprintf */
13
14 #include "src/internal_config.h"
15
16 #include "src/xbt_modinter.h"
17
18 #include "src/xbt/log_private.h"
19 #include "xbt/asserts.h"
20 #include "xbt/dynar.h"
21 #include "xbt/ex.h"
22 #include "xbt/misc.h"
23 #include "xbt/str.h"
24 #include "xbt/sysdep.h"
25 #include "xbt/xbt_os_thread.h"
26
27 int xbt_log_no_loc = 0; /* if set to true (with --log=no_loc), file localization will be omitted (for tesh tests) */
28 static xbt_os_mutex_t log_cat_init_mutex = NULL;
29
30 /** \addtogroup XBT_log
31  *
32  *  For more information, please refer to @ref outcomes_logs Section.
33  */
34
35 xbt_log_appender_t xbt_log_default_appender = NULL;     /* set in log_init */
36 xbt_log_layout_t xbt_log_default_layout = NULL; /* set in log_init */
37
38 typedef struct {
39   char *catname;
40   char *fmt;
41   e_xbt_log_priority_t thresh;
42   int additivity;
43   xbt_log_appender_t appender;
44 } s_xbt_log_setting_t;
45
46 typedef s_xbt_log_setting_t* xbt_log_setting_t;
47
48 static xbt_dynar_t xbt_log_settings = NULL;
49
50 static void _free_setting(void *s)
51 {
52   xbt_log_setting_t set = *(xbt_log_setting_t *) s;
53   if (set) {
54     free(set->catname);
55     free(set->fmt);
56     free(set);
57   }
58 }
59
60 static void _xbt_log_cat_apply_set(xbt_log_category_t category, xbt_log_setting_t setting);
61
62 const char *xbt_log_priority_names[8] = {
63   "NONE",
64   "TRACE",
65   "DEBUG",
66   "VERBOSE",
67   "INFO",
68   "WARNING",
69   "ERROR",
70   "CRITICAL"
71 };
72
73 s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = {
74   NULL /*parent */ , NULL /* firstChild */ , NULL /* nextSibling */ ,
75       "root", "The common ancestor for all categories",
76       0 /*initialized */, xbt_log_priority_uninitialized /* threshold */ ,
77       0 /* isThreshInherited */ ,
78       NULL /* appender */ , NULL /* layout */ ,
79       0                         /* additivity */
80 };
81
82 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log, xbt, "Loggings from the logging mechanism itself");
83
84 /* create the default appender and install it in the root category,
85    which were already created (damnit. Too slow little beetle) */
86 void xbt_log_preinit(void)
87 {
88   xbt_log_default_appender = xbt_log_appender_file_new(NULL);
89   xbt_log_default_layout = xbt_log_layout_simple_new(NULL);
90   _XBT_LOGV(XBT_LOG_ROOT_CAT).appender = xbt_log_default_appender;
91   _XBT_LOGV(XBT_LOG_ROOT_CAT).layout = xbt_log_default_layout;
92   log_cat_init_mutex = xbt_os_mutex_init();
93 }
94
95 static void xbt_log_help(void);
96 static void xbt_log_help_categories(void);
97
98 /** @brief Get all logging settings from the command line
99  *
100  * xbt_log_control_set() is called on each string we got from cmd line
101  */
102 void xbt_log_init(int *argc, char **argv)
103 {
104   unsigned help_requested = 0;  /* 1: logs; 2: categories */
105   int j                   = 1;
106
107   /* Set logs and init log submodule */
108   for (int i = 1; i < *argc; i++) {
109     if (!strncmp(argv[i], "--log=", strlen("--log="))) {
110       char* opt = strchr(argv[i], '=');
111       opt++;
112       xbt_log_control_set(opt);
113       XBT_DEBUG("Did apply '%s' as log setting", opt);
114     } else if (!strcmp(argv[i], "--help-logs")) {
115       help_requested |= 1U;
116     } else if (!strcmp(argv[i], "--help-log-categories")) {
117       help_requested |= 2U;
118     } else {
119       argv[j++] = argv[i];
120     }
121   }
122   if (j < *argc) {
123     argv[j] = NULL;
124     *argc = j;
125   }
126
127   if (help_requested) {
128     if (help_requested & 1)
129       xbt_log_help();
130     if (help_requested & 2)
131       xbt_log_help_categories();
132     exit(0);
133   }
134 }
135
136 static void log_cat_exit(xbt_log_category_t cat)
137 {
138   xbt_log_category_t child;
139
140   if (cat->appender) {
141     if (cat->appender->free_)
142       cat->appender->free_(cat->appender);
143     free(cat->appender);
144   }
145   if (cat->layout) {
146     if (cat->layout->free_)
147       cat->layout->free_(cat->layout);
148     free(cat->layout);
149   }
150
151   for (child = cat->firstChild; child != NULL; child = child->nextSibling)
152     log_cat_exit(child);
153 }
154
155 void xbt_log_postexit(void)
156 {
157   XBT_VERB("Exiting log");
158   xbt_os_mutex_destroy(log_cat_init_mutex);
159   xbt_dynar_free(&xbt_log_settings);
160   log_cat_exit(&_XBT_LOGV(XBT_LOG_ROOT_CAT));
161 }
162
163 /* Size of the static string in which we build the log string */
164 #define XBT_LOG_STATIC_BUFFER_SIZE 2048
165 /* Minimum size of the dynamic string in which we build the log string
166    (should be greater than XBT_LOG_STATIC_BUFFER_SIZE) */
167 #define XBT_LOG_DYNAMIC_BUFFER_SIZE 4096
168
169 void _xbt_log_event_log(xbt_log_event_t ev, const char *fmt, ...)
170 {
171   xbt_log_category_t cat = ev->cat;
172
173   xbt_assert(ev->priority >= 0, "Negative logging priority naturally forbidden");
174   xbt_assert(ev->priority < sizeof(xbt_log_priority_names), "Priority %d is greater than the biggest allowed value",
175              ev->priority);
176
177   while (1) {
178     xbt_log_appender_t appender = cat->appender;
179
180     if (appender != NULL) {
181       xbt_assert(cat->layout, "No valid layout for the appender of category %s", cat->name);
182
183       /* First, try with a static buffer */
184       int done = 0;
185       char buff[XBT_LOG_STATIC_BUFFER_SIZE];
186       ev->buffer      = buff;
187       ev->buffer_size = sizeof buff;
188       va_start(ev->ap, fmt);
189       done = cat->layout->do_layout(cat->layout, ev, fmt);
190       va_end(ev->ap);
191       if (done) {
192         appender->do_append(appender, buff);
193       } else {
194
195         /* The static buffer was too small, use a dynamically expanded one */
196         ev->buffer_size = XBT_LOG_DYNAMIC_BUFFER_SIZE;
197         ev->buffer      = xbt_malloc(ev->buffer_size);
198         while (1) {
199           va_start(ev->ap, fmt);
200           done = cat->layout->do_layout(cat->layout, ev, fmt);
201           va_end(ev->ap);
202           if (done)
203             break; /* Got it */
204           ev->buffer_size *= 2;
205           ev->buffer = xbt_realloc(ev->buffer, ev->buffer_size);
206         }
207         appender->do_append(appender, ev->buffer);
208         xbt_free(ev->buffer);
209       }
210     }
211
212     if (!cat->additivity)
213       break;
214     cat = cat->parent;
215   }
216 }
217
218 /* NOTE:
219  *
220  * The standard logging macros use _XBT_LOG_ISENABLED, which calls _xbt_log_cat_init().  Thus, if we want to avoid an
221  * infinite recursion, we can not use the standard logging macros in _xbt_log_cat_init(), and in all functions called
222  * from it.
223  *
224  * To circumvent the problem, we define the macro DISABLE_XBT_LOG_CAT_INIT() to hide the real _xbt_log_cat_init(). The
225  * macro has to be called at the beginning of the affected functions.
226  */
227 static int fake_xbt_log_cat_init(xbt_log_category_t XBT_ATTRIB_UNUSED category,
228                                  e_xbt_log_priority_t XBT_ATTRIB_UNUSED priority)
229 {
230   return 0;
231 }
232 #define DISABLE_XBT_LOG_CAT_INIT()                                                                                     \
233   int (*_xbt_log_cat_init)(xbt_log_category_t, e_xbt_log_priority_t) XBT_ATTRIB_UNUSED = fake_xbt_log_cat_init;
234
235 static void _xbt_log_cat_apply_set(xbt_log_category_t category, xbt_log_setting_t setting)
236 {
237   DISABLE_XBT_LOG_CAT_INIT();
238   if (setting->thresh != xbt_log_priority_uninitialized) {
239     xbt_log_threshold_set(category, setting->thresh);
240
241     XBT_DEBUG("Apply settings for category '%s': set threshold to %s (=%d)",
242            category->name, xbt_log_priority_names[category->threshold], category->threshold);
243   }
244
245   if (setting->fmt) {
246     xbt_log_layout_set(category, xbt_log_layout_format_new(setting->fmt));
247
248     XBT_DEBUG("Apply settings for category '%s': set format to %s", category->name, setting->fmt);
249   }
250
251   if (setting->additivity != -1) {
252     xbt_log_additivity_set(category, setting->additivity);
253
254     XBT_DEBUG("Apply settings for category '%s': set additivity to %s",
255            category->name, (setting->additivity ? "on" : "off"));
256   }
257   if (setting->appender) {
258     xbt_log_appender_set(category, setting->appender);
259     if (!category->layout)
260       xbt_log_layout_set(category, xbt_log_layout_simple_new(NULL));
261     category->additivity = 0;
262     XBT_DEBUG("Set %p as appender of category '%s'", setting->appender, category->name);
263   }
264 }
265
266 /*
267  * This gets called the first time a category is referenced and performs the initialization.
268  * Also resets threshold to inherited!
269  */
270 int _xbt_log_cat_init(xbt_log_category_t category, e_xbt_log_priority_t priority)
271 {
272   DISABLE_XBT_LOG_CAT_INIT();
273   if (log_cat_init_mutex != NULL)
274     xbt_os_mutex_acquire(log_cat_init_mutex);
275
276   if (category->initialized) {
277     if (log_cat_init_mutex != NULL)
278       xbt_os_mutex_release(log_cat_init_mutex);
279     return priority >= category->threshold;
280   }
281
282   unsigned int cursor;
283   xbt_log_setting_t setting = NULL;
284
285   XBT_DEBUG("Initializing category '%s' (firstChild=%s, nextSibling=%s)", category->name,
286          (category->firstChild ? category->firstChild->name : "none"),
287          (category->nextSibling ? category->nextSibling->name : "none"));
288
289   if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) {
290     category->threshold = xbt_log_priority_info;
291     category->appender = xbt_log_default_appender;
292     category->layout = xbt_log_default_layout;
293   } else {
294     if (!category->parent)
295       category->parent = &_XBT_LOGV(XBT_LOG_ROOT_CAT);
296
297     XBT_DEBUG("Set %s (%s) as father of %s ", category->parent->name,
298            (category->parent->initialized ? xbt_log_priority_names[category->parent->threshold] : "uninited"),
299            category->name);
300     xbt_log_parent_set(category, category->parent);
301
302     if (XBT_LOG_ISENABLED(log, xbt_log_priority_debug)) {
303       char *buf;
304       char *res = NULL;
305       xbt_log_category_t cpp = category->parent->firstChild;
306       while (cpp) {
307         if (res) {
308           buf = bprintf("%s %s", res, cpp->name);
309           free(res);
310           res = buf;
311         } else {
312           res = xbt_strdup(cpp->name);
313         }
314         cpp = cpp->nextSibling;
315       }
316
317       XBT_DEBUG("Children of %s: %s; nextSibling: %s", category->parent->name, res,
318              (category->parent->nextSibling ? category->parent->nextSibling->name : "none"));
319
320       free(res);
321     }
322   }
323
324   /* Apply the control */
325   if (xbt_log_settings) {
326     xbt_assert(category, "NULL category");
327     xbt_assert(category->name);
328     int found = 0;
329
330     xbt_dynar_foreach(xbt_log_settings, cursor, setting) {
331       xbt_assert(setting, "Damnit, NULL cat in the list");
332       xbt_assert(setting->catname, "NULL setting(=%p)->catname", (void *) setting);
333
334       if (!strcmp(setting->catname, category->name)) {
335         found = 1;
336         _xbt_log_cat_apply_set(category, setting);
337         xbt_dynar_cursor_rm(xbt_log_settings, &cursor);
338       }
339     }
340
341     if (!found)
342       XBT_DEBUG("Category '%s': inherited threshold = %s (=%d)",
343                 category->name, xbt_log_priority_names[category->threshold], category->threshold);
344   }
345
346   category->initialized = 1;
347   if (log_cat_init_mutex != NULL)
348     xbt_os_mutex_release(log_cat_init_mutex);
349   return priority >= category->threshold;
350 }
351
352 void xbt_log_parent_set(xbt_log_category_t cat, xbt_log_category_t parent)
353 {
354   xbt_assert(cat, "NULL category to be given a parent");
355   xbt_assert(parent, "The parent category of %s is NULL", cat->name);
356
357   /* if the category is initialized, unlink from current parent */
358   if (cat->initialized) {
359     xbt_log_category_t *cpp = &cat->parent->firstChild;
360
361     while (*cpp != cat && *cpp != NULL) {
362       cpp = &(*cpp)->nextSibling;
363     }
364
365     xbt_assert(*cpp == cat);
366     *cpp = cat->nextSibling;
367   }
368
369   cat->parent = parent;
370   cat->nextSibling = parent->firstChild;
371
372   parent->firstChild = cat;
373
374   if (!parent->initialized)
375     _xbt_log_cat_init(parent, xbt_log_priority_uninitialized /* ignored */ );
376
377   cat->threshold = parent->threshold;
378
379   cat->isThreshInherited = 1;
380 }
381
382 static void _set_inherited_thresholds(xbt_log_category_t cat)
383 {
384   xbt_log_category_t child = cat->firstChild;
385
386   for (; child != NULL; child = child->nextSibling) {
387     if (child->isThreshInherited) {
388       if (cat != &_XBT_LOGV(log))
389         XBT_VERB("Set category threshold of %s to %s (=%d)",
390               child->name, xbt_log_priority_names[cat->threshold], cat->threshold);
391       child->threshold = cat->threshold;
392       _set_inherited_thresholds(child);
393     }
394   }
395 }
396
397 void xbt_log_threshold_set(xbt_log_category_t cat, e_xbt_log_priority_t threshold)
398 {
399   cat->threshold = threshold;
400   cat->isThreshInherited = 0;
401
402   _set_inherited_thresholds(cat);
403 }
404
405 static xbt_log_setting_t _xbt_log_parse_setting(const char *control_string)
406 {
407   const char *orig_control_string = control_string;
408   xbt_log_setting_t set = xbt_new(s_xbt_log_setting_t, 1);
409
410   set->catname = NULL;
411   set->thresh = xbt_log_priority_uninitialized;
412   set->fmt = NULL;
413   set->additivity = -1;
414   set->appender = NULL;
415
416   if (!*control_string)
417     return set;
418   XBT_DEBUG("Parse log setting '%s'", control_string);
419
420   control_string += strspn(control_string, " ");
421   const char *name = control_string;
422   control_string += strcspn(control_string, ".:= ");
423   const char *dot = control_string;
424   control_string += strcspn(control_string, ":= ");
425   const char *eq = control_string;
426
427   xbt_assert(*dot == '.' || (*eq != '=' && *eq != ':'), "Invalid control string '%s'", orig_control_string);
428
429   if (!strncmp(dot + 1, "threshold", (size_t) (eq - dot - 1))) {
430     int i;
431     char *neweq = xbt_strdup(eq + 1);
432     char *p = neweq - 1;
433
434     while (*(++p) != '\0') {
435       if (*p >= 'a' && *p <= 'z') {
436         *p -= 'a' - 'A';
437       }
438     }
439
440     XBT_DEBUG("New priority name = %s", neweq);
441     for (i = 0; i < xbt_log_priority_infinite; i++) {
442       if (!strncmp(xbt_log_priority_names[i], neweq, p - eq)) {
443         XBT_DEBUG("This is priority %d", i);
444         break;
445       }
446     }
447
448     if(i<XBT_LOG_STATIC_THRESHOLD){
449      fprintf(stderr,
450          "Priority '%s' (in setting '%s') is above allowed priority '%s'.\n\n"
451          "Compiling SimGrid with -DNDEBUG forbids the levels 'trace' and 'debug'\n"
452          "while -DNLOG forbids any logging, at any level.",
453              eq + 1, name, xbt_log_priority_names[XBT_LOG_STATIC_THRESHOLD]);
454      exit(1);
455     }else if (i < xbt_log_priority_infinite) {
456       set->thresh = (e_xbt_log_priority_t) i;
457     } else {
458       THROWF(arg_error, 0,
459              "Unknown priority name: %s (must be one of: trace,debug,verbose,info,warning,error,critical)", eq + 1);
460     }
461     free(neweq);
462   } else if (!strncmp(dot + 1, "add", (size_t) (eq - dot - 1)) ||
463              !strncmp(dot + 1, "additivity", (size_t) (eq - dot - 1))) {
464     char *neweq = xbt_strdup(eq + 1);
465     char *p = neweq - 1;
466
467     while (*(++p) != '\0') {
468       if (*p >= 'a' && *p <= 'z') {
469         *p -= 'a' - 'A';
470       }
471     }
472     if (!strcmp(neweq, "ON") || !strcmp(neweq, "YES") || !strcmp(neweq, "1")) {
473       set->additivity = 1;
474     } else {
475       set->additivity = 0;
476     }
477     free(neweq);
478   } else if (!strncmp(dot + 1, "app", (size_t) (eq - dot - 1)) ||
479              !strncmp(dot + 1, "appender", (size_t) (eq - dot - 1))) {
480     char *neweq = xbt_strdup(eq + 1);
481
482     if (!strncmp(neweq, "file:", 5)) {
483       set->appender = xbt_log_appender_file_new(neweq + 5);
484     }else if (!strncmp(neweq, "rollfile:", 9)) {
485       set->appender = xbt_log_appender2_file_new(neweq + 9,1);
486     }else if (!strncmp(neweq, "splitfile:", 10)) {
487       set->appender = xbt_log_appender2_file_new(neweq + 10,0);
488     } else {
489       THROWF(arg_error, 0, "Unknown appender log type: '%s'", neweq);
490     }
491     free(neweq);
492   } else if (!strncmp(dot + 1, "fmt", (size_t) (eq - dot - 1))) {
493     set->fmt = xbt_strdup(eq + 1);
494   } else {
495     char buff[512];
496     snprintf(buff, MIN(512, eq - dot), "%s", dot + 1);
497     xbt_die("Unknown setting of the log category: '%s'", buff);
498   }
499   set->catname = (char *) xbt_malloc(dot - name + 1);
500
501   memcpy(set->catname, name, dot - name);
502   set->catname[dot - name] = '\0';      /* Just in case */
503   XBT_DEBUG("This is for cat '%s'", set->catname);
504
505   return set;
506 }
507
508 static xbt_log_category_t _xbt_log_cat_searchsub(xbt_log_category_t cat, char *name)
509 {
510   xbt_log_category_t child;
511   xbt_log_category_t res;
512
513   XBT_DEBUG("Search '%s' into '%s' (firstChild='%s'; nextSibling='%s')", name,
514          cat->name, (cat->firstChild ? cat->firstChild->name : "none"),
515          (cat->nextSibling ? cat->nextSibling->name : "none"));
516   if (!strcmp(cat->name, name))
517     return cat;
518
519   for (child = cat->firstChild; child != NULL; child = child->nextSibling) {
520     XBT_DEBUG("Dig into %s", child->name);
521     res = _xbt_log_cat_searchsub(child, name);
522     if (res)
523       return res;
524   }
525
526   return NULL;
527 }
528
529 /**
530  * \ingroup XBT_log
531  * \param control_string What to parse
532  *
533  * Typically passed a command-line argument. The string has the syntax:
534  *
535  *      ( [category] "." [keyword] ":" value (" ")... )...
536  *
537  * where [category] is one the category names (see \ref XBT_log_cats for a complete list of the ones defined in the
538  * SimGrid library) and keyword is one of the following:
539  *
540  *    - thres: category's threshold priority. Possible values:
541  *             TRACE,DEBUG,VERBOSE,INFO,WARNING,ERROR,CRITICAL
542  *    - add or additivity: whether the logging actions must be passed to the parent category.
543  *      Possible values: 0, 1, no, yes, on, off.
544  *      Default value: yes.
545  *    - fmt: the format to use. See \ref log_use_conf_fmt for more information.
546  *    - app or appender: the appender to use. See \ref log_use_conf_app for more information.
547  */
548 void xbt_log_control_set(const char *control_string)
549 {
550   xbt_log_setting_t set;
551
552   /* To split the string in commands, and the cursors */
553   xbt_dynar_t set_strings;
554   char *str;
555   unsigned int cpt;
556
557   if (!control_string)
558     return;
559   XBT_DEBUG("Parse log settings '%s'", control_string);
560
561   /* Special handling of no_loc request, which asks for any file localization to be omitted (for tesh runs) */
562   if (!strcmp(control_string, "no_loc")) {
563     xbt_log_no_loc = 1;
564     return;
565   }
566   /* some initialization if this is the first time that this get called */
567   if (xbt_log_settings == NULL)
568     xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t), _free_setting);
569
570   /* split the string, and remove empty entries */
571   set_strings = xbt_str_split_quoted(control_string);
572
573   if (xbt_dynar_is_empty(set_strings)) {     /* vicious user! */
574     xbt_dynar_free(&set_strings);
575     return;
576   }
577
578   /* Parse each entry and either use it right now (if the category was already created), or store it for further use */
579   xbt_dynar_foreach(set_strings, cpt, str) {
580     set = _xbt_log_parse_setting(str);
581     xbt_log_category_t cat = _xbt_log_cat_searchsub(&_XBT_LOGV(XBT_LOG_ROOT_CAT), set->catname);
582
583     if (cat) {
584       XBT_DEBUG("Apply directly");
585       _xbt_log_cat_apply_set(cat, set);
586       _free_setting((void *) &set);
587     } else {
588       XBT_DEBUG("Store for further application");
589       XBT_DEBUG("push %p to the settings", (void *) set);
590       xbt_dynar_push(xbt_log_settings, &set);
591     }
592   }
593   xbt_dynar_free(&set_strings);
594 }
595
596 void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app)
597 {
598   if (cat->appender) {
599     if (cat->appender->free_)
600       cat->appender->free_(cat->appender);
601     free(cat->appender);
602   }
603   cat->appender = app;
604 }
605
606 void xbt_log_layout_set(xbt_log_category_t cat, xbt_log_layout_t lay)
607 {
608   DISABLE_XBT_LOG_CAT_INIT();
609   if (!cat->appender) {
610     XBT_VERB ("No appender to category %s. Setting the file appender as default", cat->name);
611     xbt_log_appender_set(cat, xbt_log_appender_file_new(NULL));
612   }
613   if (cat->layout) {
614     if (cat->layout->free_) {
615       cat->layout->free_(cat->layout);
616     }
617     free(cat->layout);
618   }
619   cat->layout = lay;
620   xbt_log_additivity_set(cat, 0);
621 }
622
623 void xbt_log_additivity_set(xbt_log_category_t cat, int additivity)
624 {
625   cat->additivity = additivity;
626 }
627
628 static void xbt_log_help(void)
629 {
630   printf("Description of the logging output:\n"
631          "\n"
632          "   Threshold configuration: --log=CATEGORY_NAME.thres:PRIORITY_LEVEL\n"
633          "      CATEGORY_NAME: defined in code with function 'XBT_LOG_NEW_CATEGORY'\n"
634          "      PRIORITY_LEVEL: the level to print (trace,debug,verbose,info,warning,error,critical)\n"
635          "         -> trace: enter and return of some functions\n"
636          "         -> debug: crufty output\n"
637          "         -> verbose: verbose output for the user wanting more\n"
638          "         -> info: output about the regular functioning\n"
639          "         -> warning: minor issue encountered\n"
640          "         -> error: issue encountered\n"
641          "         -> critical: major issue encountered\n"
642          "      The default priority level is 'info'.\n"
643          "\n"
644          "   Format configuration: --log=CATEGORY_NAME.fmt:FORMAT\n"
645          "      FORMAT string may contain:\n"
646          "         -> %%%%: the %% char\n"
647          "         -> %%n: platform-dependent line separator (LOG4J compatible)\n"
648          "         -> %%e: plain old space (SimGrid extension)\n"
649          "\n"
650          "         -> %%m: user-provided message\n"
651          "\n"
652          "         -> %%c: Category name (LOG4J compatible)\n"
653          "         -> %%p: Priority name (LOG4J compatible)\n"
654          "\n"
655          "         -> %%h: Hostname (SimGrid extension)\n"
656          "         -> %%P: Process name (SimGrid extension)\n"
657          "         -> %%t: Thread \"name\" (LOG4J compatible -- actually the address of the thread in memory)\n"
658          "         -> %%i: Process PID (SimGrid extension -- this is a 'i' as in 'i'dea)\n"
659          "\n"
660          "         -> %%F: file name where the log event was raised (LOG4J compatible)\n"
661          "         -> %%l: location where the log event was raised (LOG4J compatible, like '%%F:%%L' -- this is a l as "
662          "in 'l'etter)\n"
663          "         -> %%L: line number where the log event was raised (LOG4J compatible)\n"
664          "         -> %%M: function name (LOG4J compatible -- called method name here of course).\n"
665          "                 Defined only when using gcc because there is no __func__ elsewhere.\n"
666          "\n"
667          "         -> %%b: full backtrace (Called %%throwable in LOG4J). Defined only under windows or when using the "
668          "GNU libc because\n"
669          "                 backtrace() is not defined elsewhere, and we only have a fallback for windows boxes, not "
670          "mac ones for example.\n"
671          "         -> %%B: short backtrace (only the first line of the %%b). Called %%throwable{short} in LOG4J; "
672          "defined where %%b is.\n"
673          "\n"
674          "         -> %%d: date (UNIX-like epoch)\n"
675          "         -> %%r: application age (time elapsed since the beginning of the application)\n"
676          "\n"
677          "   Miscellaneous:\n"
678          "      --help-log-categories    Display the current hierarchy of log categories.\n"
679          "      --log=no_loc             Don't print file names in messages (for tesh tests).\n"
680          "\n");
681 }
682
683 static int xbt_log_cat_cmp(const void *pa, const void *pb)
684 {
685   xbt_log_category_t a = *(xbt_log_category_t *)pa;
686   xbt_log_category_t b = *(xbt_log_category_t *)pb;
687   return strcmp(a->name, b->name);
688 }
689
690 static void xbt_log_help_categories_rec(xbt_log_category_t category, const char *prefix)
691 {
692   char *this_prefix;
693   char *child_prefix;
694   unsigned i;
695   xbt_log_category_t cat;
696
697   if (!category)
698     return;
699
700   if (category->parent) {
701     this_prefix = bprintf("%s \\_ ", prefix);
702     child_prefix = bprintf("%s |  ", prefix);
703   } else {
704     this_prefix = xbt_strdup(prefix);
705     child_prefix = xbt_strdup(prefix);
706   }
707
708   xbt_dynar_t dynar = xbt_dynar_new(sizeof(xbt_log_category_t), NULL);
709   for (cat = category ; cat != NULL; cat = cat->nextSibling)
710     xbt_dynar_push_as(dynar, xbt_log_category_t, cat);
711
712   xbt_dynar_sort(dynar, xbt_log_cat_cmp);
713
714   xbt_dynar_foreach(dynar, i, cat){
715     if (i == xbt_dynar_length(dynar) - 1 && category->parent)
716       *strrchr(child_prefix, '|') = ' ';
717     printf("%s%s: %s\n", this_prefix, cat->name, cat->description);
718     xbt_log_help_categories_rec(cat->firstChild, child_prefix);
719   }
720
721   xbt_dynar_free(&dynar);
722   xbt_free(this_prefix);
723   xbt_free(child_prefix);
724 }
725
726 static void xbt_log_help_categories(void)
727 {
728   printf("Current log category hierarchy:\n");
729   xbt_log_help_categories_rec(&_XBT_LOGV(XBT_LOG_ROOT_CAT), "   ");
730   printf("\n");
731 }