10 #include "mono-compiler.h"
11 #include "mono-logger-internals.h"
12 #include <mono/utils/mono-threads-debug.h>
20 GLogLevelFlags mono_internal_current_level
= (GLogLevelFlags
)INT_MAX
;
21 MonoTraceMask mono_internal_current_mask
= (MonoTraceMask
)~0;
22 gboolean mono_trace_log_header
= FALSE
;
24 static GQueue
*level_stack
= NULL
;
25 static const char *mono_log_domain
= "Mono";
26 static MonoPrintCallback print_callback
, printerr_callback
;
28 static MonoLogCallParm logCallback
;
31 MonoLogCallback legacy_callback
;
33 } UserSuppliedLoggerUserData
;
38 * Initializes the mono tracer.
41 mono_trace_init (void)
43 if(level_stack
== NULL
) {
44 mono_internal_current_level
= G_LOG_LEVEL_ERROR
;
45 level_stack
= g_queue_new();
47 char *mask
= g_getenv ("MONO_LOG_MASK");
48 char *level
= g_getenv ("MONO_LOG_LEVEL");
49 char *header
= g_getenv ("MONO_LOG_HEADER");
50 char *dest
= g_getenv ("MONO_LOG_DEST");
52 mono_trace_set_mask_string(mask
);
53 mono_trace_set_level_string(level
);
54 mono_trace_set_logheader_string(header
);
55 mono_trace_set_logdest_string(dest
);
67 * Releases the mono tracer.
70 mono_trace_cleanup (void)
72 if(level_stack
!= NULL
) {
73 while(!g_queue_is_empty (level_stack
)) {
74 g_free (g_queue_pop_head (level_stack
));
78 g_queue_free (level_stack
);
85 * \param level Verbose level of the specified message
86 * \param mask Type of the specified message
87 * Traces a new message, depending on the current logging level
91 mono_tracev_inner (GLogLevelFlags level
, MonoTraceMask mask
, const char *format
, va_list args
)
94 if (level_stack
== NULL
) {
96 if(level
> mono_internal_current_level
|| !(mask
& mono_internal_current_mask
))
100 g_assert (logCallback
.opener
); // mono_trace_init should have provided us with one!
102 if (g_vasprintf (&log_message
, format
, args
) < 0)
104 logCallback
.writer (mono_log_domain
, level
, logCallback
.header
, log_message
);
105 g_free (log_message
);
109 * mono_trace_set_level:
110 * \param level Verbose level to set
111 * Sets the current logging level. Every subsequent call to
112 * \c mono_trace will check the visibility of a message against this
116 mono_trace_set_level (GLogLevelFlags level
)
118 if(level_stack
== NULL
)
121 mono_internal_current_level
= level
;
125 * mono_trace_set_mask:
126 * \param mask Mask of visible message types.
127 * Sets the current logging level. Every subsequent call to
128 * \c mono_trace will check the visibility of a message against this
132 mono_trace_set_mask (MonoTraceMask mask
)
134 if(level_stack
== NULL
)
137 mono_internal_current_mask
= mask
;
141 * mono_trace_set_logdest:
142 * \param dest Destination for logging
143 * Sets the current logging destination. This can be a file or, if supported,
147 mono_trace_set_logdest_string (const char *dest
)
149 MonoLogCallParm logger
;
151 if(level_stack
== NULL
)
155 logger
.opener
= mono_log_open_logcat
;
156 logger
.writer
= mono_log_write_logcat
;
157 logger
.closer
= mono_log_close_logcat
;
158 logger
.dest
= (char*) dest
;
159 #elif defined (HOST_IOS)
160 logger
.opener
= mono_log_open_asl
;
161 logger
.writer
= mono_log_write_asl
;
162 logger
.closer
= mono_log_close_asl
;
163 logger
.dest
= (char*) dest
;
165 if (dest
&& !strcmp("flight-recorder", dest
)) {
166 logger
.opener
= mono_log_open_recorder
;
167 logger
.writer
= mono_log_write_recorder
;
168 logger
.closer
= mono_log_close_recorder
;
169 logger
.dest
= (char *) dest
;
171 // Increase log level with flight recorder
172 if (mono_internal_current_level
== G_LOG_LEVEL_ERROR
|| mono_internal_current_level
== G_LOG_LEVEL_CRITICAL
)
173 mono_trace_set_level (G_LOG_LEVEL_WARNING
);
175 } else if (dest
&& !strcmp("syslog", dest
)) {
176 logger
.opener
= mono_log_open_syslog
;
177 logger
.writer
= mono_log_write_syslog
;
178 logger
.closer
= mono_log_close_syslog
;
179 logger
.dest
= (char *) dest
;
181 logger
.opener
= mono_log_open_logfile
;
182 logger
.writer
= mono_log_write_logfile
;
183 logger
.closer
= mono_log_close_logfile
;
184 logger
.dest
= (char *) dest
;
188 mono_trace_set_log_handler_internal(&logger
, NULL
);
192 * mono_trace_set_logheader:
193 * \param head Whether we want pid/date/time header on log messages
194 * Sets the current logging header option.
197 mono_trace_set_logheader_string(const char *head
)
200 mono_trace_log_header
= FALSE
;
202 mono_trace_log_header
= TRUE
;
208 * \param level Verbose level to set
209 * \param mask Mask of visible message types.
210 * Saves the current values of level and mask then calls \c mono_trace_set
211 * with the specified new values.
214 mono_trace_push (GLogLevelFlags level
, MonoTraceMask mask
)
216 if(level_stack
== NULL
)
217 g_error("%s: cannot use mono_trace_push without calling mono_trace_init first.", __func__
);
219 MonoLogLevelEntry
*entry
= (MonoLogLevelEntry
*) g_malloc(sizeof(MonoLogLevelEntry
));
220 entry
->level
= mono_internal_current_level
;
221 entry
->mask
= mono_internal_current_mask
;
223 g_queue_push_head (level_stack
, (gpointer
)entry
);
225 /* Set the new level and mask
227 mono_internal_current_level
= level
;
228 mono_internal_current_mask
= mask
;
235 * Restores level and mask values saved from a previous call to mono_trace_push.
238 mono_trace_pop (void)
240 if(level_stack
== NULL
)
241 g_error("%s: cannot use mono_trace_pop without calling mono_trace_init first.", __func__
);
243 if(!g_queue_is_empty (level_stack
)) {
244 MonoLogLevelEntry
*entry
= (MonoLogLevelEntry
*)g_queue_pop_head (level_stack
);
246 /* Restore previous level and mask
248 mono_internal_current_level
= entry
->level
;
249 mono_internal_current_mask
= entry
->mask
;
258 mono_trace_set_level_string (const char *value
)
261 const char *valid_vals
[] = {"error", "critical", "warning", "message", "info", "debug", NULL
};
262 const GLogLevelFlags valid_ids
[] = {G_LOG_LEVEL_ERROR
, G_LOG_LEVEL_CRITICAL
, G_LOG_LEVEL_WARNING
,
263 G_LOG_LEVEL_MESSAGE
, G_LOG_LEVEL_INFO
, G_LOG_LEVEL_DEBUG
};
268 while(valid_vals
[i
]) {
269 if(!strcmp(valid_vals
[i
], value
)){
270 mono_trace_set_level(valid_ids
[i
]);
277 g_print("Unknown trace loglevel: %s\n", value
);
281 mono_trace_set_mask_string (const char *value
)
287 static const struct { const char * const flag
; const MonoTraceMask mask
; } flag_mask_map
[] = {
288 { "asm", MONO_TRACE_ASSEMBLY
},
289 { "type", MONO_TRACE_TYPE
},
290 { "dll", MONO_TRACE_DLLIMPORT
},
291 { "gc", MONO_TRACE_GC
},
292 { "cfg", MONO_TRACE_CONFIG
},
293 { "aot", MONO_TRACE_AOT
},
294 { "security", MONO_TRACE_SECURITY
},
295 { "threadpool", MONO_TRACE_THREADPOOL
},
296 { "io-threadpool", MONO_TRACE_IO_SELECTOR
},
297 { "io-selector", MONO_TRACE_IO_SELECTOR
},
298 { "io-layer-process", MONO_TRACE_IO_LAYER_PROCESS
},
299 { "io-layer-socket", MONO_TRACE_IO_LAYER_SOCKET
},
300 { "io-layer-file", MONO_TRACE_IO_LAYER_FILE
},
301 { "io-layer-console", MONO_TRACE_IO_LAYER_FILE
},
302 { "io-layer-pipe", MONO_TRACE_IO_LAYER_FILE
},
303 { "io-layer-event", MONO_TRACE_IO_LAYER_EVENT
},
304 { "io-layer-semaphore", MONO_TRACE_IO_LAYER_SEMAPHORE
},
305 { "io-layer-mutex", MONO_TRACE_IO_LAYER_MUTEX
},
306 { "io-layer-handle", MONO_TRACE_IO_LAYER_HANDLE
},
307 { "io-layer", (MonoTraceMask
)(MONO_TRACE_IO_LAYER_PROCESS
308 | MONO_TRACE_IO_LAYER_SOCKET
309 | MONO_TRACE_IO_LAYER_FILE
310 | MONO_TRACE_IO_LAYER_EVENT
311 | MONO_TRACE_IO_LAYER_SEMAPHORE
312 | MONO_TRACE_IO_LAYER_MUTEX
313 | MONO_TRACE_IO_LAYER_HANDLE
) },
314 { "w32handle", MONO_TRACE_IO_LAYER_HANDLE
},
315 { "tailcall", MONO_TRACE_TAILCALL
},
316 { "profiler", MONO_TRACE_PROFILER
},
317 { "tiered", MONO_TRACE_TIERED
},
318 { "all", (MonoTraceMask
)~0 }, // FIXMEcxx there is a better way -- operator overloads of enums
319 { NULL
, (MonoTraceMask
)0 },
332 for (i
= 0; flag_mask_map
[i
].flag
; i
++) {
333 size_t len
= strlen (flag_mask_map
[i
].flag
);
334 if (strncmp (tok
, flag_mask_map
[i
].flag
, len
) == 0 && (tok
[len
] == 0 || tok
[len
] == ',')) {
335 flags
|= flag_mask_map
[i
].mask
;
340 if (!flag_mask_map
[i
].flag
) {
341 g_print("Unknown trace flag: %s\n", tok
);
346 mono_trace_set_mask ((MonoTraceMask
) flags
);
350 * mono_trace_is_traced:
352 * Returns whenever a message with @level and @mask will be printed or not.
355 mono_trace_is_traced (GLogLevelFlags level
, MonoTraceMask mask
)
357 return MONO_TRACE_IS_TRACED (level
, mask
);
362 * @log_level severity level
364 * Convert log level into a string for legacy log handlers
367 log_level_get_name (GLogLevelFlags log_level
)
369 switch (log_level
& G_LOG_LEVEL_MASK
) {
370 case G_LOG_LEVEL_ERROR
: return "error";
371 case G_LOG_LEVEL_CRITICAL
: return "critical";
372 case G_LOG_LEVEL_WARNING
: return "warning";
373 case G_LOG_LEVEL_MESSAGE
: return "message";
374 case G_LOG_LEVEL_INFO
: return "info";
375 case G_LOG_LEVEL_DEBUG
: return "debug";
376 default: return "unknown";
383 * @log_domain Message prefix
384 * @log_level Severity
385 * @message Message to be written
386 * @fatal Fatal flag - write then abort
387 * @user_data Argument passed to @callback
389 * This adapts the old callback writer exposed by MonoCallback to the newer method of
390 * logging. We ignore the header request as legacy handlers never had headers.
393 callback_adapter (const char *domain
, GLogLevelFlags level
, mono_bool fatal
, const char *message
)
395 UserSuppliedLoggerUserData
*ll
= (UserSuppliedLoggerUserData
*)logCallback
.user_data
;
397 ll
->legacy_callback (domain
, log_level_get_name(level
), message
, fatal
, ll
->user_data
);
401 eglib_log_adapter (const gchar
*log_domain
, GLogLevelFlags log_level
, const gchar
*message
, gpointer user_data
)
403 UserSuppliedLoggerUserData
*ll
= (UserSuppliedLoggerUserData
*)logCallback
.user_data
;
405 ll
->legacy_callback (log_domain
, log_level_get_name (log_level
), message
, log_level
& G_LOG_LEVEL_ERROR
, ll
->user_data
);
411 * Dummy routine for older style loggers
414 legacy_opener(const char *path
, void *user_data
)
422 * Cleanup routine for older style loggers
427 if (logCallback
.user_data
!= NULL
) {
428 g_free (logCallback
.user_data
); /* This is a UserSuppliedLoggerUserData struct */
429 logCallback
.opener
= NULL
;
430 logCallback
.writer
= NULL
;
431 logCallback
.closer
= NULL
;
432 logCallback
.user_data
= NULL
;
433 logCallback
.header
= FALSE
;
438 * mono_trace_set_log_handler:
440 * @callback The callback that will replace the default logging handler
441 * @user_data Argument passed to @callback
443 * The log handler replaces the default runtime logger. All logging requests with be routed to it.
444 * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
445 * execution will not resume after a fatal error.
448 mono_trace_set_log_handler (MonoLogCallback callback
, void *user_data
)
452 if (logCallback
.closer
!= NULL
)
453 logCallback
.closer();
454 UserSuppliedLoggerUserData
*ll
= (UserSuppliedLoggerUserData
*)g_malloc (sizeof (UserSuppliedLoggerUserData
));
455 ll
->legacy_callback
= callback
;
456 ll
->user_data
= user_data
;
457 logCallback
.opener
= legacy_opener
;
458 logCallback
.writer
= callback_adapter
;
459 logCallback
.closer
= legacy_closer
;
460 logCallback
.user_data
= ll
;
461 logCallback
.dest
= NULL
;
463 g_log_set_default_handler (eglib_log_adapter
, user_data
);
467 structured_log_adapter (const gchar
*log_domain
, GLogLevelFlags log_level
, const gchar
*message
, gpointer user_data
)
469 logCallback
.writer (log_domain
, log_level
, logCallback
.header
, message
);
473 * mono_trace_set_log_handler_internal:
474 * \param callback The callback that will replace the default logging handler
475 * \param user_data Argument passed to \p callback
476 * The log handler replaces the default runtime logger. All logging requests with be routed to it.
477 * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
478 * execution will not resume after a fatal error.
481 mono_trace_set_log_handler_internal (MonoLogCallParm
*callback
, void *user_data
)
484 if (logCallback
.closer
!= NULL
)
485 logCallback
.closer();
486 logCallback
.opener
= callback
->opener
;
487 logCallback
.writer
= callback
->writer
;
488 logCallback
.closer
= callback
->closer
;
489 logCallback
.header
= mono_trace_log_header
;
490 logCallback
.dest
= callback
->dest
;
491 logCallback
.opener (logCallback
.dest
, user_data
);
493 g_log_set_default_handler (structured_log_adapter
, user_data
);
497 print_handler (const char *string
)
499 print_callback (string
, TRUE
);
503 printerr_handler (const char *string
)
505 printerr_callback (string
, FALSE
);
509 * mono_trace_set_print_handler:
510 * \param callback The callback that will replace the default runtime behavior for stdout output.
511 * The print handler replaces the default runtime stdout output handler. This is used by free form output done by the runtime.
514 mono_trace_set_print_handler (MonoPrintCallback callback
)
517 print_callback
= callback
;
518 g_set_print_handler (print_handler
);
522 * mono_trace_set_printerr_handler:
523 * \param callback The callback that will replace the default runtime behavior for stderr output.
524 * The print handler replaces the default runtime stderr output handler. This is used by free form output done by the runtime.
527 mono_trace_set_printerr_handler (MonoPrintCallback callback
)
530 printerr_callback
= callback
;
531 g_set_printerr_handler (printerr_handler
);
535 conv_ascii_char (gchar s
)
544 /* No memfree because only called during crash */
546 mono_dump_mem (gpointer d
, int len
)
548 guint8
*data
= (guint8
*) d
;
550 for (int off
= 0; off
< len
; off
+= 0x10) {
551 g_async_safe_printf("%p ", data
+ off
);
553 for (int i
= 0; i
< 0x10; i
++) {
554 if ((i
+ off
) >= len
) {
555 g_async_safe_printf("%s", " ");
557 g_async_safe_printf("%02x ", data
[off
+ i
]);
561 g_async_safe_printf(" ");
563 for (int i
= 0; i
< 0x10; i
++) {
564 if ((i
+ off
) >= len
) {
565 g_async_safe_printf("%s", " ");
567 g_async_safe_printf("%c", conv_ascii_char (data
[off
+ i
]));
571 g_async_safe_printf ("\n");