[interp] Fix DEBUG_INTERP build (#16057)
[mono-project.git] / mono / mini / debugger-state-machine.c
blob1aff76cbade3212fc534e07c25d8f911f23252a0
1 /**
2 * \file
3 * Support for verbose unmanaged crash dumps
5 * Author:
6 * Alexander Kyte (alkyte@microsoft.com)
8 * (C) 2018 Microsoft, Inc.
11 #include <config.h>
12 #include <glib.h>
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>
23 static const char *
24 mono_debug_log_thread_state_to_string (MonoDebuggerThreadState state)
26 switch (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";
31 default:
32 g_assert_not_reached ();
36 typedef enum {
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,
42 DEBUG_LOG_EXIT = 0x5
43 } MonoDebugLogKind;
45 // Number of messages
46 #define MONO_MAX_DEBUGGER_LOG_LEN 65
47 // Length of each message
48 #define MONO_MAX_DEBUGGER_MSG_LEN 200
50 typedef struct {
51 MonoDebugLogKind kind;
52 intptr_t tid;
53 char message [MONO_MAX_DEBUGGER_MSG_LEN];
54 } MonoDebugLogItem;
56 static const char *
57 mono_debug_log_kind_to_string (MonoDebugLogKind kind)
59 switch (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";
65 default:
66 g_assert_not_reached ();
70 #define MONO_DEBUGGER_LOG_FREED -1
71 static MonoFlightRecorder *debugger_log;
72 static GPtrArray *breakpoint_copy;
74 void
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 ();
84 void
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);
94 void
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))
98 return;
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;
104 payload.tid = 0x0;
105 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
106 mono_flight_recorder_append (debugger_log, &payload);
109 void
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))
113 return;
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;
120 payload.tid = tid;
121 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
122 mono_flight_recorder_append (debugger_log, &payload);
125 void
126 mono_debugger_log_exit (int exit_code)
128 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
129 return;
131 char *msg = g_strdup_printf ("Exited with code %d", exit_code);
132 MonoDebugLogItem payload;
133 payload.kind = DEBUG_LOG_EXIT;
134 payload.tid = 0x0;
135 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
136 mono_flight_recorder_append (debugger_log, &payload);
139 void
140 mono_debugger_log_add_bp (gpointer bp, MonoMethod *method, long il_offset)
142 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
143 return;
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;
153 payload.tid = 0x0;
154 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
155 mono_flight_recorder_append (debugger_log, &payload);
158 void
159 mono_debugger_log_remove_bp (gpointer bp, MonoMethod *method, long il_offset)
161 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
162 return;
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;
172 payload.tid = 0x0;
173 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
174 mono_flight_recorder_append (debugger_log, &payload);
177 void
178 mono_debugger_log_bp_hit (DebuggerTlsData *tls, MonoMethod *method, long il_offset)
180 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
181 return;
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;
187 payload.tid = tid;
188 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
189 mono_flight_recorder_append (debugger_log, &payload);
192 void
193 mono_debugger_log_resume (DebuggerTlsData *tls)
195 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
196 return;
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;
206 payload.tid = tid;
207 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
208 mono_flight_recorder_append (debugger_log, &payload);
211 void
212 mono_debugger_log_suspend (DebuggerTlsData *tls)
214 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
215 return;
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;
225 payload.tid = tid;
226 g_snprintf ((gchar *) &payload.message, MONO_MAX_DEBUGGER_MSG_LEN, "%s", msg);
227 mono_flight_recorder_append (debugger_log, &payload);
230 typedef struct {
231 JsonWriter *writer;
232 gboolean not_first;
233 } DebuggerThreadIterState;
235 // FIXME: log TCP handshake / connection state
236 static void
237 dump_thread_state (gpointer key, gpointer value, gpointer user_data)
239 DebuggerTlsData *debugger_tls = (DebuggerTlsData *) value;
240 DebuggerThreadIterState *data = (DebuggerThreadIterState *) user_data;
242 if (data->not_first)
243 mono_json_writer_printf (data->writer, ",\n");
244 else
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);
264 void
265 mono_debugger_state (JsonWriter *writer)
267 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
268 return;
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");
328 // Log history
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)) {
341 if (!first)
342 mono_json_writer_printf (writer, ",\n");
343 else
344 first = FALSE;
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);
395 char *
396 mono_debugger_state_str (void)
398 if (debugger_log == GINT_TO_POINTER (MONO_DEBUGGER_LOG_FREED))
399 return NULL;
401 JsonWriter writer;
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);
408 return result;