Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Implement the field width in log formats
[simgrid.git] / src / xbt / xbt_log_layout_format.c
1 /* layout_simple - a dumb log layout                                        */
2
3 /* Copyright (c) 2007, 2008, 2009, 2010. 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 "portable.h"           /* execinfo when available */
10 #include "xbt/sysdep.h"
11 #include "xbt/strbuff.h"
12 #include "xbt/log_private.h"
13 #include "gras/virtu.h"         /* gras_os_myname (KILLME) */
14 #include "xbt/synchro.h"        /* xbt_thread_self_name */
15 #include <stdio.h>
16
17 extern const char *xbt_log_priority_names[8];
18
19 static double format_begin_of_time = -1;
20
21 #define append(data,letter) \
22   do { \
23     if (precision == -1 && length == -1) { \
24       tmp = bprintf("%" letter, data); \
25     } else if (precision == -1) { \
26       sprintf(tmpfmt,"%% %d" letter,length); \
27       tmp = bprintf(tmpfmt, data); \
28       length = -1; \
29     } else if (length == -1) { \
30       tmp = bprintf("%.*" letter, precision, data);\
31       precision = -1; \
32     } else { \
33       sprintf(tmpfmt,"%% %d.*" letter,length); \
34       tmp = bprintf(tmpfmt, precision, data); \
35       length = precision = -1; \
36     } \
37     xbt_strbuff_append(buff,tmp);\
38     free(tmp); \
39   } while (0)
40
41 #define append_string(data) append(data, "s")
42 #define append_int(data)    append(data, "d")
43 #define append_double(data) append(data, "f")
44
45 #define append2(fmt,elm,elm2)                                         \
46   do {                                                               \
47     xbt_strbuff_append(buff, tmp=bprintf(fmt,elm,elm2));          \
48     free(tmp);                                                    \
49     precision = -1;                                               \
50   } while (0)
51
52 #define ERRMSG "Unknown %%%c sequence in layout format (%s).\nKnown sequences:\n"                       \
53   "  what:        %%m: user message  %%c: log category  %%p: log priority\n"                      \
54   "  where:\n"                                                                                    \
55   "    source:    %%F: file          %%L: line          %%M: function  %%l: location (%%F:%%L)\n" \
56   "    runtime:   %%h: hostname      %%t: thread        %%P: process   %%i: PID\n"                \
57   "    backtrace: %%b: full          %%B: short\n"                                                \
58   "  when:        %%d: date          %%r: app. age\n"                                             \
59   "  other:       %%%%: %%             %%n: new line      %%e: plain space\n"
60
61
62 static void xbt_log_layout_format_dynamic(xbt_log_layout_t l,
63                                           xbt_log_event_t ev,
64                                           const char *fmt,
65                                           xbt_log_appender_t app)
66 {
67   xbt_strbuff_t buff = xbt_strbuff_new();
68   char tmpfmt[50];
69   int precision = -1;
70   int length = -1;
71   char *q = l->data;
72   char *tmp;
73   char *tmp2;
74
75   while (*q != '\0') {
76     if (*q == '%') {
77       q++;
78     handle_modifier:
79       switch (*q) {
80       case '\0':
81         fprintf(stderr, "Layout format (%s) ending with %%\n",
82                 (char *) l->data);
83         abort();
84       case '%':
85         xbt_strbuff_append(buff, "%");
86         break;
87       case 'n':                /* platform-dependant line separator (LOG4J compliant) */
88         xbt_strbuff_append(buff, "\n");
89         break;
90       case 'e':                /* plain space (SimGrid extension) */
91         xbt_strbuff_append(buff, " ");
92         break;
93
94       case '.':                /* precision specifyier */
95         q++;
96         sscanf(q, "%d", &precision);
97         q += (precision>9?2:1);
98         goto handle_modifier;
99
100       case '0':
101       case '1':
102       case '2':
103       case '3':
104       case '4':
105       case '5':
106       case '6':
107       case '7':
108       case '8':
109       case '9': /* length modifier */
110         sscanf(q, "%d", &length);
111         q += (length>9?2:1);
112         goto handle_modifier;
113
114       case 'c':                /* category name; LOG4J compliant
115                                    should accept a precision postfix to show the hierarchy */
116         append_string(ev->cat->name);
117         break;
118       case 'p':                /* priority name; LOG4J compliant */
119         append_string(xbt_log_priority_names[ev->priority]);
120         break;
121
122       case 'h':                /* host name; SimGrid extension */
123         append_string(gras_os_myname());
124         break;
125       case 't':                /* thread name; LOG4J compliant */
126         append_string(xbt_thread_self_name());
127         break;
128       case 'P':                /* process name; SimGrid extension */
129         append_string(xbt_procname());
130         break;
131       case 'i':                /* process PID name; SimGrid extension */
132         append_int((*xbt_getpid) ());
133         break;
134
135       case 'F':                /* file name; LOG4J compliant */
136         append_string(ev->fileName);
137         break;
138       case 'l':                /* location; LOG4J compliant */
139         append2("%s:%d", ev->fileName, ev->lineNum);
140         precision = -1;         /* Ignored */
141         break;
142       case 'L':                /* line number; LOG4J compliant */
143         append_int(ev->lineNum);
144         break;
145       case 'M':                /* method (ie, function) name; LOG4J compliant */
146         append_string(ev->functionName);
147         break;
148       case 'b':                /* backtrace; called %throwable in LOG4J */
149       case 'B':                /* short backtrace; called %throwable{short} in LOG4J */
150 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
151         {
152           xbt_ex_t e;
153           int i;
154
155           e.used = backtrace((void **) e.bt, XBT_BACKTRACE_SIZE);
156           e.bt_strings = NULL;
157           e.msg = NULL;
158           e.remote = 0;
159           xbt_backtrace_current(&e);
160           if (*q == 'B') {
161             append_string(e.bt_strings[2] + 8);
162           } else {
163             for (i = 2; i < e.used; i++) {
164               append_string(e.bt_strings[i] + 8);
165               xbt_strbuff_append(buff, "\n");
166             }
167           }
168
169           xbt_ex_free(e);
170         }
171 #else
172         append_string("(no backtrace on this arch)");
173 #endif
174         break;
175
176       case 'd':                /* date; LOG4J compliant */
177         append_double(gras_os_time());
178         break;
179       case 'r':                /* application age; LOG4J compliant */
180         append_double(gras_os_time() - format_begin_of_time);
181         break;
182
183       case 'm':                /* user-provided message; LOG4J compliant */
184         tmp2 = bvprintf(fmt, ev->ap_copy);
185         append_string(tmp2);
186         free(tmp2);
187         break;
188
189       default:
190         fprintf(stderr, ERRMSG, *q, (char *) l->data);
191         abort();
192       }
193       q++;
194     } else {
195       char tmp2[2];
196       tmp2[0] = *(q++);
197       tmp2[1] = '\0';
198       xbt_strbuff_append(buff, tmp2);
199     }
200   }
201   app->do_append(app, buff->data);
202   xbt_strbuff_free(buff);
203 }
204
205 #undef check_overflow
206 #define check_overflow \
207   if (p-ev->buffer > XBT_LOG_BUFF_SIZE) { /* buffer overflow */ \
208   xbt_log_layout_format_dynamic(l,ev,msg_fmt,app); \
209   return;\
210   }
211
212 #define show_it(data,letter) \
213   do { \
214     if (precision == -1 && length == -1) { \
215       p += snprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer), "%" letter, data); \
216     } else if (precision == -1) { \
217       sprintf(tmpfmt,"%% %d" letter,length); \
218       p += snprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer), tmpfmt, data); \
219       length = -1; \
220     } else if (length == -1) { \
221       p += sprintf(p, "%.*" letter, \
222                    (int) MIN(XBT_LOG_BUFF_SIZE - (p - ev->buffer), precision), \
223                    data);\
224       precision = -1; \
225     } else { \
226       sprintf(tmpfmt,"%% %d.%d" letter,length, \
227           (int) MIN(XBT_LOG_BUFF_SIZE - (p - ev->buffer), precision));\
228       p += sprintf(p, tmpfmt, data);\
229       length = precision = -1; \
230     } \
231     check_overflow; \
232   } while (0)
233
234 #define show_string(data) show_it(data, "s")
235 #define show_int(data)    show_it(data, "d")
236 #define show_double(data) show_it(data, "f")
237
238 static void xbt_log_layout_format_doit(xbt_log_layout_t l,
239                                        xbt_log_event_t ev,
240                                        const char *msg_fmt,
241                                        xbt_log_appender_t app)
242 {
243   char *p, *q;
244   char tmpfmt[50];
245   int precision = -1;
246   int length = -1;
247
248
249   p = ev->buffer;
250   q = l->data;
251
252   while (*q != '\0') {
253     if (*q == '%') {
254       q++;
255     handle_modifier:
256       switch (*q) {
257       case '\0':
258         fprintf(stderr, "Layout format (%s) ending with %%\n",
259                 (char *) l->data);
260         abort();
261       case '%':
262         *p++ = '%';
263         break;
264       case 'n':                /* platform-dependant line separator (LOG4J compliant) */
265         p += snprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer), "\n");
266         check_overflow;
267         break;
268       case 'e':                /* plain space (SimGrid extension) */
269         p += snprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer), " ");
270         check_overflow;
271         break;
272
273       case '.':                /* precision specifyier */
274         q++;
275         sscanf(q, "%d", &precision);
276         q += (precision>9?2:1);
277         goto handle_modifier;
278
279       case '0':
280       case '1':
281       case '2':
282       case '3':
283       case '4':
284       case '5':
285       case '6':
286       case '7':
287       case '8':
288       case '9': /* length modifier */
289         sscanf(q, "%d", &length);
290         q += (length>9?2:1);
291         goto handle_modifier;
292
293       case 'c':                /* category name; LOG4J compliant
294                                    should accept a precision postfix to show the hierarchy */
295         show_string(ev->cat->name);
296         break;
297       case 'p':                /* priority name; LOG4J compliant */
298         show_string(xbt_log_priority_names[ev->priority]);
299         break;
300
301       case 'h':                /* host name; SimGrid extension */
302         show_string(gras_os_myname());
303         break;
304       case 't':                /* thread name; LOG4J compliant */
305         show_string(xbt_thread_self_name());
306         break;
307       case 'P':                /* process name; SimGrid extension */
308         show_string(xbt_procname());
309         break;
310       case 'i':                /* process PID name; SimGrid extension */
311         show_int((*xbt_getpid) ());
312         break;
313
314       case 'F':                /* file name; LOG4J compliant */
315         show_string(ev->fileName);
316         break;
317       case 'l':                /* location; LOG4J compliant */
318         if (precision == -1) {
319           p += snprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer), "%s:%d",
320                         ev->fileName, ev->lineNum);
321           check_overflow;
322         } else {
323           p += snprintf(p,
324                         (int) MIN(XBT_LOG_BUFF_SIZE - (p - ev->buffer),
325                                   precision), "%s:%d", ev->fileName,
326                         ev->lineNum);
327           check_overflow;
328           precision = -1;
329         }
330         break;
331       case 'L':                /* line number; LOG4J compliant */
332         show_int(ev->lineNum);
333         break;
334       case 'M':                /* method (ie, function) name; LOG4J compliant */
335         show_string(ev->functionName);
336         break;
337       case 'b':                /* backtrace; called %throwable in LOG4J */
338       case 'B':                /* short backtrace; called %throwable{short} in LOG4J */
339 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
340         {
341           xbt_ex_t e;
342           int i;
343
344           e.used = backtrace((void **) e.bt, XBT_BACKTRACE_SIZE);
345           e.bt_strings = NULL;
346           e.msg = NULL;
347           e.remote = 0;
348           xbt_backtrace_current(&e);
349           if (*q == 'B') {
350             show_string(e.bt_strings[2] + 8);
351           } else {
352             for (i = 2; i < e.used; i++)
353               if (precision == -1) {
354                 p += snprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer),
355                               "%s\n", e.bt_strings[i] + 8);
356                 check_overflow;
357               } else {
358                 p += sprintf(p, "%.*s\n",
359                              (int) MIN(XBT_LOG_BUFF_SIZE -
360                                        (p - ev->buffer), precision),
361                              e.bt_strings[i] + 8);
362                 check_overflow;
363                 precision = -1;
364               }
365           }
366
367           xbt_ex_free(e);
368         }
369 #else
370         p += snprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer),
371                       "(no backtrace on this arch)");
372         check_overflow;
373 #endif
374         break;
375
376       case 'd':                /* date; LOG4J compliant */
377         show_double(gras_os_time());
378         break;
379       case 'r':                /* application age; LOG4J compliant */
380         show_double(gras_os_time() - format_begin_of_time);
381         break;
382
383       case 'm':                /* user-provided message; LOG4J compliant */
384         if (precision == -1) {
385           p += vsnprintf(p, XBT_LOG_BUFF_SIZE - (p - ev->buffer), msg_fmt,
386                          ev->ap);
387           check_overflow;
388         } else {
389           p += vsnprintf(p,
390                          (int) MIN(XBT_LOG_BUFF_SIZE - (p - ev->buffer),
391                                    precision), msg_fmt, ev->ap);
392           check_overflow;
393           precision = -1;
394         }
395         break;
396
397       default:
398         fprintf(stderr, ERRMSG, *q, (char *) l->data);
399         abort();
400       }
401       q++;
402     } else {
403       *(p++) = *(q++);
404       check_overflow;
405     }
406   }
407   *p = '\0';
408   app->do_append(app, ev->buffer);
409 }
410
411 static void xbt_log_layout_format_free(xbt_log_layout_t lay)
412 {
413   free(lay->data);
414 }
415
416 xbt_log_layout_t xbt_log_layout_format_new(char *arg)
417 {
418   xbt_log_layout_t res = xbt_new0(s_xbt_log_layout_t, 1);
419   res->do_layout = xbt_log_layout_format_doit;
420   res->free_ = xbt_log_layout_format_free;
421   res->data = xbt_strdup((char *) arg);
422
423   if (format_begin_of_time < 0)
424     format_begin_of_time = gras_os_time();
425
426   return res;
427 }