3 * Support for verbose unmanaged crash dumps
6 * Alexander Kyte (alkyte@microsoft.com)
8 * (C) 2018 Microsoft, Inc.
14 #include <mono/utils/json.h>
15 #include <mono/mini/debugger-state-machine.h>
16 #include <mono/mini/debugger-state-machine.h>
17 #include <mono/metadata/object-internals.h>
18 #include <mono/mini/mini-runtime.h>
19 #include <mono/mini/debugger-engine.h>
20 #include <mono/utils/mono-coop-mutex.h>
21 #include <mono/utils/mono-flight-recorder.h>
24 mono_debug_log_thread_state_to_string (MonoDebuggerThreadState state
)
27 case MONO_DEBUGGER_SUSPENDED
: return "suspended";
28 case MONO_DEBUGGER_RESUMED
: return "resumed";
29 case MONO_DEBUGGER_TERMINATED
: return "terminated";
30 case MONO_DEBUGGER_STARTED
: return "started";
32 g_assert_not_reached ();
37 DEBUG_LOG_ILLEGAL
= 0x0,
38 DEBUG_LOG_STATE_CHANGE
= 0x1,
39 DEBUG_LOG_BREAKPOINT
= 0x2,
40 DEBUG_LOG_COMMAND
= 0x3,
41 DEBUG_LOG_EVENT
= 0x4,
46 #define MONO_MAX_DEBUGGER_LOG_LEN 65
47 // Length of each message
48 #define MONO_MAX_DEBUGGER_MSG_LEN 200
51 MonoDebugLogKind kind
;
53 char message
[MONO_MAX_DEBUGGER_MSG_LEN
];
57 mono_debug_log_kind_to_string (MonoDebugLogKind kind
)
60 case DEBUG_LOG_STATE_CHANGE
: return "transition";
61 case DEBUG_LOG_BREAKPOINT
: return "breakpoint";
62 case DEBUG_LOG_COMMAND
: return "command";
63 case DEBUG_LOG_EVENT
: return "event";
64 case DEBUG_LOG_EXIT
: return "exit";
66 g_assert_not_reached ();
70 #define MONO_DEBUGGER_LOG_FREED -1
71 static MonoFlightRecorder
*debugger_log
;
72 static GPtrArray
*breakpoint_copy
;
75 mono_debugger_log_init (void)
77 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
78 g_error ("Attempted to initialize debugger log after cleanup");
80 debugger_log
= mono_flight_recorder_init (MONO_MAX_DEBUGGER_LOG_LEN
, sizeof (MonoDebugLogItem
));
81 breakpoint_copy
= g_ptr_array_new ();
85 mono_debugger_log_free (void)
87 MonoFlightRecorder
*log
= debugger_log
;
88 debugger_log
= (MonoFlightRecorder
*)GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
);
90 mono_memory_barrier ();
91 mono_flight_recorder_free (log
);
95 mono_debugger_log_command (const char *command_set
, const char *command
, guint8
*buf
, int len
)
97 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
100 // FIXME: print the array in a format that can be decoded / printed?
101 char *msg
= g_strdup_printf ("Command Logged: %s %s Response: %d", command_set
, command
, len
);
102 MonoDebugLogItem payload
;
103 payload
.kind
= DEBUG_LOG_COMMAND
;
105 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
106 mono_flight_recorder_append (debugger_log
, &payload
);
110 mono_debugger_log_event (DebuggerTlsData
*tls
, const char *event
, guint8
*buf
, int len
)
112 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
115 // FIXME: print the array in a format that can be decoded / printed?
116 intptr_t tid
= mono_debugger_tls_thread_id (tls
);
117 char *msg
= g_strdup_printf ("Event logged of type %s Response: %d", event
, len
);
118 MonoDebugLogItem payload
;
119 payload
.kind
= DEBUG_LOG_EVENT
;
121 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
122 mono_flight_recorder_append (debugger_log
, &payload
);
126 mono_debugger_log_exit (int exit_code
)
128 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
131 char *msg
= g_strdup_printf ("Exited with code %d", exit_code
);
132 MonoDebugLogItem payload
;
133 payload
.kind
= DEBUG_LOG_EXIT
;
135 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
136 mono_flight_recorder_append (debugger_log
, &payload
);
140 mono_debugger_log_add_bp (gpointer bp
, MonoMethod
*method
, long il_offset
)
142 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
145 MonoCoopMutex
*debugger_log_mutex
= mono_flight_recorder_mutex (debugger_log
);
146 mono_coop_mutex_lock (debugger_log_mutex
);
147 g_ptr_array_add (breakpoint_copy
, bp
);
148 mono_coop_mutex_unlock (debugger_log_mutex
);
150 char *msg
= g_strdup_printf ("Add breakpoint %s %lu", method
? mono_method_full_name (method
, TRUE
) : "No method", il_offset
);
151 MonoDebugLogItem payload
;
152 payload
.kind
= DEBUG_LOG_BREAKPOINT
;
154 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
155 mono_flight_recorder_append (debugger_log
, &payload
);
159 mono_debugger_log_remove_bp (gpointer bp
, MonoMethod
*method
, long il_offset
)
161 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
164 MonoCoopMutex
*debugger_log_mutex
= mono_flight_recorder_mutex (debugger_log
);
165 mono_coop_mutex_lock (debugger_log_mutex
);
166 g_ptr_array_remove (breakpoint_copy
, bp
);
167 mono_coop_mutex_unlock (debugger_log_mutex
);
169 char *msg
= g_strdup_printf ("Remove breakpoint %s %lu", method
? mono_method_full_name (method
, TRUE
) : "No method", il_offset
);
170 MonoDebugLogItem payload
;
171 payload
.kind
= DEBUG_LOG_BREAKPOINT
;
173 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
174 mono_flight_recorder_append (debugger_log
, &payload
);
178 mono_debugger_log_bp_hit (DebuggerTlsData
*tls
, MonoMethod
*method
, long il_offset
)
180 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
183 intptr_t tid
= mono_debugger_tls_thread_id (tls
);
184 char *msg
= g_strdup_printf ("Hit breakpoint %s %lu", method
? mono_method_full_name (method
, TRUE
) : "No method", il_offset
);
185 MonoDebugLogItem payload
;
186 payload
.kind
= DEBUG_LOG_BREAKPOINT
;
188 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
189 mono_flight_recorder_append (debugger_log
, &payload
);
193 mono_debugger_log_resume (DebuggerTlsData
*tls
)
195 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
198 intptr_t tid
= mono_debugger_tls_thread_id (tls
);
199 MonoDebuggerThreadState prev_state
= mono_debugger_get_thread_state (tls
);
200 g_assert (prev_state
== MONO_DEBUGGER_SUSPENDED
|| prev_state
== MONO_DEBUGGER_STARTED
);
201 mono_debugger_set_thread_state (tls
, prev_state
, MONO_DEBUGGER_RESUMED
);
203 char *msg
= g_strdup_printf ("Resuming 0x%p from state %s", (void*)tid
, mono_debug_log_thread_state_to_string (prev_state
));
204 MonoDebugLogItem payload
;
205 payload
.kind
= DEBUG_LOG_STATE_CHANGE
;
207 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
208 mono_flight_recorder_append (debugger_log
, &payload
);
212 mono_debugger_log_suspend (DebuggerTlsData
*tls
)
214 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
217 intptr_t tid
= mono_debugger_tls_thread_id (tls
);
218 MonoDebuggerThreadState prev_state
= mono_debugger_get_thread_state (tls
);
219 g_assert (prev_state
== MONO_DEBUGGER_RESUMED
|| prev_state
== MONO_DEBUGGER_STARTED
);
220 mono_debugger_set_thread_state (tls
, prev_state
, MONO_DEBUGGER_SUSPENDED
);
222 char *msg
= g_strdup_printf ("Suspending 0x%p from state %s", (void*)tid
, mono_debug_log_thread_state_to_string (prev_state
));
223 MonoDebugLogItem payload
;
224 payload
.kind
= DEBUG_LOG_STATE_CHANGE
;
226 g_snprintf ((gchar
*) &payload
.message
, MONO_MAX_DEBUGGER_MSG_LEN
, "%s", msg
);
227 mono_flight_recorder_append (debugger_log
, &payload
);
233 } DebuggerThreadIterState
;
235 // FIXME: log TCP handshake / connection state
237 dump_thread_state (gpointer key
, gpointer value
, gpointer user_data
)
239 DebuggerTlsData
*debugger_tls
= (DebuggerTlsData
*) value
;
240 DebuggerThreadIterState
*data
= (DebuggerThreadIterState
*) user_data
;
243 mono_json_writer_printf (data
->writer
, ",\n");
245 data
->not_first
= TRUE
;
247 mono_json_writer_indent (data
->writer
);
248 mono_json_writer_object_begin (data
->writer
);
250 mono_json_writer_indent (data
->writer
);
251 mono_json_writer_object_key(data
->writer
, "thread_id");
252 mono_json_writer_printf (data
->writer
, "\"0x%x\",\n", mono_debugger_tls_thread_id (debugger_tls
));
254 mono_json_writer_indent (data
->writer
);
255 mono_json_writer_object_key (data
->writer
, "thread_state");
256 const char *state
= mono_debug_log_thread_state_to_string (mono_debugger_get_thread_state (debugger_tls
));
257 mono_json_writer_printf (data
->writer
, "\"%s\"\n", state
);
259 mono_json_writer_indent_pop (data
->writer
);
260 mono_json_writer_indent (data
->writer
);
261 mono_json_writer_object_end (data
->writer
);
265 mono_debugger_state (JsonWriter
*writer
)
267 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
270 MonoCoopMutex
*debugger_log_mutex
= mono_flight_recorder_mutex (debugger_log
);
271 mono_coop_mutex_lock (debugger_log_mutex
);
272 mono_json_writer_object_begin(writer
);
274 mono_json_writer_indent (writer
);
275 mono_json_writer_object_key(writer
, "debugger_state");
276 mono_json_writer_object_begin(writer
);
278 mono_json_writer_indent (writer
);
279 mono_json_writer_object_key(writer
, "thread_states");
280 mono_json_writer_array_begin (writer
);
281 mono_json_writer_indent_push (writer
);
283 DebuggerThreadIterState iterState
;
284 iterState
.writer
= writer
;
285 iterState
.not_first
= FALSE
;
286 MonoGHashTable
*thread_to_tls
= mono_debugger_get_thread_states ();
287 mono_g_hash_table_foreach (thread_to_tls
, dump_thread_state
, &iterState
);
288 mono_json_writer_printf (writer
, "\n");
290 mono_json_writer_indent_pop (writer
);
291 mono_json_writer_indent (writer
);
292 mono_json_writer_array_end (writer
);
294 mono_json_writer_printf (writer
, ",\n");
296 // FIXME: Log breakpoint state
297 if (breakpoint_copy
->len
> 0) {
298 mono_json_writer_indent (writer
);
299 mono_json_writer_object_key(writer
, "breakpoints");
300 mono_json_writer_array_begin (writer
);
302 for (int i
=0; i
< breakpoint_copy
->len
; i
++) {
303 MonoBreakpoint
*bp
= (MonoBreakpoint
*) g_ptr_array_index (breakpoint_copy
, i
);
305 mono_json_writer_indent (writer
);
306 mono_json_writer_object_begin(writer
);
308 mono_json_writer_indent (writer
);
309 mono_json_writer_object_key(writer
, "method");
310 mono_json_writer_printf (writer
, "\"%s\",\n", bp
->method
? mono_method_full_name (bp
->method
, TRUE
) : "No method");
312 mono_json_writer_indent (writer
);
313 mono_json_writer_object_key(writer
, "il_offset");
314 mono_json_writer_printf (writer
, "\"0x%x\",\n", bp
->il_offset
);
316 mono_json_writer_indent_pop (writer
);
317 mono_json_writer_indent (writer
);
318 mono_json_writer_object_end (writer
);
319 mono_json_writer_printf (writer
, ",\n");
322 mono_json_writer_indent_pop (writer
);
323 mono_json_writer_indent (writer
);
324 mono_json_writer_array_end (writer
);
325 mono_json_writer_printf (writer
, ",\n");
329 MonoFlightRecorderIter diter
;
330 mono_flight_recorder_iter_init (debugger_log
, &diter
);
332 mono_json_writer_indent (writer
);
333 mono_json_writer_object_key(writer
, "debugger_history");
334 mono_json_writer_array_begin (writer
);
336 gboolean first
= TRUE
;
337 MonoDebugLogItem item
;
338 MonoFlightRecorderHeader header
;
340 while (mono_flight_recorder_iter_next (&diter
, &header
, (gpointer
*) &item
)) {
342 mono_json_writer_printf (writer
, ",\n");
346 mono_json_writer_indent (writer
);
347 mono_json_writer_object_begin(writer
);
349 mono_json_writer_indent (writer
);
350 mono_json_writer_object_key(writer
, "kind");
351 mono_json_writer_printf (writer
, "\"%s\",\n", mono_debug_log_kind_to_string (item
.kind
));
353 mono_json_writer_indent (writer
);
354 mono_json_writer_object_key(writer
, "tid");
355 mono_json_writer_printf (writer
, "\"0x%x\",\n", item
.tid
);
357 mono_json_writer_indent (writer
);
358 mono_json_writer_object_key(writer
, "message");
359 mono_json_writer_printf (writer
, "\"%s\",\n", item
.message
);
361 mono_json_writer_indent (writer
);
362 mono_json_writer_object_key(writer
, "counter");
363 mono_json_writer_printf (writer
, "\"%d\"\n", header
.counter
);
365 mono_json_writer_indent_pop (writer
);
366 mono_json_writer_indent (writer
);
367 mono_json_writer_object_end (writer
);
369 mono_json_writer_printf (writer
, "\n");
371 mono_json_writer_indent_pop (writer
);
372 mono_json_writer_indent (writer
);
373 mono_json_writer_array_end (writer
);
374 mono_json_writer_printf (writer
, ",\n");
376 mono_flight_recorder_iter_destroy (&diter
);
378 // Log client/connection state
379 gboolean disconnected
= mono_debugger_is_disconnected ();
380 mono_json_writer_indent (writer
);
381 mono_json_writer_object_key(writer
, "client_state");
382 mono_json_writer_printf (writer
, "\"%s\"\n", disconnected
? "disconnected" : "connected");
384 mono_json_writer_indent (writer
);
385 mono_json_writer_object_end (writer
);
386 mono_json_writer_printf (writer
, "\n");
388 mono_json_writer_indent_pop (writer
);
389 mono_json_writer_indent (writer
);
390 mono_json_writer_object_end (writer
);
392 mono_coop_mutex_unlock (debugger_log_mutex
);
396 mono_debugger_state_str (void)
398 if (debugger_log
== GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED
))
402 mono_json_writer_init (&writer
);
403 mono_debugger_state (&writer
);
405 char *result
= g_strdup(writer
.text
->str
);
406 mono_json_writer_destroy (&writer
);