Add include for fd_set
[mono/afaerber.git] / mono / mini / debugger-agent.c
blob6bb41d993db45d8b8e218cfdd1050cf44aefd9e2
1 /*
2 * debugger-agent.c: Debugger back-end module
4 * Author:
5 * Zoltan Varga (vargaz@gmail.com)
7 * (C) 2009 Novell, Inc.
8 */
10 #include <config.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #ifdef HAVE_SYS_TYPES_H
15 #include <sys/types.h>
16 #endif
17 #ifdef HAVE_SYS_SELECT_H
18 #include <sys/select.h>
19 #endif
20 #ifdef HAVE_SYS_SOCKET_H
21 #include <sys/socket.h>
22 #endif
23 #ifdef HAVE_NETINET_TCP_H
24 #include <netinet/tcp.h>
25 #endif
26 #ifdef HAVE_NETINET_IN_H
27 #include <netinet/in.h>
28 #endif
29 #ifdef HAVE_NETDB_H
30 #include <netdb.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #include <errno.h>
36 #include <glib.h>
38 #ifdef HAVE_PTHREAD_H
39 #include <pthread.h>
40 #endif
42 #ifdef HAVE_UCONTEXT_H
43 #include <ucontext.h>
44 #endif
46 #ifdef HOST_WIN32
47 #ifdef _MSC_VER
48 #include <winsock2.h>
49 #endif
50 #include <ws2tcpip.h>
51 #ifdef __GNUC__
52 /* cygwin's headers do not seem to define these */
53 void WSAAPI freeaddrinfo (struct addrinfo*);
54 int WSAAPI getaddrinfo (const char*,const char*,const struct addrinfo*,
55 struct addrinfo**);
56 int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
57 char*,DWORD,int);
58 #endif
59 #endif
61 #ifdef PLATFORM_ANDROID
62 #include <linux/in.h>
63 #include <linux/tcp.h>
64 #include <sys/endian.h>
65 #endif
67 #include <mono/metadata/mono-debug.h>
68 #include <mono/metadata/mono-debug-debugger.h>
69 #include <mono/metadata/debug-mono-symfile.h>
70 #include <mono/metadata/gc-internal.h>
71 #include <mono/metadata/threads-types.h>
72 #include <mono/metadata/socket-io.h>
73 #include <mono/utils/mono-semaphore.h>
74 #include "debugger-agent.h"
75 #include "mini.h"
77 #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
78 #define DISABLE_DEBUGGER_AGENT 1
79 #endif
81 #ifdef DISABLE_SOFT_DEBUG
82 #define DISABLE_DEBUGGER_AGENT 1
83 #endif
85 #ifndef DISABLE_DEBUGGER_AGENT
86 #include <mono/io-layer/mono-mutex.h>
88 /* Definitions to make backporting to 2.6 easier */
89 //#define MonoInternalThread MonoThread
90 //#define mono_thread_internal_current mono_thread_current
91 #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
93 typedef struct {
94 gboolean enabled;
95 char *transport;
96 char *address;
97 int log_level;
98 char *log_file;
99 gboolean suspend;
100 gboolean server;
101 gboolean onuncaught;
102 GSList *onthrow;
103 int timeout;
104 char *launch;
105 } AgentConfig;
107 typedef struct
109 int id;
110 guint32 il_offset;
111 MonoDomain *domain;
112 MonoMethod *method;
113 MonoContext ctx;
114 MonoDebugMethodJitInfo *jit;
115 int flags;
117 * Whenever ctx is set. This is FALSE for the last frame of running threads, since
118 * the frame can become invalid.
120 gboolean has_ctx;
121 } StackFrame;
123 typedef struct _InvokeData InvokeData;
125 struct _InvokeData
127 int id;
128 int flags;
129 guint8 *p;
130 guint8 *endp;
131 /* This is the context which needs to be restored after the invoke */
132 MonoContext ctx;
133 gboolean has_ctx;
135 * If this is set, invoke this method with the arguments given by ARGS.
137 MonoMethod *method;
138 gpointer *args;
139 guint32 suspend_count;
141 InvokeData *last_invoke;
144 typedef struct {
145 MonoContext ctx;
146 MonoLMF *lmf;
147 MonoDomain *domain;
148 gboolean has_context;
149 gpointer resume_event;
150 /* This is computed on demand when it is requested using the wire protocol */
151 /* It is freed up when the thread is resumed */
152 int frame_count;
153 StackFrame **frames;
155 * Whenever the frame info is up-to-date. If not, compute_frame_info () will need to
156 * re-compute it.
158 gboolean frames_up_to_date;
160 * Points to data about a pending invoke which needs to be executed after the thread
161 * resumes.
163 InvokeData *pending_invoke;
165 * Set to TRUE if this thread is suspended in suspend_current () or it is executing
166 * native code.
168 gboolean suspended;
170 * Signals whenever the thread is in the process of suspending, i.e. it will suspend
171 * within a finite amount of time.
173 gboolean suspending;
175 * Set to TRUE if this thread is suspended in suspend_current ().
177 gboolean really_suspended;
178 /* Used to pass the context to the breakpoint/single step handler */
179 MonoContext handler_ctx;
180 /* Whenever thread_stop () was called for this thread */
181 gboolean terminated;
183 /* Number of thread interruptions not yet processed */
184 gint32 interrupt_count;
186 /* Whenever to disable breakpoints (used during invokes) */
187 gboolean disable_breakpoints;
190 * Number of times this thread has been resumed using resume_thread ().
192 guint32 resume_count;
194 MonoInternalThread *thread;
197 * Information about the frame which transitioned to native code for running
198 * threads.
200 StackFrameInfo async_last_frame;
203 * The context where the stack walk can be started for running threads.
205 MonoContext async_ctx;
207 gboolean has_async_ctx;
210 * The lmf where the stack walk can be started for running threads.
212 gpointer async_lmf;
215 * The callee address of the last mono_runtime_invoke call
217 gpointer invoke_addr;
219 gboolean abort_requested;
222 * The current mono_runtime_invoke invocation.
224 InvokeData *invoke;
225 } DebuggerTlsData;
228 * Wire Protocol definitions
231 #define HEADER_LENGTH 11
233 #define MAJOR_VERSION 2
234 #define MINOR_VERSION 1
236 typedef enum {
237 CMD_SET_VM = 1,
238 CMD_SET_OBJECT_REF = 9,
239 CMD_SET_STRING_REF = 10,
240 CMD_SET_THREAD = 11,
241 CMD_SET_ARRAY_REF = 13,
242 CMD_SET_EVENT_REQUEST = 15,
243 CMD_SET_STACK_FRAME = 16,
244 CMD_SET_APPDOMAIN = 20,
245 CMD_SET_ASSEMBLY = 21,
246 CMD_SET_METHOD = 22,
247 CMD_SET_TYPE = 23,
248 CMD_SET_MODULE = 24,
249 CMD_SET_EVENT = 64
250 } CommandSet;
252 typedef enum {
253 EVENT_KIND_VM_START = 0,
254 EVENT_KIND_VM_DEATH = 1,
255 EVENT_KIND_THREAD_START = 2,
256 EVENT_KIND_THREAD_DEATH = 3,
257 EVENT_KIND_APPDOMAIN_CREATE = 4,
258 EVENT_KIND_APPDOMAIN_UNLOAD = 5,
259 EVENT_KIND_METHOD_ENTRY = 6,
260 EVENT_KIND_METHOD_EXIT = 7,
261 EVENT_KIND_ASSEMBLY_LOAD = 8,
262 EVENT_KIND_ASSEMBLY_UNLOAD = 9,
263 EVENT_KIND_BREAKPOINT = 10,
264 EVENT_KIND_STEP = 11,
265 EVENT_KIND_TYPE_LOAD = 12,
266 EVENT_KIND_EXCEPTION = 13
267 } EventKind;
269 typedef enum {
270 SUSPEND_POLICY_NONE = 0,
271 SUSPEND_POLICY_EVENT_THREAD = 1,
272 SUSPEND_POLICY_ALL = 2
273 } SuspendPolicy;
275 typedef enum {
276 ERR_NONE = 0,
277 ERR_INVALID_OBJECT = 20,
278 ERR_INVALID_FIELDID = 25,
279 ERR_INVALID_FRAMEID = 30,
280 ERR_NOT_IMPLEMENTED = 100,
281 ERR_NOT_SUSPENDED = 101,
282 ERR_INVALID_ARGUMENT = 102,
283 ERR_UNLOADED = 103,
284 ERR_NO_INVOCATION = 104
285 } ErrorCode;
287 typedef enum {
288 MOD_KIND_COUNT = 1,
289 MOD_KIND_THREAD_ONLY = 3,
290 MOD_KIND_LOCATION_ONLY = 7,
291 MOD_KIND_EXCEPTION_ONLY = 8,
292 MOD_KIND_STEP = 10,
293 MOD_KIND_ASSEMBLY_ONLY = 11
294 } ModifierKind;
296 typedef enum {
297 STEP_DEPTH_INTO = 0,
298 STEP_DEPTH_OVER = 1,
299 STEP_DEPTH_OUT = 2
300 } StepDepth;
302 typedef enum {
303 STEP_SIZE_MIN = 0,
304 STEP_SIZE_LINE = 1
305 } StepSize;
307 typedef enum {
308 TOKEN_TYPE_STRING = 0,
309 TOKEN_TYPE_TYPE = 1,
310 TOKEN_TYPE_FIELD = 2,
311 TOKEN_TYPE_METHOD = 3,
312 TOKEN_TYPE_UNKNOWN = 4
313 } DebuggerTokenType;
315 typedef enum {
316 VALUE_TYPE_ID_NULL = 0xf0,
317 VALUE_TYPE_ID_TYPE = 0xf1
318 } ValueTypeId;
320 typedef enum {
321 FRAME_FLAG_DEBUGGER_INVOKE = 1
322 } StackFrameFlags;
324 typedef enum {
325 INVOKE_FLAG_DISABLE_BREAKPOINTS = 1,
326 INVOKE_FLAG_SINGLE_THREADED = 2
327 } InvokeFlags;
329 typedef enum {
330 CMD_VM_VERSION = 1,
331 CMD_VM_ALL_THREADS = 2,
332 CMD_VM_SUSPEND = 3,
333 CMD_VM_RESUME = 4,
334 CMD_VM_EXIT = 5,
335 CMD_VM_DISPOSE = 6,
336 CMD_VM_INVOKE_METHOD = 7,
337 CMD_VM_SET_PROTOCOL_VERSION = 8,
338 CMD_VM_ABORT_INVOKE = 9
339 } CmdVM;
341 typedef enum {
342 CMD_THREAD_GET_FRAME_INFO = 1,
343 CMD_THREAD_GET_NAME = 2,
344 CMD_THREAD_GET_STATE = 3,
345 CMD_THREAD_GET_INFO = 4
346 } CmdThread;
348 typedef enum {
349 CMD_EVENT_REQUEST_SET = 1,
350 CMD_EVENT_REQUEST_CLEAR = 2,
351 CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS = 3
352 } CmdEvent;
354 typedef enum {
355 CMD_COMPOSITE = 100
356 } CmdComposite;
358 typedef enum {
359 CMD_APPDOMAIN_GET_ROOT_DOMAIN = 1,
360 CMD_APPDOMAIN_GET_FRIENDLY_NAME = 2,
361 CMD_APPDOMAIN_GET_ASSEMBLIES = 3,
362 CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4,
363 CMD_APPDOMAIN_CREATE_STRING = 5,
364 CMD_APPDOMAIN_GET_CORLIB = 6,
365 CMD_APPDOMAIN_CREATE_BOXED_VALUE = 7,
366 } CmdAppDomain;
368 typedef enum {
369 CMD_ASSEMBLY_GET_LOCATION = 1,
370 CMD_ASSEMBLY_GET_ENTRY_POINT = 2,
371 CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3,
372 CMD_ASSEMBLY_GET_OBJECT = 4,
373 CMD_ASSEMBLY_GET_TYPE = 5,
374 CMD_ASSEMBLY_GET_NAME = 6
375 } CmdAssembly;
377 typedef enum {
378 CMD_MODULE_GET_INFO = 1,
379 } CmdModule;
381 typedef enum {
382 CMD_METHOD_GET_NAME = 1,
383 CMD_METHOD_GET_DECLARING_TYPE = 2,
384 CMD_METHOD_GET_DEBUG_INFO = 3,
385 CMD_METHOD_GET_PARAM_INFO = 4,
386 CMD_METHOD_GET_LOCALS_INFO = 5,
387 CMD_METHOD_GET_INFO = 6,
388 CMD_METHOD_GET_BODY = 7,
389 CMD_METHOD_RESOLVE_TOKEN = 8,
390 } CmdMethod;
392 typedef enum {
393 CMD_TYPE_GET_INFO = 1,
394 CMD_TYPE_GET_METHODS = 2,
395 CMD_TYPE_GET_FIELDS = 3,
396 CMD_TYPE_GET_VALUES = 4,
397 CMD_TYPE_GET_OBJECT = 5,
398 CMD_TYPE_GET_SOURCE_FILES = 6,
399 CMD_TYPE_SET_VALUES = 7,
400 CMD_TYPE_IS_ASSIGNABLE_FROM = 8,
401 CMD_TYPE_GET_PROPERTIES = 9,
402 CMD_TYPE_GET_CATTRS = 10,
403 CMD_TYPE_GET_FIELD_CATTRS = 11,
404 CMD_TYPE_GET_PROPERTY_CATTRS = 12
405 } CmdType;
407 typedef enum {
408 CMD_STACK_FRAME_GET_VALUES = 1,
409 CMD_STACK_FRAME_GET_THIS = 2,
410 CMD_STACK_FRAME_SET_VALUES = 3
411 } CmdStackFrame;
413 typedef enum {
414 CMD_ARRAY_REF_GET_LENGTH = 1,
415 CMD_ARRAY_REF_GET_VALUES = 2,
416 CMD_ARRAY_REF_SET_VALUES = 3,
417 } CmdArray;
419 typedef enum {
420 CMD_STRING_REF_GET_VALUE = 1,
421 } CmdString;
423 typedef enum {
424 CMD_OBJECT_REF_GET_TYPE = 1,
425 CMD_OBJECT_REF_GET_VALUES = 2,
426 CMD_OBJECT_REF_IS_COLLECTED = 3,
427 CMD_OBJECT_REF_GET_ADDRESS = 4,
428 CMD_OBJECT_REF_GET_DOMAIN = 5,
429 CMD_OBJECT_REF_SET_VALUES = 6
430 } CmdObject;
432 typedef struct {
433 ModifierKind kind;
434 union {
435 int count; /* For kind == MOD_KIND_COUNT */
436 MonoInternalThread *thread; /* For kind == MOD_KIND_THREAD_ONLY */
437 MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */
438 MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */
439 } data;
440 gboolean caught, uncaught; /* For kind == MOD_KIND_EXCEPTION_ONLY */
441 } Modifier;
443 typedef struct{
444 int id;
445 int event_kind;
446 int suspend_policy;
447 int nmodifiers;
448 gpointer info;
449 Modifier modifiers [MONO_ZERO_LEN_ARRAY];
450 } EventRequest;
453 * Describes a single step request.
455 typedef struct {
456 EventRequest *req;
457 MonoInternalThread *thread;
458 StepDepth depth;
459 StepSize size;
460 gpointer last_sp;
461 gpointer start_sp;
462 MonoMethod *last_method;
463 int last_line;
464 /* Whenever single stepping is performed using start/stop_single_stepping () */
465 gboolean global;
466 /* The list of breakpoints used to implement step-over */
467 GSList *bps;
468 } SingleStepReq;
471 * Contains additional information for an event
473 typedef struct {
474 /* For EVENT_KIND_EXCEPTION */
475 MonoObject *exc;
476 MonoContext catch_ctx;
477 gboolean caught;
478 } EventInfo;
480 /* Dummy structure used for the profiler callbacks */
481 typedef struct {
482 void* dummy;
483 } DebuggerProfiler;
485 #define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; fflush (log_file); } } while (0)
488 * Globals
491 static AgentConfig agent_config;
494 * Whenever the agent is fully initialized.
495 * When using the onuncaught or onthrow options, only some parts of the agent are
496 * initialized on startup, and the full initialization which includes connection
497 * establishment and the startup of the agent thread is only done in response to
498 * an event.
500 static gint32 inited;
502 static int conn_fd;
504 static int packet_id = 0;
506 static int objref_id = 0;
508 static int event_request_id = 0;
510 static int frame_id = 0;
512 static GPtrArray *event_requests;
514 static guint32 debugger_tls_id;
516 static gboolean vm_start_event_sent, vm_death_event_sent, disconnected;
518 /* Maps MonoInternalThread -> DebuggerTlsData */
519 static MonoGHashTable *thread_to_tls;
521 /* Maps tid -> MonoInternalThread */
522 static MonoGHashTable *tid_to_thread;
524 /* Maps tid -> MonoThread (not MonoInternalThread) */
525 static MonoGHashTable *tid_to_thread_obj;
527 static gsize debugger_thread_id;
529 static HANDLE debugger_thread_handle;
531 static int log_level;
533 static FILE *log_file;
535 /* Classes whose class load event has been sent */
536 static GHashTable *loaded_classes;
538 /* Assemblies whose assembly load event has no been sent yet */
539 static GPtrArray *pending_assembly_loads;
541 /* Whenever the debugger thread has exited */
542 static gboolean debugger_thread_exited;
544 /* Cond variable used to wait for debugger_thread_exited becoming true */
545 static mono_cond_t debugger_thread_exited_cond;
547 /* Mutex for the cond var above */
548 static mono_mutex_t debugger_thread_exited_mutex;
550 static DebuggerProfiler debugger_profiler;
552 /* The single step request instance */
553 static SingleStepReq *ss_req = NULL;
554 static gpointer ss_invoke_addr = NULL;
556 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
557 /* Number of single stepping operations in progress */
558 static int ss_count;
559 #endif
561 /* The protocol version of the client */
562 static int major_version, minor_version;
564 /* Whenever the variables above are set by the client */
565 static gboolean protocol_version_set;
567 static void transport_connect (const char *host, int port);
569 static guint32 WINAPI debugger_thread (void *arg);
571 static void runtime_initialized (MonoProfiler *prof);
573 static void runtime_shutdown (MonoProfiler *prof);
575 static void thread_startup (MonoProfiler *prof, intptr_t tid);
577 static void thread_end (MonoProfiler *prof, intptr_t tid);
579 static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result);
581 static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain);
583 static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_data);
585 static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result);
587 static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly);
589 static void start_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
591 static void end_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
593 static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result);
595 static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *jinfo);
597 static void start_single_stepping (void);
599 static void stop_single_stepping (void);
601 static void suspend_current (void);
603 static void clear_event_requests_for_assembly (MonoAssembly *assembly);
605 /* Submodule init/cleanup */
606 static void breakpoints_init (void);
607 static void breakpoints_cleanup (void);
609 static void objrefs_init (void);
610 static void objrefs_cleanup (void);
612 static void ids_init (void);
613 static void ids_cleanup (void);
615 static void suspend_init (void);
617 static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls);
618 static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
619 static void ss_destroy (SingleStepReq *req);
621 static void start_debugger_thread (void);
623 static void finish_agent_init (gboolean on_startup);
625 static int
626 parse_address (char *address, char **host, int *port)
628 char *pos = strchr (address, ':');
630 if (pos == NULL || pos == address)
631 return 1;
633 *host = g_malloc (pos - address + 1);
634 strncpy (*host, address, pos - address);
635 (*host) [pos - address] = '\0';
637 *port = atoi (pos + 1);
639 return 0;
642 static void
643 print_usage (void)
645 fprintf (stderr, "Usage: mono --debugger-agent=[<option>=<value>,...] ...\n");
646 fprintf (stderr, "Available options:\n");
647 fprintf (stderr, " transport=<transport>\t\tTransport to use for connecting to the debugger (mandatory, possible values: 'dt_socket')\n");
648 fprintf (stderr, " address=<hostname>:<port>\tAddress to connect to (mandatory)\n");
649 fprintf (stderr, " loglevel=<n>\t\t\tLog level (defaults to 0)\n");
650 fprintf (stderr, " logfile=<file>\t\tFile to log to (defaults to stdout)\n");
651 fprintf (stderr, " suspend=y/n\t\t\tWhenever to suspend after startup.\n");
652 fprintf (stderr, " timeout=<n>\t\t\tTimeout for connecting in milliseconds.\n");
653 fprintf (stderr, " help\t\t\t\tPrint this help.\n");
656 static gboolean
657 parse_flag (const char *option, char *flag)
659 if (!strcmp (flag, "y"))
660 return TRUE;
661 else if (!strcmp (flag, "n"))
662 return FALSE;
663 else {
664 fprintf (stderr, "debugger-agent: The valid values for the '%s' option are 'y' and 'n'.\n", option);
665 exit (1);
666 return FALSE;
670 void
671 mono_debugger_agent_parse_options (char *options)
673 char **args, **ptr;
674 char *host;
675 int port;
677 #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
678 fprintf (stderr, "--debugger-agent is not supported on this platform.\n");
679 exit (1);
680 #endif
682 agent_config.enabled = TRUE;
683 agent_config.suspend = TRUE;
684 agent_config.server = FALSE;
686 args = g_strsplit (options, ",", -1);
687 for (ptr = args; ptr && *ptr; ptr ++) {
688 char *arg = *ptr;
690 if (strncmp (arg, "transport=", 10) == 0) {
691 agent_config.transport = g_strdup (arg + 10);
692 } else if (strncmp (arg, "address=", 8) == 0) {
693 agent_config.address = g_strdup (arg + 8);
694 } else if (strncmp (arg, "loglevel=", 9) == 0) {
695 agent_config.log_level = atoi (arg + 9);
696 } else if (strncmp (arg, "logfile=", 8) == 0) {
697 agent_config.log_file = g_strdup (arg + 8);
698 } else if (strncmp (arg, "suspend=", 8) == 0) {
699 agent_config.suspend = parse_flag ("suspend", arg + 8);
700 } else if (strncmp (arg, "server=", 7) == 0) {
701 agent_config.server = parse_flag ("server", arg + 7);
702 } else if (strncmp (arg, "onuncaught=", 11) == 0) {
703 agent_config.onuncaught = parse_flag ("onuncaught", arg + 11);
704 } else if (strncmp (arg, "onthrow=", 8) == 0) {
705 /* We support multiple onthrow= options */
706 agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (arg + 8));
707 } else if (strncmp (arg, "onthrow", 7) == 0) {
708 agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (""));
709 } else if (strncmp (arg, "help", 4) == 0) {
710 print_usage ();
711 exit (0);
712 } else if (strncmp (arg, "timeout=", 8) == 0) {
713 agent_config.timeout = atoi (arg + 8);
714 } else if (strncmp (arg, "launch=", 7) == 0) {
715 agent_config.launch = g_strdup (arg + 7);
716 } else {
717 print_usage ();
718 exit (1);
722 if (agent_config.transport == NULL) {
723 fprintf (stderr, "debugger-agent: The 'transport' option is mandatory.\n");
724 exit (1);
726 if (strcmp (agent_config.transport, "dt_socket") != 0) {
727 fprintf (stderr, "debugger-agent: The only supported value for the 'transport' option is 'dt_socket'.\n");
728 exit (1);
731 if (agent_config.address == NULL && !agent_config.server) {
732 fprintf (stderr, "debugger-agent: The 'address' option is mandatory.\n");
733 exit (1);
736 if (agent_config.address && parse_address (agent_config.address, &host, &port)) {
737 fprintf (stderr, "debugger-agent: The format of the 'address' options is '<host>:<port>'\n");
738 exit (1);
742 void
743 mono_debugger_agent_init (void)
745 if (!agent_config.enabled)
746 return;
748 /* Need to know whenever a thread has acquired the loader mutex */
749 mono_loader_lock_track_ownership (TRUE);
751 event_requests = g_ptr_array_new ();
753 mono_mutex_init (&debugger_thread_exited_mutex, NULL);
754 mono_cond_init (&debugger_thread_exited_cond, NULL);
756 mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
757 mono_profiler_set_events (MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS);
758 mono_profiler_install_runtime_initialized (runtime_initialized);
759 mono_profiler_install_appdomain (NULL, appdomain_load, NULL, appdomain_unload);
760 mono_profiler_install_thread (thread_startup, thread_end);
761 mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL);
762 mono_profiler_install_jit_end (jit_end);
763 mono_profiler_install_method_invoke (start_runtime_invoke, end_runtime_invoke);
765 debugger_tls_id = TlsAlloc ();
767 thread_to_tls = mono_g_hash_table_new (NULL, NULL);
768 MONO_GC_REGISTER_ROOT (thread_to_tls);
770 tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
771 MONO_GC_REGISTER_ROOT (tid_to_thread);
773 tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
774 MONO_GC_REGISTER_ROOT (tid_to_thread_obj);
776 loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL);
777 pending_assembly_loads = g_ptr_array_new ();
779 log_level = agent_config.log_level;
781 if (agent_config.log_file) {
782 log_file = fopen (agent_config.log_file, "w+");
783 if (!log_file) {
784 fprintf (stderr, "Unable to create log file '%s': %s.\n", agent_config.log_file, strerror (errno));
785 exit (1);
787 } else {
788 log_file = stdout;
791 ids_init ();
792 objrefs_init ();
793 breakpoints_init ();
794 suspend_init ();
796 mini_get_debug_options ()->gen_seq_points = TRUE;
798 * This is needed because currently we don't handle liveness info.
800 mini_get_debug_options ()->mdb_optimizations = TRUE;
802 /* This is needed because we can't set local variables in registers yet */
803 mono_disable_optimizations (MONO_OPT_LINEARS);
805 if (!agent_config.onuncaught && !agent_config.onthrow)
806 finish_agent_init (TRUE);
810 * finish_agent_init:
812 * Finish the initialization of the agent. This involves connecting the transport
813 * and starting the agent thread. This is either done at startup, or
814 * in response to some event like an unhandled exception.
816 static void
817 finish_agent_init (gboolean on_startup)
819 char *host;
820 int port;
821 int res;
823 if (InterlockedCompareExchange (&inited, 1, 0) == 1)
824 return;
826 if (agent_config.launch) {
827 char *argv [16];
829 // FIXME: Generated address
830 // FIXME: Races with transport_connect ()
832 argv [0] = agent_config.launch;
833 argv [1] = agent_config.transport;
834 argv [2] = agent_config.address;
835 argv [3] = NULL;
837 res = g_spawn_async_with_pipes (NULL, argv, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
838 if (!res) {
839 fprintf (stderr, "Failed to execute '%s'.\n", agent_config.launch);
840 exit (1);
844 if (agent_config.address) {
845 res = parse_address (agent_config.address, &host, &port);
846 g_assert (res == 0);
847 } else {
848 host = NULL;
849 port = 0;
852 transport_connect (host, port);
854 if (!on_startup) {
855 /* Do some which is usually done after sending the VMStart () event */
856 vm_start_event_sent = TRUE;
857 start_debugger_thread ();
861 static void
862 mono_debugger_agent_cleanup (void)
864 if (!inited)
865 return;
867 /* This will interrupt the agent thread */
868 /* Close the read part only so it can still send back replies */
869 #ifdef HOST_WIN32
870 shutdown (conn_fd, SD_RECEIVE);
871 #else
872 shutdown (conn_fd, SHUT_RD);
873 #endif
876 * Wait for the thread to exit.
878 * If we continue with the shutdown without waiting for it, then the client might
879 * not receive an answer to its last command like a resume.
880 * The WaitForSingleObject infrastructure doesn't seem to work during shutdown, so
881 * use pthreads.
883 //WaitForSingleObject (debugger_thread_handle, INFINITE);
884 if (GetCurrentThreadId () != debugger_thread_id) {
885 mono_mutex_lock (&debugger_thread_exited_mutex);
886 if (!debugger_thread_exited) {
887 #ifdef HOST_WIN32
888 if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) {
889 mono_mutex_unlock (&debugger_thread_exited_mutex);
890 Sleep(0);
891 mono_mutex_lock (&debugger_thread_exited_mutex);
893 #else
894 mono_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex);
895 #endif
897 mono_mutex_unlock (&debugger_thread_exited_mutex);
900 breakpoints_cleanup ();
901 objrefs_cleanup ();
902 ids_cleanup ();
904 #ifdef HOST_WIN32
905 shutdown (conn_fd, SD_BOTH);
906 #else
907 shutdown (conn_fd, SHUT_RDWR);
908 #endif
910 mono_mutex_destroy (&debugger_thread_exited_mutex);
911 mono_cond_destroy (&debugger_thread_exited_cond);
915 * recv_length:
917 * recv() + handle incomplete reads and EINTR
919 static int
920 recv_length (int fd, void *buf, int len, int flags)
922 int res;
923 int total = 0;
925 do {
926 res = recv (fd, (char *) buf + total, len - total, flags);
927 if (res > 0)
928 total += res;
929 } while ((res > 0 && total < len) || (res == -1 && errno == EINTR));
930 return total;
933 * transport_connect:
935 * Connect/Listen on HOST:PORT. If HOST is NULL, generate an address and listen on it.
937 static void
938 transport_connect (const char *host, int port)
940 struct addrinfo hints;
941 struct addrinfo *result, *rp;
942 int sfd, s, res;
943 char port_string [128];
944 char handshake_msg [128];
945 guint8 buf [128];
947 conn_fd = -1;
949 if (host) {
950 sprintf (port_string, "%d", port);
952 mono_network_init ();
954 /* Obtain address(es) matching host/port */
956 memset (&hints, 0, sizeof (struct addrinfo));
957 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
958 hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
959 hints.ai_flags = 0;
960 hints.ai_protocol = 0; /* Any protocol */
962 s = getaddrinfo (host, port_string, &hints, &result);
963 if (s != 0) {
964 fprintf (stderr, "debugger-agent: Unable to connect to %s:%d: %s\n", host, port, gai_strerror (s));
965 exit (1);
969 if (agent_config.server) {
970 /* Wait for a connection */
971 if (!host) {
972 struct sockaddr_in addr;
973 socklen_t addrlen;
975 /* No address, generate one */
976 sfd = socket (AF_INET, SOCK_STREAM, 0);
977 g_assert (sfd);
979 /* This will bind the socket to a random port */
980 res = listen (sfd, 16);
981 if (res == -1) {
982 fprintf (stderr, "debugger-agent: Unable to setup listening socket: %s\n", strerror (errno));
983 exit (1);
986 addrlen = sizeof (addr);
987 memset (&addr, 0, sizeof (addr));
988 res = getsockname (sfd, &addr, &addrlen);
989 g_assert (res == 0);
991 host = "127.0.0.1";
992 port = ntohs (addr.sin_port);
994 /* Emit the address to stdout */
995 /* FIXME: Should print another interface, not localhost */
996 printf ("%s:%d\n", host, port);
997 } else {
998 /* Listen on the provided address */
999 for (rp = result; rp != NULL; rp = rp->ai_next) {
1000 sfd = socket (rp->ai_family, rp->ai_socktype,
1001 rp->ai_protocol);
1002 if (sfd == -1)
1003 continue;
1005 res = bind (sfd, rp->ai_addr, rp->ai_addrlen);
1006 if (res == -1)
1007 continue;
1009 res = listen (sfd, 16);
1010 if (res == -1)
1011 continue;
1012 break;
1015 #ifndef HOST_WIN32
1017 * this function is not present on win2000 which we still support, and the
1018 * workaround described here:
1019 * http://msdn.microsoft.com/en-us/library/ms737931(VS.85).aspx
1020 * only works with MSVC.
1022 freeaddrinfo (result);
1023 #endif
1026 DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
1028 if (agent_config.timeout) {
1029 fd_set readfds;
1030 struct timeval tv;
1032 tv.tv_sec = 0;
1033 tv.tv_usec = agent_config.timeout * 1000;
1034 FD_ZERO (&readfds);
1035 FD_SET (sfd, &readfds);
1036 res = select (sfd + 1, &readfds, NULL, NULL, &tv);
1037 if (res == 0) {
1038 fprintf (stderr, "debugger-agent: Timed out waiting to connect.\n");
1039 exit (1);
1043 conn_fd = accept (sfd, NULL, NULL);
1044 if (conn_fd == -1) {
1045 fprintf (stderr, "debugger-agent: Unable to listen on %s:%d\n", host, port);
1046 exit (1);
1049 DEBUG (1, fprintf (log_file, "Accepted connection from client, socket fd=%d.\n", conn_fd));
1050 } else {
1051 /* Connect to the specified address */
1052 /* FIXME: Respect the timeout */
1053 for (rp = result; rp != NULL; rp = rp->ai_next) {
1054 sfd = socket (rp->ai_family, rp->ai_socktype,
1055 rp->ai_protocol);
1056 if (sfd == -1)
1057 continue;
1059 if (connect (sfd, rp->ai_addr, rp->ai_addrlen) != -1)
1060 break; /* Success */
1062 close (sfd);
1065 conn_fd = sfd;
1067 #ifndef HOST_WIN32
1068 /* See the comment above */
1069 freeaddrinfo (result);
1070 #endif
1072 if (rp == 0) {
1073 fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
1074 exit (1);
1078 /* Write handshake message */
1079 sprintf (handshake_msg, "DWP-Handshake");
1080 do {
1081 res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0);
1082 } while (res == -1 && errno == EINTR);
1083 g_assert (res != -1);
1085 /* Read answer */
1086 res = recv_length (conn_fd, buf, strlen (handshake_msg), 0);
1087 if ((res != strlen (handshake_msg)) || (memcmp (buf, handshake_msg, strlen (handshake_msg) != 0))) {
1088 fprintf (stderr, "debugger-agent: DWP handshake failed.\n");
1089 exit (1);
1093 * To support older clients, the client sends its protocol version after connecting
1094 * using a command. Until that is received, default to our protocol version.
1096 major_version = MAJOR_VERSION;
1097 minor_version = MINOR_VERSION;
1098 protocol_version_set = FALSE;
1101 * Set TCP_NODELAY on the socket so the client receives events/command
1102 * results immediately.
1105 int flag = 1;
1106 int result = setsockopt(conn_fd,
1107 IPPROTO_TCP,
1108 TCP_NODELAY,
1109 (char *) &flag,
1110 sizeof(int));
1111 g_assert (result >= 0);
1115 static gboolean
1116 transport_send (guint8 *data, int len)
1118 int res;
1120 do {
1121 res = send (conn_fd, data, len, 0);
1122 } while (res == -1 && errno == EINTR);
1123 if (res != len)
1124 return FALSE;
1125 else
1126 return TRUE;
1129 static void
1130 start_debugger_thread (void)
1132 gsize tid;
1134 debugger_thread_handle = mono_create_thread (NULL, 0, debugger_thread, NULL, 0, &tid);
1135 g_assert (debugger_thread_handle);
1139 * Functions to decode protocol data
1142 static inline int
1143 decode_byte (guint8 *buf, guint8 **endbuf, guint8 *limit)
1145 *endbuf = buf + 1;
1146 g_assert (*endbuf <= limit);
1147 return buf [0];
1150 static inline int
1151 decode_int (guint8 *buf, guint8 **endbuf, guint8 *limit)
1153 *endbuf = buf + 4;
1154 g_assert (*endbuf <= limit);
1156 return (((int)buf [0]) << 24) | (((int)buf [1]) << 16) | (((int)buf [2]) << 8) | (((int)buf [3]) << 0);
1159 static inline gint64
1160 decode_long (guint8 *buf, guint8 **endbuf, guint8 *limit)
1162 guint32 high = decode_int (buf, &buf, limit);
1163 guint32 low = decode_int (buf, &buf, limit);
1165 *endbuf = buf;
1167 return ((((guint64)high) << 32) | ((guint64)low));
1170 static inline int
1171 decode_id (guint8 *buf, guint8 **endbuf, guint8 *limit)
1173 return decode_int (buf, endbuf, limit);
1176 static inline char*
1177 decode_string (guint8 *buf, guint8 **endbuf, guint8 *limit)
1179 int len = decode_int (buf, &buf, limit);
1180 char *s;
1182 s = g_malloc (len + 1);
1183 g_assert (s);
1185 memcpy (s, buf, len);
1186 s [len] = '\0';
1187 buf += len;
1188 *endbuf = buf;
1190 return s;
1194 * Functions to encode protocol data
1197 typedef struct {
1198 guint8 *buf, *p, *end;
1199 } Buffer;
1201 static inline void
1202 buffer_init (Buffer *buf, int size)
1204 buf->buf = g_malloc (size);
1205 buf->p = buf->buf;
1206 buf->end = buf->buf + size;
1209 static inline void
1210 buffer_make_room (Buffer *buf, int size)
1212 if (buf->end - buf->p < size) {
1213 int new_size = buf->end - buf->buf + size + 32;
1214 guint8 *p = g_realloc (buf->buf, new_size);
1215 size = buf->p - buf->buf;
1216 buf->buf = p;
1217 buf->p = p + size;
1218 buf->end = buf->buf + new_size;
1222 static inline void
1223 buffer_add_byte (Buffer *buf, guint8 val)
1225 buffer_make_room (buf, 1);
1226 buf->p [0] = val;
1227 buf->p++;
1230 static inline void
1231 buffer_add_int (Buffer *buf, guint32 val)
1233 buffer_make_room (buf, 4);
1234 buf->p [0] = (val >> 24) & 0xff;
1235 buf->p [1] = (val >> 16) & 0xff;
1236 buf->p [2] = (val >> 8) & 0xff;
1237 buf->p [3] = (val >> 0) & 0xff;
1238 buf->p += 4;
1241 static inline void
1242 buffer_add_long (Buffer *buf, guint64 l)
1244 buffer_add_int (buf, (l >> 32) & 0xffffffff);
1245 buffer_add_int (buf, (l >> 0) & 0xffffffff);
1248 static inline void
1249 buffer_add_id (Buffer *buf, int id)
1251 buffer_add_int (buf, (guint64)id);
1254 static inline void
1255 buffer_add_data (Buffer *buf, guint8 *data, int len)
1257 buffer_make_room (buf, len);
1258 memcpy (buf->p, data, len);
1259 buf->p += len;
1262 static inline void
1263 buffer_add_string (Buffer *buf, const char *str)
1265 int len;
1267 if (str == NULL) {
1268 buffer_add_int (buf, 0);
1269 } else {
1270 len = strlen (str);
1271 buffer_add_int (buf, len);
1272 buffer_add_data (buf, (guint8*)str, len);
1276 static inline void
1277 buffer_free (Buffer *buf)
1279 g_free (buf->buf);
1282 static gboolean
1283 send_packet (int command_set, int command, Buffer *data)
1285 Buffer buf;
1286 int len, id;
1287 gboolean res;
1289 id = InterlockedIncrement (&packet_id);
1291 len = data->p - data->buf + 11;
1292 buffer_init (&buf, len);
1293 buffer_add_int (&buf, len);
1294 buffer_add_int (&buf, id);
1295 buffer_add_byte (&buf, 0); /* flags */
1296 buffer_add_byte (&buf, command_set);
1297 buffer_add_byte (&buf, command);
1298 memcpy (buf.buf + 11, data->buf, data->p - data->buf);
1300 res = transport_send (buf.buf, len);
1302 buffer_free (&buf);
1304 return res;
1307 static gboolean
1308 send_reply_packet (int id, int error, Buffer *data)
1310 Buffer buf;
1311 int len;
1312 gboolean res;
1314 len = data->p - data->buf + 11;
1315 buffer_init (&buf, len);
1316 buffer_add_int (&buf, len);
1317 buffer_add_int (&buf, id);
1318 buffer_add_byte (&buf, 0x80); /* flags */
1319 buffer_add_byte (&buf, (error >> 8) & 0xff);
1320 buffer_add_byte (&buf, error);
1321 memcpy (buf.buf + 11, data->buf, data->p - data->buf);
1323 res = transport_send (buf.buf, len);
1325 buffer_free (&buf);
1327 return res;
1331 * OBJECT IDS
1335 * Represents an object accessible by the debugger client.
1337 typedef struct {
1338 /* Unique id used in the wire protocol to refer to objects */
1339 int id;
1341 * A weakref gc handle pointing to the object. The gc handle is used to
1342 * detect if the object was garbage collected.
1344 guint32 handle;
1345 } ObjRef;
1347 /* Maps objid -> ObjRef */
1348 static GHashTable *objrefs;
1350 static void
1351 free_objref (gpointer value)
1353 ObjRef *o = value;
1355 mono_gchandle_free (o->handle);
1357 g_free (o);
1360 static void
1361 objrefs_init (void)
1363 objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref);
1366 static void
1367 objrefs_cleanup (void)
1369 g_hash_table_destroy (objrefs);
1370 objrefs = NULL;
1373 static GHashTable *obj_to_objref;
1376 * Return an ObjRef for OBJ.
1378 static ObjRef*
1379 get_objref (MonoObject *obj)
1381 ObjRef *ref;
1383 if (obj == NULL)
1384 return 0;
1386 #ifdef HAVE_SGEN_GC
1387 NOT_IMPLEMENTED;
1388 #endif
1390 /* Use a hash table with masked pointers to internalize object references */
1391 /* FIXME: This can grow indefinitely */
1392 mono_loader_lock ();
1394 if (!obj_to_objref)
1395 obj_to_objref = g_hash_table_new (NULL, NULL);
1397 ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
1398 /* ref might refer to a different object with the same addr which was GCd */
1399 if (ref && mono_gchandle_get_target (ref->handle) == obj) {
1400 mono_loader_unlock ();
1401 return ref;
1404 ref = g_new0 (ObjRef, 1);
1405 ref->id = InterlockedIncrement (&objref_id);
1406 ref->handle = mono_gchandle_new_weakref (obj, FALSE);
1408 g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
1409 g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
1411 mono_loader_unlock ();
1413 return ref;
1416 static inline int
1417 get_objid (MonoObject *obj)
1419 return get_objref (obj)->id;
1423 * Set OBJ to the object identified by OBJID.
1424 * Returns 0 or an error code if OBJID is invalid or the object has been garbage
1425 * collected.
1427 static ErrorCode
1428 get_object_allow_null (int objid, MonoObject **obj)
1430 ObjRef *ref;
1432 if (objid == 0) {
1433 *obj = NULL;
1434 return 0;
1437 if (!objrefs)
1438 return ERR_INVALID_OBJECT;
1440 mono_loader_lock ();
1442 ref = g_hash_table_lookup (objrefs, GINT_TO_POINTER (objid));
1444 if (ref) {
1445 *obj = mono_gchandle_get_target (ref->handle);
1446 mono_loader_unlock ();
1447 if (!(*obj))
1448 return ERR_INVALID_OBJECT;
1449 return 0;
1450 } else {
1451 mono_loader_unlock ();
1452 return ERR_INVALID_OBJECT;
1456 static ErrorCode
1457 get_object (int objid, MonoObject **obj)
1459 int err = get_object_allow_null (objid, obj);
1461 if (err)
1462 return err;
1463 if (!(*obj))
1464 return ERR_INVALID_OBJECT;
1465 return 0;
1468 static inline int
1469 decode_objid (guint8 *buf, guint8 **endbuf, guint8 *limit)
1471 return decode_id (buf, endbuf, limit);
1474 static inline void
1475 buffer_add_objid (Buffer *buf, MonoObject *o)
1477 buffer_add_id (buf, get_objid (o));
1481 * IDS
1484 typedef enum {
1485 ID_ASSEMBLY = 0,
1486 ID_MODULE = 1,
1487 ID_TYPE = 2,
1488 ID_METHOD = 3,
1489 ID_FIELD = 4,
1490 ID_DOMAIN = 5,
1491 ID_PROPERTY = 6,
1492 ID_NUM
1493 } IdType;
1496 * Represents a runtime structure accessible to the debugger client
1498 typedef struct {
1499 /* Unique id used in the wire protocol */
1500 int id;
1501 /* Domain of the runtime structure, NULL if the domain was unloaded */
1502 MonoDomain *domain;
1503 union {
1504 gpointer val;
1505 MonoClass *klass;
1506 MonoMethod *method;
1507 MonoImage *image;
1508 MonoAssembly *assembly;
1509 MonoClassField *field;
1510 MonoDomain *domain;
1511 MonoProperty *property;
1512 } data;
1513 } Id;
1515 typedef struct {
1516 /* Maps runtime structure -> Id */
1517 GHashTable *val_to_id [ID_NUM];
1518 } AgentDomainInfo;
1520 /* Maps id -> Id */
1521 static GPtrArray *ids [ID_NUM];
1523 static void
1524 ids_init (void)
1526 int i;
1528 for (i = 0; i < ID_NUM; ++i)
1529 ids [i] = g_ptr_array_new ();
1532 static void
1533 ids_cleanup (void)
1535 int i, j;
1537 for (i = 0; i < ID_NUM; ++i) {
1538 if (ids [i]) {
1539 for (j = 0; j < ids [i]->len; ++j)
1540 g_free (g_ptr_array_index (ids [i], j));
1541 g_ptr_array_free (ids [i], TRUE);
1543 ids [i] = NULL;
1547 void
1548 mono_debugger_agent_free_domain_info (MonoDomain *domain)
1550 AgentDomainInfo *info = domain_jit_info (domain)->agent_info;
1551 int i, j;
1553 if (info) {
1554 for (i = 0; i < ID_NUM; ++i)
1555 if (info->val_to_id [i])
1556 g_hash_table_destroy (info->val_to_id [i]);
1557 g_free (info);
1560 domain_jit_info (domain)->agent_info = NULL;
1562 /* Clear ids referencing structures in the domain */
1563 for (i = 0; i < ID_NUM; ++i) {
1564 if (ids [i]) {
1565 for (j = 0; j < ids [i]->len; ++j) {
1566 Id *id = g_ptr_array_index (ids [i], j);
1567 if (id->domain == domain)
1568 id->domain = NULL;
1574 static int
1575 get_id (MonoDomain *domain, IdType type, gpointer val)
1577 Id *id;
1578 AgentDomainInfo *info;
1580 if (val == NULL)
1581 return 0;
1583 mono_loader_lock ();
1585 mono_domain_lock (domain);
1587 if (!domain_jit_info (domain)->agent_info)
1588 domain_jit_info (domain)->agent_info = g_new0 (AgentDomainInfo, 1);
1589 info = domain_jit_info (domain)->agent_info;
1590 if (info->val_to_id [type] == NULL)
1591 info->val_to_id [type] = g_hash_table_new (mono_aligned_addr_hash, NULL);
1593 id = g_hash_table_lookup (info->val_to_id [type], val);
1594 if (id) {
1595 mono_domain_unlock (domain);
1596 mono_loader_unlock ();
1597 return id->id;
1600 id = g_new0 (Id, 1);
1601 /* Reserve id 0 */
1602 id->id = ids [type]->len + 1;
1603 id->domain = domain;
1604 id->data.val = val;
1606 g_hash_table_insert (info->val_to_id [type], val, id);
1608 mono_domain_unlock (domain);
1610 g_ptr_array_add (ids [type], id);
1612 mono_loader_unlock ();
1614 return id->id;
1617 static inline gpointer
1618 decode_ptr_id (guint8 *buf, guint8 **endbuf, guint8 *limit, IdType type, MonoDomain **domain, int *err)
1620 Id *res;
1622 int id = decode_id (buf, endbuf, limit);
1624 *err = 0;
1625 if (domain)
1626 *domain = NULL;
1628 if (id == 0)
1629 return NULL;
1631 // FIXME: error handling
1632 mono_loader_lock ();
1633 g_assert (id > 0 && id <= ids [type]->len);
1635 res = g_ptr_array_index (ids [type], GPOINTER_TO_INT (id - 1));
1636 mono_loader_unlock ();
1638 if (res->domain == NULL) {
1639 *err = ERR_UNLOADED;
1640 return NULL;
1643 if (domain)
1644 *domain = res->domain;
1646 return res->data.val;
1649 static inline void
1650 buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val)
1652 buffer_add_id (buf, get_id (domain, type, val));
1655 static inline MonoClass*
1656 decode_typeid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
1658 return decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
1661 static inline MonoAssembly*
1662 decode_assemblyid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
1664 return decode_ptr_id (buf, endbuf, limit, ID_ASSEMBLY, domain, err);
1667 static inline MonoImage*
1668 decode_moduleid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
1670 return decode_ptr_id (buf, endbuf, limit, ID_MODULE, domain, err);
1673 static inline MonoMethod*
1674 decode_methodid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
1676 return decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err);
1679 static inline MonoClassField*
1680 decode_fieldid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
1682 return decode_ptr_id (buf, endbuf, limit, ID_FIELD, domain, err);
1685 static inline MonoDomain*
1686 decode_domainid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
1688 return decode_ptr_id (buf, endbuf, limit, ID_DOMAIN, domain, err);
1691 static inline MonoProperty*
1692 decode_propertyid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
1694 return decode_ptr_id (buf, endbuf, limit, ID_PROPERTY, domain, err);
1697 static inline void
1698 buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass)
1700 buffer_add_ptr_id (buf, domain, ID_TYPE, klass);
1703 static inline void
1704 buffer_add_methodid (Buffer *buf, MonoDomain *domain, MonoMethod *method)
1706 buffer_add_ptr_id (buf, domain, ID_METHOD, method);
1709 static inline void
1710 buffer_add_assemblyid (Buffer *buf, MonoDomain *domain, MonoAssembly *assembly)
1712 buffer_add_ptr_id (buf, domain, ID_ASSEMBLY, assembly);
1715 static inline void
1716 buffer_add_moduleid (Buffer *buf, MonoDomain *domain, MonoImage *image)
1718 buffer_add_ptr_id (buf, domain, ID_MODULE, image);
1721 static inline void
1722 buffer_add_fieldid (Buffer *buf, MonoDomain *domain, MonoClassField *field)
1724 buffer_add_ptr_id (buf, domain, ID_FIELD, field);
1727 static inline void
1728 buffer_add_propertyid (Buffer *buf, MonoDomain *domain, MonoProperty *property)
1730 buffer_add_ptr_id (buf, domain, ID_PROPERTY, property);
1733 static inline void
1734 buffer_add_domainid (Buffer *buf, MonoDomain *domain)
1736 buffer_add_ptr_id (buf, domain, ID_DOMAIN, domain);
1739 static void invoke_method (void);
1742 * SUSPEND/RESUME
1746 * save_thread_context:
1748 * Set CTX as the current threads context which is used for computing stack traces.
1749 * This function is signal-safe.
1751 static void
1752 save_thread_context (MonoContext *ctx)
1754 DebuggerTlsData *tls;
1756 tls = TlsGetValue (debugger_tls_id);
1757 g_assert (tls);
1759 if (ctx) {
1760 memcpy (&tls->ctx, ctx, sizeof (MonoContext));
1761 } else {
1762 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
1763 MONO_INIT_CONTEXT_FROM_CURRENT (&tls->ctx);
1764 #else
1765 MONO_INIT_CONTEXT_FROM_FUNC (&tls->ctx, save_thread_context);
1766 #endif
1769 tls->lmf = mono_get_lmf ();
1770 tls->domain = mono_domain_get ();
1771 tls->has_context = TRUE;
1774 /* The number of times the runtime is suspended */
1775 static gint32 suspend_count;
1777 /* Number of threads suspended */
1779 * If this is equal to the size of thread_to_tls, the runtime is considered
1780 * suspended.
1782 static gint32 threads_suspend_count;
1784 static mono_mutex_t suspend_mutex;
1786 /* Cond variable used to wait for suspend_count becoming 0 */
1787 static mono_cond_t suspend_cond;
1789 /* Semaphore used to wait for a thread becoming suspended */
1790 static MonoSemType suspend_sem;
1792 static void
1793 suspend_init (void)
1795 mono_mutex_init (&suspend_mutex, NULL);
1796 mono_cond_init (&suspend_cond, NULL);
1797 MONO_SEM_INIT (&suspend_sem, 0);
1800 typedef struct
1802 StackFrameInfo last_frame;
1803 gboolean last_frame_set;
1804 MonoContext ctx;
1805 gpointer lmf;
1806 } GetLastFrameUserData;
1808 static gboolean
1809 get_last_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
1811 GetLastFrameUserData *data = user_data;
1813 if (info->type == FRAME_TYPE_MANAGED_TO_NATIVE)
1814 return FALSE;
1816 if (!data->last_frame_set) {
1817 /* Store the last frame */
1818 memcpy (&data->last_frame, info, sizeof (StackFrameInfo));
1819 data->last_frame_set = TRUE;
1820 return FALSE;
1821 } else {
1822 /* Store the context/lmf for the frame above the last frame */
1823 memcpy (&data->ctx, ctx, sizeof (MonoContext));
1824 data->lmf = info->lmf;
1826 return TRUE;
1831 * mono_debugger_agent_thread_interrupt:
1833 * Called by the abort signal handler.
1834 * Should be signal safe.
1836 gboolean
1837 mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
1839 DebuggerTlsData *tls;
1841 if (!inited)
1842 return FALSE;
1844 tls = TlsGetValue (debugger_tls_id);
1845 if (!tls)
1846 return FALSE;
1849 * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
1850 * guarantee the signal handler will be called that many times. Instead of tracking
1851 * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
1852 * has been requested that hasn't been handled yet, otherwise we can have threads
1853 * refuse to die when VM_EXIT is called
1855 #if defined(__APPLE__)
1856 if (InterlockedCompareExchange (&tls->interrupt_count, 0, 1) == 0)
1857 return FALSE;
1858 #else
1860 * We use interrupt_count to determine whenever this interrupt should be processed
1861 * by us or the normal interrupt processing code in the signal handler.
1862 * There is no race here with notify_thread (), since the signal is sent after
1863 * incrementing interrupt_count.
1865 if (tls->interrupt_count == 0)
1866 return FALSE;
1868 InterlockedDecrement (&tls->interrupt_count);
1869 #endif
1871 // FIXME: Races when the thread leaves managed code before hitting a single step
1872 // event.
1874 if (ji) {
1875 /* Running managed code, will be suspended by the single step code */
1876 DEBUG (1, printf ("[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)GetCurrentThreadId (), ji->method->name, mono_arch_ip_from_context (sigctx)));
1877 return TRUE;
1878 } else {
1880 * Running native code, will be suspended when it returns to/enters
1881 * managed code. Treat it as already suspended.
1882 * This might interrupt the code in process_single_step_inner (), we use the
1883 * tls->suspending flag to avoid races when that happens.
1885 if (!tls->suspended && !tls->suspending) {
1886 MonoContext ctx;
1887 GetLastFrameUserData data;
1889 // FIXME: printf is not signal safe, but this is only used during
1890 // debugger debugging
1891 DEBUG (1, printf ("[%p] Received interrupt while at %p, treating as suspended.\n", (gpointer)GetCurrentThreadId (), mono_arch_ip_from_context (sigctx)));
1892 //save_thread_context (&ctx);
1894 if (!tls->thread)
1895 /* Already terminated */
1896 return TRUE;
1899 * We are in a difficult position: we want to be able to provide stack
1900 * traces for this thread, but we can't use the current ctx+lmf, since
1901 * the thread is still running, so it might return to managed code,
1902 * making these invalid.
1903 * So we start a stack walk and save the first frame, along with the
1904 * parent frame's ctx+lmf. This (hopefully) works because the thread will be
1905 * suspended when it returns to managed code, so the parent's ctx should
1906 * remain valid.
1908 data.last_frame_set = FALSE;
1909 if (sigctx) {
1910 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
1911 mono_jit_walk_stack_from_ctx_in_thread (get_last_frame, mono_domain_get (), &ctx, FALSE, tls->thread, mono_get_lmf (), &data);
1913 if (data.last_frame_set) {
1914 memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo));
1915 memcpy (&tls->async_ctx, &data.ctx, sizeof (MonoContext));
1916 tls->async_lmf = data.lmf;
1917 tls->has_async_ctx = TRUE;
1918 tls->domain = mono_domain_get ();
1919 memcpy (&tls->ctx, &ctx, sizeof (MonoContext));
1920 } else {
1921 tls->has_async_ctx = FALSE;
1924 mono_memory_barrier ();
1926 tls->suspended = TRUE;
1927 MONO_SEM_POST (&suspend_sem);
1929 return TRUE;
1933 #ifdef HOST_WIN32
1934 static void CALLBACK notify_thread_apc (ULONG_PTR param)
1936 //DebugBreak ();
1937 mono_debugger_agent_thread_interrupt (NULL, NULL);
1939 #endif /* HOST_WIN32 */
1942 * reset_native_thread_suspend_state:
1944 * Reset the suspended flag on native threads
1946 static void
1947 reset_native_thread_suspend_state (gpointer key, gpointer value, gpointer user_data)
1949 DebuggerTlsData *tls = value;
1951 if (!tls->really_suspended && tls->suspended)
1952 tls->suspended = FALSE;
1956 * notify_thread:
1958 * Notify a thread that it needs to suspend.
1960 static void
1961 notify_thread (gpointer key, gpointer value, gpointer user_data)
1963 MonoInternalThread *thread = key;
1964 DebuggerTlsData *tls = value;
1965 gsize tid = thread->tid;
1967 if (GetCurrentThreadId () == tid || tls->terminated)
1968 return;
1970 DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
1973 * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
1974 * guarantee the signal handler will be called that many times. Instead of tracking
1975 * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
1976 * has been requested that hasn't been handled yet, otherwise we can have threads
1977 * refuse to die when VM_EXIT is called
1979 #if defined(__APPLE__)
1980 if (InterlockedCompareExchange (&tls->interrupt_count, 1, 0) == 1)
1981 return;
1982 #else
1984 * Maybe we could use the normal interrupt infrastructure, but that does a lot
1985 * of things like breaking waits etc. which we don't want.
1987 InterlockedIncrement (&tls->interrupt_count);
1988 #endif
1990 /* This is _not_ equivalent to ves_icall_System_Threading_Thread_Abort () */
1991 #ifdef HOST_WIN32
1992 QueueUserAPC (notify_thread_apc, thread->handle, NULL);
1993 #else
1994 pthread_kill ((pthread_t) tid, mono_thread_get_abort_signal ());
1995 #endif
1998 static void
1999 process_suspend (DebuggerTlsData *tls, MonoContext *ctx)
2001 guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
2002 MonoJitInfo *ji;
2004 if (debugger_thread_id == GetCurrentThreadId ())
2005 return;
2007 /* Prevent races with mono_debugger_agent_thread_interrupt () */
2008 if (suspend_count - tls->resume_count > 0)
2009 tls->suspending = TRUE;
2011 DEBUG(1, fprintf (log_file, "[%p] Received single step event for suspending.\n", (gpointer)GetCurrentThreadId ()));
2013 if (suspend_count - tls->resume_count == 0) {
2015 * We are executing a single threaded invoke but the single step for
2016 * suspending is still active.
2017 * FIXME: This slows down single threaded invokes.
2019 DEBUG(1, fprintf (log_file, "[%p] Ignored during single threaded invoke.\n", (gpointer)GetCurrentThreadId ()));
2020 return;
2023 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
2025 /* Can't suspend in these methods */
2026 if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
2027 return;
2029 save_thread_context (ctx);
2031 suspend_current ();
2035 * suspend_vm:
2037 * Increase the suspend count of the VM. While the suspend count is greater
2038 * than 0, runtime threads are suspended at certain points during execution.
2040 static void
2041 suspend_vm (void)
2043 mono_loader_lock ();
2045 mono_mutex_lock (&suspend_mutex);
2047 suspend_count ++;
2049 DEBUG(1, fprintf (log_file, "[%p] Suspending vm...\n", (gpointer)GetCurrentThreadId ()));
2051 if (suspend_count == 1) {
2052 // FIXME: Is it safe to call this inside the lock ?
2053 start_single_stepping ();
2054 mono_g_hash_table_foreach (thread_to_tls, notify_thread, NULL);
2057 mono_mutex_unlock (&suspend_mutex);
2059 mono_loader_unlock ();
2063 * resume_vm:
2065 * Decrease the suspend count of the VM. If the count reaches 0, runtime threads
2066 * are resumed.
2068 static void
2069 resume_vm (void)
2071 int err;
2073 g_assert (debugger_thread_id == GetCurrentThreadId ());
2075 mono_loader_lock ();
2077 mono_mutex_lock (&suspend_mutex);
2079 g_assert (suspend_count > 0);
2080 suspend_count --;
2082 DEBUG(1, fprintf (log_file, "[%p] Resuming vm...\n", (gpointer)GetCurrentThreadId ()));
2084 if (suspend_count == 0) {
2085 // FIXME: Is it safe to call this inside the lock ?
2086 stop_single_stepping ();
2087 mono_g_hash_table_foreach (thread_to_tls, reset_native_thread_suspend_state, NULL);
2090 /* Signal this even when suspend_count > 0, since some threads might have resume_count > 0 */
2091 err = mono_cond_broadcast (&suspend_cond);
2092 g_assert (err == 0);
2094 mono_mutex_unlock (&suspend_mutex);
2095 //g_assert (err == 0);
2097 mono_loader_unlock ();
2101 * resume_thread:
2103 * Resume just one thread.
2105 static void
2106 resume_thread (MonoInternalThread *thread)
2108 int err;
2109 DebuggerTlsData *tls;
2111 g_assert (debugger_thread_id == GetCurrentThreadId ());
2113 mono_loader_lock ();
2115 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
2116 g_assert (tls);
2118 mono_mutex_lock (&suspend_mutex);
2120 g_assert (suspend_count > 0);
2122 DEBUG(1, fprintf (log_file, "[%p] Resuming thread...\n", (gpointer)(gssize)thread->tid));
2124 tls->resume_count += suspend_count;
2127 * Signal suspend_count without decreasing suspend_count, the threads will wake up
2128 * but only the one whose resume_count field is > 0 will be resumed.
2130 err = mono_cond_broadcast (&suspend_cond);
2131 g_assert (err == 0);
2133 mono_mutex_unlock (&suspend_mutex);
2134 //g_assert (err == 0);
2136 mono_loader_unlock ();
2139 static void
2140 invalidate_frames (DebuggerTlsData *tls)
2142 int i;
2144 if (!tls)
2145 tls = TlsGetValue (debugger_tls_id);
2146 g_assert (tls);
2148 for (i = 0; i < tls->frame_count; ++i) {
2149 if (tls->frames [i]->jit)
2150 mono_debug_free_method_jit_info (tls->frames [i]->jit);
2151 g_free (tls->frames [i]);
2153 g_free (tls->frames);
2154 tls->frame_count = 0;
2155 tls->frames = NULL;
2159 * suspend_current:
2161 * Suspend the current thread until the runtime is resumed. If the thread has a
2162 * pending invoke, then the invoke is executed before this function returns.
2164 static void
2165 suspend_current (void)
2167 int err;
2168 DebuggerTlsData *tls;
2170 g_assert (debugger_thread_id != GetCurrentThreadId ());
2172 if (mono_loader_lock_is_owned_by_self ()) {
2174 * If we own the loader mutex, can't suspend until we release it, since the
2175 * whole runtime can deadlock otherwise.
2177 return;
2180 tls = TlsGetValue (debugger_tls_id);
2181 g_assert (tls);
2183 mono_mutex_lock (&suspend_mutex);
2185 tls->suspending = FALSE;
2186 tls->really_suspended = TRUE;
2188 if (!tls->suspended) {
2189 tls->suspended = TRUE;
2190 MONO_SEM_POST (&suspend_sem);
2193 DEBUG(1, fprintf (log_file, "[%p] Suspended.\n", (gpointer)GetCurrentThreadId ()));
2195 while (suspend_count - tls->resume_count > 0) {
2196 #ifdef HOST_WIN32
2197 if (WAIT_TIMEOUT == WaitForSingleObject(suspend_cond, 0))
2199 mono_mutex_unlock (&suspend_mutex);
2200 Sleep(0);
2201 mono_mutex_lock (&suspend_mutex);
2203 else
2206 #else
2207 err = mono_cond_wait (&suspend_cond, &suspend_mutex);
2208 g_assert (err == 0);
2209 #endif
2212 tls->suspended = FALSE;
2213 tls->really_suspended = FALSE;
2215 threads_suspend_count --;
2217 mono_mutex_unlock (&suspend_mutex);
2219 DEBUG(1, fprintf (log_file, "[%p] Resumed.\n", (gpointer)GetCurrentThreadId ()));
2221 if (tls->pending_invoke) {
2222 /* Save the original context */
2223 tls->pending_invoke->has_ctx = TRUE;
2224 memcpy (&tls->pending_invoke->ctx, &tls->ctx, sizeof (MonoContext));
2226 invoke_method ();
2229 /* The frame info becomes invalid after a resume */
2230 tls->has_context = FALSE;
2231 tls->has_async_ctx = FALSE;
2232 invalidate_frames (NULL);
2235 static void
2236 count_thread (gpointer key, gpointer value, gpointer user_data)
2238 DebuggerTlsData *tls = value;
2240 if (!tls->suspended && !tls->terminated)
2241 *(int*)user_data = *(int*)user_data + 1;
2244 static int
2245 count_threads_to_wait_for (void)
2247 int count = 0;
2249 mono_loader_lock ();
2250 mono_g_hash_table_foreach (thread_to_tls, count_thread, &count);
2251 mono_loader_unlock ();
2253 return count;
2257 * wait_for_suspend:
2259 * Wait until the runtime is completely suspended.
2261 static void
2262 wait_for_suspend (void)
2264 int nthreads, nwait, err;
2265 gboolean waited = FALSE;
2267 // FIXME: Threads starting/stopping ?
2268 mono_loader_lock ();
2269 nthreads = mono_g_hash_table_size (thread_to_tls);
2270 mono_loader_unlock ();
2272 while (TRUE) {
2273 nwait = count_threads_to_wait_for ();
2274 if (nwait) {
2275 DEBUG(1, fprintf (log_file, "Waiting for %d(%d) threads to suspend...\n", nwait, nthreads));
2276 err = MONO_SEM_WAIT (&suspend_sem);
2277 g_assert (err == 0);
2278 waited = TRUE;
2279 } else {
2280 break;
2284 if (waited)
2285 DEBUG(1, fprintf (log_file, "%d threads suspended.\n", nthreads));
2289 * is_suspended:
2291 * Return whenever the runtime is suspended.
2293 static gboolean
2294 is_suspended (void)
2296 return count_threads_to_wait_for () == 0;
2300 * find_seq_point_for_native_offset:
2302 * Find the sequence point corresponding to the native offset NATIVE_OFFSET, which
2303 * should be the location of a sequence point.
2305 static SeqPoint*
2306 find_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info)
2308 MonoSeqPointInfo *seq_points;
2309 int i;
2311 mono_domain_lock (domain);
2312 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
2313 mono_domain_unlock (domain);
2314 g_assert (seq_points);
2316 *info = seq_points;
2318 for (i = 0; i < seq_points->len; ++i) {
2319 if (seq_points->seq_points [i].native_offset == native_offset)
2320 return &seq_points->seq_points [i];
2323 return NULL;
2327 * find_seq_point:
2329 * Find the sequence point corresponding to the IL offset IL_OFFSET, which
2330 * should be the location of a sequence point.
2332 static SeqPoint*
2333 find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info)
2335 MonoSeqPointInfo *seq_points;
2336 int i;
2338 mono_domain_lock (domain);
2339 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
2340 mono_domain_unlock (domain);
2341 g_assert (seq_points);
2343 *info = seq_points;
2345 for (i = 0; i < seq_points->len; ++i) {
2346 if (seq_points->seq_points [i].il_offset == il_offset)
2347 return &seq_points->seq_points [i];
2350 return NULL;
2354 * compute_il_offset:
2356 * Compute the IL offset corresponding to NATIVE_OFFSET, which should be
2357 * a location of a sequence point.
2358 * We use this function instead of mono_debug_il_offset_from_address () etc,
2359 * which doesn't seem to work in a lot of cases.
2361 static gint32
2362 compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
2364 MonoSeqPointInfo *seq_points;
2365 int i, last_il_offset, seq_il_offset, seq_native_offset;
2367 mono_domain_lock (domain);
2368 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
2369 mono_domain_unlock (domain);
2370 g_assert (seq_points);
2372 last_il_offset = -1;
2374 /* Find the sequence point */
2375 for (i = 0; i < seq_points->len; ++i) {
2376 seq_il_offset = seq_points->seq_points [i].il_offset;
2377 seq_native_offset = seq_points->seq_points [i].native_offset;
2379 if (seq_native_offset > native_offset)
2380 break;
2381 last_il_offset = seq_il_offset;
2384 return last_il_offset;
2387 typedef struct {
2388 DebuggerTlsData *tls;
2389 GSList *frames;
2390 } ComputeFramesUserData;
2392 static gboolean
2393 process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
2395 ComputeFramesUserData *ud = user_data;
2396 StackFrame *frame;
2397 MonoMethod *method;
2399 if (info->type != FRAME_TYPE_MANAGED) {
2400 if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) {
2401 /* Mark the last frame as an invoke frame */
2402 if (ud->frames)
2403 ((StackFrame*)g_slist_last (ud->frames)->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE;
2405 return FALSE;
2408 if (info->ji)
2409 method = info->ji->method;
2410 else
2411 method = info->method;
2413 if (!method || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD))
2414 return FALSE;
2416 if (info->il_offset == -1) {
2417 /* Can't use compute_il_offset () since ip doesn't point precisely at at a seq point */
2418 info->il_offset = mono_debug_il_offset_from_address (method, info->domain, info->native_offset);
2421 DEBUG (1, fprintf (log_file, "\tFrame: %s %d %d %d\n", mono_method_full_name (method, TRUE), info->native_offset, info->il_offset, info->managed));
2423 if (!info->managed && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) {
2425 * mono_arch_find_jit_info () returns the context stored in the LMF for
2426 * native frames, but it should unwind once. This is why we have duplicate
2427 * frames on the stack sometimes.
2428 * !managed also seems to be set for dynamic methods.
2430 return FALSE;
2433 frame = g_new0 (StackFrame, 1);
2434 frame->method = method;
2435 frame->il_offset = info->il_offset;
2436 if (ctx) {
2437 frame->ctx = *ctx;
2438 frame->has_ctx = TRUE;
2440 frame->domain = info->domain;
2442 ud->frames = g_slist_append (ud->frames, frame);
2444 return FALSE;
2447 static void
2448 compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
2450 ComputeFramesUserData user_data;
2451 GSList *tmp;
2452 int i, findex, new_frame_count;
2453 StackFrame **new_frames, *f;
2455 // FIXME: Locking on tls
2456 if (tls->frames && tls->frames_up_to_date)
2457 return;
2459 DEBUG(1, fprintf (log_file, "Frames for %p(tid=%lx):\n", thread, (glong)thread->tid));
2461 user_data.tls = tls;
2462 user_data.frames = NULL;
2463 if (tls->terminated) {
2464 tls->frame_count = 0;
2465 return;
2466 } if (!tls->really_suspended && tls->has_async_ctx) {
2467 /* Have to use the state saved by the signal handler */
2468 process_frame (&tls->async_last_frame, NULL, &user_data);
2469 mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->async_ctx, FALSE, thread, tls->async_lmf, &user_data);
2470 } else if (tls->has_context) {
2471 mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->ctx, FALSE, thread, tls->lmf, &user_data);
2472 } else {
2473 // FIXME:
2474 tls->frame_count = 0;
2475 return;
2478 new_frame_count = g_slist_length (user_data.frames);
2479 new_frames = g_new0 (StackFrame*, new_frame_count);
2480 findex = 0;
2481 for (tmp = user_data.frames; tmp; tmp = tmp->next) {
2482 f = tmp->data;
2485 * Reuse the id for already existing stack frames, so invokes don't invalidate
2486 * the still valid stack frames.
2488 for (i = 0; i < tls->frame_count; ++i) {
2489 if (MONO_CONTEXT_GET_SP (&tls->frames [i]->ctx) == MONO_CONTEXT_GET_SP (&f->ctx)) {
2490 f->id = tls->frames [i]->id;
2491 break;
2495 if (i >= tls->frame_count)
2496 f->id = InterlockedIncrement (&frame_id);
2498 new_frames [findex ++] = f;
2501 g_slist_free (user_data.frames);
2503 invalidate_frames (tls);
2505 tls->frames = new_frames;
2506 tls->frame_count = new_frame_count;
2507 tls->frames_up_to_date = TRUE;
2511 * EVENT HANDLING
2515 * create_event_list:
2517 * Return a list of event request ids matching EVENT, starting from REQS, which
2518 * can be NULL to include all event requests. Set SUSPEND_POLICY to the suspend
2519 * policy.
2520 * We return request ids, instead of requests, to simplify threading, since
2521 * requests could be deleted anytime when the loader lock is not held.
2522 * LOCKING: Assumes the loader lock is held.
2524 static GSList*
2525 create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo *ei, int *suspend_policy)
2527 int i, j;
2528 GSList *events = NULL;
2530 *suspend_policy = SUSPEND_POLICY_NONE;
2532 if (!reqs)
2533 reqs = event_requests;
2535 if (!reqs)
2536 return NULL;
2538 for (i = 0; i < reqs->len; ++i) {
2539 EventRequest *req = g_ptr_array_index (reqs, i);
2540 if (req->event_kind == event) {
2541 gboolean filtered = FALSE;
2543 /* Apply filters */
2544 for (j = 0; j < req->nmodifiers; ++j) {
2545 Modifier *mod = &req->modifiers [j];
2547 if (mod->kind == MOD_KIND_COUNT) {
2548 filtered = TRUE;
2549 if (mod->data.count > 0) {
2550 if (mod->data.count > 0) {
2551 mod->data.count --;
2552 if (mod->data.count == 0)
2553 filtered = FALSE;
2556 } else if (mod->kind == MOD_KIND_THREAD_ONLY) {
2557 if (mod->data.thread != mono_thread_internal_current ())
2558 filtered = TRUE;
2559 } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && ei) {
2560 if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass))
2561 filtered = TRUE;
2562 if (ei->caught && !mod->caught)
2563 filtered = TRUE;
2564 if (!ei->caught && !mod->uncaught)
2565 filtered = TRUE;
2566 } else if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && ji) {
2567 int k;
2568 gboolean found = FALSE;
2569 MonoAssembly **assemblies = mod->data.assemblies;
2571 if (assemblies) {
2572 for (k = 0; assemblies [k]; ++k)
2573 if (assemblies [k] == ji->method->klass->image->assembly)
2574 found = TRUE;
2576 if (!found)
2577 filtered = TRUE;
2581 if (!filtered) {
2582 *suspend_policy = MAX (*suspend_policy, req->suspend_policy);
2583 events = g_slist_append (events, GINT_TO_POINTER (req->id));
2588 /* Send a VM START/DEATH event by default */
2589 if (event == EVENT_KIND_VM_START)
2590 events = g_slist_append (events, GINT_TO_POINTER (0));
2591 if (event == EVENT_KIND_VM_DEATH)
2592 events = g_slist_append (events, GINT_TO_POINTER (0));
2594 return events;
2597 static G_GNUC_UNUSED const char*
2598 event_to_string (EventKind event)
2600 switch (event) {
2601 case EVENT_KIND_VM_START: return "VM_START";
2602 case EVENT_KIND_VM_DEATH: return "VM_DEATH";
2603 case EVENT_KIND_THREAD_START: return "THREAD_START";
2604 case EVENT_KIND_THREAD_DEATH: return "THREAD_DEATH";
2605 case EVENT_KIND_APPDOMAIN_CREATE: return "APPDOMAIN_CREATE";
2606 case EVENT_KIND_APPDOMAIN_UNLOAD: return "APPDOMAIN_UNLOAD";
2607 case EVENT_KIND_METHOD_ENTRY: return "METHOD_ENTRY";
2608 case EVENT_KIND_METHOD_EXIT: return "METHOD_EXIT";
2609 case EVENT_KIND_ASSEMBLY_LOAD: return "ASSEMBLY_LOAD";
2610 case EVENT_KIND_ASSEMBLY_UNLOAD: return "ASSEMBLY_UNLOAD";
2611 case EVENT_KIND_BREAKPOINT: return "BREAKPOINT";
2612 case EVENT_KIND_STEP: return "STEP";
2613 case EVENT_KIND_TYPE_LOAD: return "TYPE_LOAD";
2614 case EVENT_KIND_EXCEPTION: return "EXCEPTION";
2615 default:
2616 g_assert_not_reached ();
2621 * process_event:
2623 * Send an event to the client, suspending the vm if needed.
2624 * LOCKING: Since this can suspend the calling thread, no locks should be held
2625 * by the caller.
2626 * The EVENTS list is freed by this function.
2628 static void
2629 process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx, GSList *events, int suspend_policy)
2631 Buffer buf;
2632 GSList *l;
2633 MonoDomain *domain = mono_domain_get ();
2634 MonoThread *thread;
2636 if (!inited)
2637 return;
2639 if (!vm_start_event_sent && event != EVENT_KIND_VM_START)
2640 // FIXME: We miss those events
2641 return;
2643 if (vm_death_event_sent)
2644 return;
2646 if (mono_runtime_is_shutting_down () && event != EVENT_KIND_VM_DEATH)
2647 return;
2649 if (disconnected)
2650 return;
2652 if (events == NULL)
2653 return;
2655 if (debugger_thread_id == GetCurrentThreadId () && event != EVENT_KIND_VM_DEATH)
2656 // FIXME: Send these with a NULL thread, don't suspend the current thread
2657 return;
2659 buffer_init (&buf, 128);
2660 buffer_add_byte (&buf, suspend_policy);
2661 buffer_add_int (&buf, g_slist_length (events)); // n of events
2663 for (l = events; l; l = l->next) {
2664 buffer_add_byte (&buf, event); // event kind
2665 buffer_add_int (&buf, GPOINTER_TO_INT (l->data)); // request id
2667 thread = mono_thread_current ();
2669 if (event == EVENT_KIND_VM_START)
2670 thread = arg;
2671 else if (event == EVENT_KIND_THREAD_START)
2672 g_assert (mono_thread_internal_current () == arg);
2674 buffer_add_objid (&buf, (MonoObject*)thread); // thread
2676 switch (event) {
2677 case EVENT_KIND_THREAD_START:
2678 case EVENT_KIND_THREAD_DEATH:
2679 break;
2680 case EVENT_KIND_APPDOMAIN_CREATE:
2681 case EVENT_KIND_APPDOMAIN_UNLOAD:
2682 buffer_add_domainid (&buf, arg);
2683 break;
2684 case EVENT_KIND_METHOD_ENTRY:
2685 case EVENT_KIND_METHOD_EXIT:
2686 buffer_add_methodid (&buf, domain, arg);
2687 break;
2688 case EVENT_KIND_ASSEMBLY_LOAD:
2689 case EVENT_KIND_ASSEMBLY_UNLOAD:
2690 buffer_add_assemblyid (&buf, domain, arg);
2691 break;
2692 case EVENT_KIND_TYPE_LOAD:
2693 buffer_add_typeid (&buf, domain, arg);
2694 break;
2695 case EVENT_KIND_BREAKPOINT:
2696 case EVENT_KIND_STEP:
2697 buffer_add_methodid (&buf, domain, arg);
2698 buffer_add_long (&buf, il_offset);
2699 break;
2700 case EVENT_KIND_VM_START:
2701 buffer_add_domainid (&buf, mono_get_root_domain ());
2702 break;
2703 case EVENT_KIND_VM_DEATH:
2704 break;
2705 case EVENT_KIND_EXCEPTION: {
2706 EventInfo *ei = arg;
2707 buffer_add_objid (&buf, ei->exc);
2708 break;
2710 default:
2711 g_assert_not_reached ();
2715 if (event == EVENT_KIND_VM_START) {
2716 suspend_policy = agent_config.suspend ? SUSPEND_POLICY_ALL : SUSPEND_POLICY_NONE;
2717 start_debugger_thread ();
2720 if (event == EVENT_KIND_VM_DEATH) {
2721 vm_death_event_sent = TRUE;
2723 suspend_policy = SUSPEND_POLICY_NONE;
2726 if (mono_runtime_is_shutting_down ())
2727 suspend_policy = SUSPEND_POLICY_NONE;
2729 if (suspend_policy != SUSPEND_POLICY_NONE) {
2731 * Save the thread context and start suspending before sending the packet,
2732 * since we could be receiving the resume request before send_packet ()
2733 * returns.
2735 save_thread_context (ctx);
2736 suspend_vm ();
2739 send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
2741 g_slist_free (events);
2742 events = NULL;
2744 if (event == EVENT_KIND_VM_START)
2745 vm_start_event_sent = TRUE;
2747 DEBUG (1, fprintf (log_file, "[%p] Sent event %s, suspend=%d.\n", (gpointer)GetCurrentThreadId (), event_to_string (event), suspend_policy));
2749 buffer_free (&buf);
2751 switch (suspend_policy) {
2752 case SUSPEND_POLICY_NONE:
2753 break;
2754 case SUSPEND_POLICY_ALL:
2755 suspend_current ();
2756 break;
2757 case SUSPEND_POLICY_EVENT_THREAD:
2758 NOT_IMPLEMENTED;
2759 break;
2760 default:
2761 g_assert_not_reached ();
2765 static void
2766 process_profiler_event (EventKind event, gpointer arg)
2768 int suspend_policy;
2769 GSList *events;
2771 mono_loader_lock ();
2772 events = create_event_list (event, NULL, NULL, NULL, &suspend_policy);
2773 mono_loader_unlock ();
2775 process_event (event, arg, 0, NULL, events, suspend_policy);
2778 static void
2779 runtime_initialized (MonoProfiler *prof)
2781 process_profiler_event (EVENT_KIND_VM_START, mono_thread_current ());
2784 static void
2785 runtime_shutdown (MonoProfiler *prof)
2787 process_profiler_event (EVENT_KIND_VM_DEATH, mono_thread_current ());
2789 mono_debugger_agent_cleanup ();
2792 static void
2793 thread_startup (MonoProfiler *prof, intptr_t tid)
2795 MonoInternalThread *thread = mono_thread_internal_current ();
2796 MonoInternalThread *old_thread;
2797 DebuggerTlsData *tls;
2799 if (tid == debugger_thread_id)
2800 return;
2802 g_assert (thread->tid == tid);
2804 mono_loader_lock ();
2805 old_thread = mono_g_hash_table_lookup (tid_to_thread, (gpointer)tid);
2806 mono_loader_unlock ();
2807 if (old_thread) {
2808 if (thread == old_thread) {
2810 * For some reason, thread_startup () might be called for the same thread
2811 * multiple times (attach ?).
2813 DEBUG (1, fprintf (log_file, "[%p] thread_start () called multiple times for %p, ignored.\n", (gpointer)tid, (gpointer)tid));
2814 return;
2815 } else {
2817 * thread_end () might not be called for some threads, and the tid could
2818 * get reused.
2820 DEBUG (1, fprintf (log_file, "[%p] Removing stale data for tid %p.\n", (gpointer)tid, (gpointer)tid));
2821 mono_loader_lock ();
2822 mono_g_hash_table_remove (thread_to_tls, old_thread);
2823 mono_g_hash_table_remove (tid_to_thread, (gpointer)tid);
2824 mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
2825 mono_loader_unlock ();
2829 tls = TlsGetValue (debugger_tls_id);
2830 g_assert (!tls);
2831 // FIXME: Free this somewhere
2832 tls = g_new0 (DebuggerTlsData, 1);
2833 tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL);
2834 MONO_GC_REGISTER_ROOT (tls->thread);
2835 tls->thread = thread;
2836 TlsSetValue (debugger_tls_id, tls);
2838 DEBUG (1, fprintf (log_file, "[%p] Thread started, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls));
2840 mono_loader_lock ();
2841 mono_g_hash_table_insert (thread_to_tls, thread, tls);
2842 mono_g_hash_table_insert (tid_to_thread, (gpointer)tid, thread);
2843 mono_g_hash_table_insert (tid_to_thread_obj, (gpointer)tid, mono_thread_current ());
2844 mono_loader_unlock ();
2846 process_profiler_event (EVENT_KIND_THREAD_START, thread);
2849 * suspend_vm () could have missed this thread, so wait for a resume.
2851 suspend_current ();
2854 static void
2855 thread_end (MonoProfiler *prof, intptr_t tid)
2857 MonoInternalThread *thread;
2858 DebuggerTlsData *tls = NULL;
2860 mono_loader_lock ();
2861 thread = mono_g_hash_table_lookup (tid_to_thread, (gpointer)tid);
2862 if (thread) {
2863 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
2864 /* FIXME: Maybe we need to free this instead, but some code can't handle that */
2865 tls->terminated = TRUE;
2866 mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
2867 /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */
2868 MONO_GC_UNREGISTER_ROOT (tls->thread);
2869 tls->thread = NULL;
2871 mono_loader_unlock ();
2873 /* We might be called for threads started before we registered the start callback */
2874 if (thread) {
2875 DEBUG (1, fprintf (log_file, "[%p] Thread terminated, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls));
2876 process_profiler_event (EVENT_KIND_THREAD_DEATH, thread);
2880 static void
2881 appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result)
2883 process_profiler_event (EVENT_KIND_APPDOMAIN_CREATE, domain);
2886 static void
2887 appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
2889 /* Invalidate each thread's frame stack */
2890 mono_g_hash_table_foreach (thread_to_tls, invalidate_each_thread, NULL);
2891 process_profiler_event (EVENT_KIND_APPDOMAIN_UNLOAD, domain);
2895 * invalidate_each_thread:
2897 * A GHFunc to invalidate frames.
2898 * value must be a DebuggerTlsData*
2900 static void
2901 invalidate_each_thread (gpointer key, gpointer value, gpointer user_data)
2903 invalidate_frames (value);
2906 static void
2907 assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result)
2909 /* Sent later in jit_end () */
2910 mono_loader_lock ();
2911 g_ptr_array_add (pending_assembly_loads, assembly);
2912 mono_loader_unlock ();
2915 static void
2916 assembly_unload (MonoProfiler *prof, MonoAssembly *assembly)
2918 process_profiler_event (EVENT_KIND_ASSEMBLY_UNLOAD, assembly);
2920 clear_event_requests_for_assembly (assembly);
2923 static void
2924 start_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
2926 #if defined(HOST_WIN32) && !defined(__GNUC__)
2927 gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
2928 #else
2929 gpointer stackptr = __builtin_frame_address (1);
2930 #endif
2931 MonoInternalThread *thread = mono_thread_internal_current ();
2932 DebuggerTlsData *tls;
2934 mono_loader_lock ();
2936 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
2937 /* Could be the debugger thread with assembly/type load hooks */
2938 if (tls)
2939 tls->invoke_addr = stackptr;
2941 mono_loader_unlock ();
2944 static void
2945 end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
2947 int i;
2948 #if defined(HOST_WIN32) && !defined(__GNUC__)
2949 gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
2950 #else
2951 gpointer stackptr = __builtin_frame_address (1);
2952 #endif
2954 if (ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
2955 return;
2958 * We need to stop single stepping when exiting a runtime invoke, since if it is
2959 * a step out, it may return to native code, and thus never end.
2961 mono_loader_lock ();
2962 ss_invoke_addr = NULL;
2964 for (i = 0; i < event_requests->len; ++i) {
2965 EventRequest *req = g_ptr_array_index (event_requests, i);
2967 if (req->event_kind == EVENT_KIND_STEP) {
2968 ss_destroy (req->info);
2969 g_ptr_array_remove_index_fast (event_requests, i);
2970 g_free (req);
2971 break;
2974 mono_loader_unlock ();
2977 static void
2978 jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
2981 * We emit type load events when the first method of the type is JITted,
2982 * since the class load profiler callbacks might be called with the
2983 * loader lock held. They could also occur in the debugger thread.
2984 * Same for assembly load events.
2986 gboolean type_load = FALSE;
2988 while (TRUE) {
2989 MonoAssembly *assembly = NULL;
2991 // FIXME: Maybe store this in TLS so the thread of the event is correct ?
2992 mono_loader_lock ();
2993 if (pending_assembly_loads->len > 0) {
2994 assembly = g_ptr_array_index (pending_assembly_loads, 0);
2995 g_ptr_array_remove_index (pending_assembly_loads, 0);
2997 mono_loader_unlock ();
2999 if (assembly)
3000 process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, assembly);
3001 else
3002 break;
3005 mono_loader_lock ();
3006 if (!g_hash_table_lookup (loaded_classes, method->klass)) {
3007 type_load = TRUE;
3008 g_hash_table_insert (loaded_classes, method->klass, method->klass);
3010 mono_loader_unlock ();
3011 if (type_load)
3012 process_profiler_event (EVENT_KIND_TYPE_LOAD, method->klass);
3014 if (!result)
3015 add_pending_breakpoints (method, jinfo);
3019 * BREAKPOINTS/SINGLE STEPPING
3023 * Contains information about an inserted breakpoint.
3025 typedef struct {
3026 long il_offset, native_offset;
3027 guint8 *ip;
3028 MonoJitInfo *ji;
3029 } BreakpointInstance;
3032 * Contains generic information about a breakpoint.
3034 typedef struct {
3036 * The method where the breakpoint is placed. Can be NULL in which case it
3037 * is inserted into every method. This is used to implement method entry/
3038 * exit events. Can be a generic method definition, in which case the
3039 * breakpoint is inserted into every instance.
3041 MonoMethod *method;
3042 long il_offset;
3043 EventRequest *req;
3045 * A list of BreakpointInstance structures describing where the breakpoint
3046 * was inserted. There could be more than one because of
3047 * generics/appdomains/method entry/exit.
3049 GPtrArray *children;
3050 } MonoBreakpoint;
3052 /* List of breakpoints */
3053 static GPtrArray *breakpoints;
3054 /* Maps breakpoint locations to the number of breakpoints at that location */
3055 static GHashTable *bp_locs;
3057 static void
3058 breakpoints_init (void)
3060 breakpoints = g_ptr_array_new ();
3061 bp_locs = g_hash_table_new (NULL, NULL);
3064 static void
3065 breakpoints_cleanup (void)
3067 int i;
3069 mono_loader_lock ();
3071 for (i = 0; i < breakpoints->len; ++i)
3072 g_free (g_ptr_array_index (breakpoints, i));
3074 g_ptr_array_free (breakpoints, TRUE);
3075 g_hash_table_destroy (bp_locs);
3077 mono_loader_unlock ();
3081 * insert_breakpoint:
3083 * Insert the breakpoint described by BP into the method described by
3084 * JI.
3086 static void
3087 insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
3089 int i, count;
3090 gint32 il_offset, native_offset;
3091 BreakpointInstance *inst;
3093 native_offset = 0;
3094 for (i = 0; i < seq_points->len; ++i) {
3095 il_offset = seq_points->seq_points [i].il_offset;
3096 native_offset = seq_points->seq_points [i].native_offset;
3098 if (il_offset == bp->il_offset)
3099 break;
3102 if (i == seq_points->len)
3103 /* Have to handle this somehow */
3104 NOT_IMPLEMENTED;
3106 inst = g_new0 (BreakpointInstance, 1);
3107 inst->native_offset = native_offset;
3108 inst->ip = (guint8*)ji->code_start + native_offset;
3109 inst->ji = ji;
3111 mono_loader_lock ();
3113 g_ptr_array_add (bp->children, inst);
3115 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, inst->ip));
3116 g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
3117 mono_loader_unlock ();
3119 if (count == 0) {
3120 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
3121 mono_arch_set_breakpoint (ji, inst->ip);
3122 #else
3123 NOT_IMPLEMENTED;
3124 #endif
3127 DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (ji->method, TRUE), (int)il_offset));
3130 static void
3131 remove_breakpoint (BreakpointInstance *inst)
3133 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
3134 int count;
3135 MonoJitInfo *ji = inst->ji;
3136 guint8 *ip = inst->ip;
3138 mono_loader_lock ();
3139 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, ip));
3140 g_hash_table_insert (bp_locs, ip, GINT_TO_POINTER (count - 1));
3141 mono_loader_unlock ();
3143 g_assert (count > 0);
3145 if (count == 1) {
3146 mono_arch_clear_breakpoint (ji, ip);
3148 #else
3149 NOT_IMPLEMENTED;
3150 #endif
3153 static inline gboolean
3154 bp_matches_method (MonoBreakpoint *bp, MonoMethod *method)
3156 return (!bp->method || method == bp->method || (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method));
3160 * add_pending_breakpoints:
3162 * Insert pending breakpoints into the newly JITted method METHOD.
3164 static void
3165 add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
3167 int i, j;
3168 MonoSeqPointInfo *seq_points;
3169 MonoDomain *domain;
3171 if (!breakpoints)
3172 return;
3174 domain = mono_domain_get ();
3176 mono_loader_lock ();
3178 for (i = 0; i < breakpoints->len; ++i) {
3179 MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
3180 gboolean found = FALSE;
3182 if (!bp_matches_method (bp, method))
3183 continue;
3185 for (j = 0; j < bp->children->len; ++j) {
3186 BreakpointInstance *inst = g_ptr_array_index (bp->children, j);
3188 if (inst->ji == ji)
3189 found = TRUE;
3192 if (!found) {
3193 mono_domain_lock (domain);
3194 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method);
3195 mono_domain_unlock (domain);
3196 if (!seq_points)
3197 /* Could be AOT code */
3198 continue;
3199 g_assert (seq_points);
3201 insert_breakpoint (seq_points, ji, bp);
3205 mono_loader_unlock ();
3208 static void
3209 set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp)
3211 gpointer code;
3212 MonoJitInfo *ji;
3214 code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
3215 if (!code) {
3216 /* Might be AOTed code */
3217 code = mono_aot_get_method (domain, method);
3218 g_assert (code);
3219 ji = mono_jit_info_table_find (domain, code);
3220 g_assert (ji);
3222 g_assert (code);
3224 insert_breakpoint (seq_points, ji, bp);
3227 static void
3228 set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data)
3230 MonoMethod *method = key;
3231 MonoSeqPointInfo *seq_points = value;
3232 MonoBreakpoint *bp = user_data;
3233 MonoDomain *domain = mono_domain_get ();
3235 if (bp_matches_method (bp, method))
3236 set_bp_in_method (domain, method, seq_points, bp);
3240 * set_breakpoint:
3242 * Set a breakpoint at IL_OFFSET in METHOD.
3243 * METHOD can be NULL, in which case a breakpoint is placed in all methods.
3244 * METHOD can also be a generic method definition, in which case a breakpoint
3245 * is placed in all instances of the method.
3247 static MonoBreakpoint*
3248 set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
3250 MonoDomain *domain;
3251 MonoBreakpoint *bp;
3253 // FIXME:
3254 // - suspend/resume the vm to prevent code patching problems
3255 // - multiple breakpoints on the same location
3256 // - dynamic methods
3257 // - races
3259 bp = g_new0 (MonoBreakpoint, 1);
3260 bp->method = method;
3261 bp->il_offset = il_offset;
3262 bp->req = req;
3263 bp->children = g_ptr_array_new ();
3265 DEBUG(1, fprintf (log_file, "[dbg] Setting %sbreakpoint at %s:0x%x.\n", (req->event_kind == EVENT_KIND_STEP) ? "single step " : "", method ? mono_method_full_name (method, TRUE) : "<all>", (int)il_offset));
3267 domain = mono_domain_get ();
3268 mono_domain_lock (domain);
3269 g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp);
3270 mono_domain_unlock (domain);
3272 mono_loader_lock ();
3273 g_ptr_array_add (breakpoints, bp);
3274 mono_loader_unlock ();
3276 return bp;
3279 static void
3280 clear_breakpoint (MonoBreakpoint *bp)
3282 int i;
3284 // FIXME: locking, races
3285 for (i = 0; i < bp->children->len; ++i) {
3286 BreakpointInstance *inst = g_ptr_array_index (bp->children, i);
3288 remove_breakpoint (inst);
3290 g_free (inst);
3293 mono_loader_lock ();
3294 g_ptr_array_remove (breakpoints, bp);
3295 mono_loader_unlock ();
3297 g_ptr_array_free (bp->children, TRUE);
3298 g_free (bp);
3301 static gboolean
3302 breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
3304 return bp->method && bp->method->klass->image->assembly == assembly;
3307 static void
3308 process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
3310 MonoJitInfo *ji;
3311 guint8 *orig_ip, *ip;
3312 int i, j, suspend_policy;
3313 guint32 native_offset;
3314 MonoBreakpoint *bp;
3315 BreakpointInstance *inst;
3316 GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs;
3317 GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL;
3318 EventKind kind = EVENT_KIND_BREAKPOINT;
3320 // FIXME: Speed this up
3322 orig_ip = ip = MONO_CONTEXT_GET_IP (ctx);
3323 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
3324 g_assert (ji);
3325 g_assert (ji->method);
3327 /* Compute the native offset of the breakpoint from the ip */
3328 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
3329 ip = mono_arch_get_ip_for_breakpoint (ji, ctx);
3330 native_offset = ip - (guint8*)ji->code_start;
3331 #else
3332 NOT_IMPLEMENTED;
3333 #endif
3336 * Skip the instruction causing the breakpoint signal.
3338 mono_arch_skip_breakpoint (ctx);
3340 if (ji->method->wrapper_type || tls->disable_breakpoints)
3341 return;
3343 bp_reqs = g_ptr_array_new ();
3344 ss_reqs = g_ptr_array_new ();
3345 ss_reqs_orig = g_ptr_array_new ();
3347 DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, native_offset));
3349 mono_loader_lock ();
3351 bp = NULL;
3352 for (i = 0; i < breakpoints->len; ++i) {
3353 bp = g_ptr_array_index (breakpoints, i);
3355 if (!bp->method)
3356 continue;
3358 for (j = 0; j < bp->children->len; ++j) {
3359 inst = g_ptr_array_index (bp->children, j);
3360 if (inst->ji == ji && inst->native_offset == native_offset) {
3361 if (bp->req->event_kind == EVENT_KIND_STEP) {
3362 g_ptr_array_add (ss_reqs_orig, bp->req);
3363 } else {
3364 g_ptr_array_add (bp_reqs, bp->req);
3369 if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) {
3370 MonoSeqPointInfo *seq_points;
3371 int seq_il_offset, seq_native_offset;
3372 MonoDomain *domain = mono_domain_get ();
3374 /* Maybe a method entry/exit event */
3375 mono_domain_lock (domain);
3376 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method);
3377 mono_domain_unlock (domain);
3378 if (!seq_points) {
3379 // FIXME: Generic sharing */
3380 mono_loader_unlock ();
3381 return;
3383 g_assert (seq_points);
3385 for (i = 0; i < seq_points->len; ++i) {
3386 seq_il_offset = seq_points->seq_points [i].il_offset;
3387 seq_native_offset = seq_points->seq_points [i].native_offset;
3389 if (native_offset == seq_native_offset) {
3390 if (seq_il_offset == METHOD_ENTRY_IL_OFFSET)
3391 kind = EVENT_KIND_METHOD_ENTRY;
3392 else if (seq_il_offset == METHOD_EXIT_IL_OFFSET)
3393 kind = EVENT_KIND_METHOD_EXIT;
3394 break;
3399 /* Process single step requests */
3400 for (i = 0; i < ss_reqs_orig->len; ++i) {
3401 EventRequest *req = g_ptr_array_index (ss_reqs_orig, i);
3402 SingleStepReq *ss_req = bp->req->info;
3403 gboolean hit = TRUE;
3404 MonoSeqPointInfo *info;
3405 SeqPoint *sp;
3407 sp = find_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
3408 g_assert (sp);
3410 if (ss_req->size == STEP_SIZE_LINE) {
3411 /* Have to check whenever a different source line was reached */
3412 MonoDebugMethodInfo *minfo;
3413 MonoDebugSourceLocation *loc = NULL;
3415 minfo = mono_debug_lookup_method (ji->method);
3417 if (minfo)
3418 loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
3420 if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line))
3421 /* Have to continue single stepping */
3422 hit = FALSE;
3424 if (loc) {
3425 ss_req->last_method = ji->method;
3426 ss_req->last_line = loc->row;
3427 mono_debug_free_source_location (loc);
3431 if (hit)
3432 g_ptr_array_add (ss_reqs, req);
3434 /* Start single stepping again from the current sequence point */
3435 ss_start (ss_req, ji->method, sp, info, ctx, NULL);
3438 if (ss_reqs->len > 0)
3439 ss_events = create_event_list (EVENT_KIND_STEP, ss_reqs, ji, NULL, &suspend_policy);
3440 if (bp_reqs->len > 0)
3441 bp_events = create_event_list (EVENT_KIND_BREAKPOINT, bp_reqs, ji, NULL, &suspend_policy);
3442 if (kind != EVENT_KIND_BREAKPOINT)
3443 enter_leave_events = create_event_list (kind, NULL, ji, NULL, &suspend_policy);
3445 mono_loader_unlock ();
3447 g_ptr_array_free (bp_reqs, TRUE);
3448 g_ptr_array_free (ss_reqs, TRUE);
3451 * FIXME: The first event will suspend, so the second will only be sent after the
3452 * resume.
3454 if (ss_events)
3455 process_event (EVENT_KIND_STEP, ji->method, 0, ctx, ss_events, suspend_policy);
3456 if (bp_events)
3457 process_event (kind, ji->method, 0, ctx, bp_events, suspend_policy);
3458 if (enter_leave_events)
3459 process_event (kind, ji->method, 0, ctx, enter_leave_events, suspend_policy);
3462 static void
3463 process_breakpoint (void)
3465 DebuggerTlsData *tls;
3466 MonoContext ctx;
3467 static void (*restore_context) (void *);
3469 if (!restore_context)
3470 restore_context = mono_get_restore_context ();
3472 tls = TlsGetValue (debugger_tls_id);
3473 memcpy (&ctx, &tls->handler_ctx, sizeof (MonoContext));
3475 process_breakpoint_inner (tls, &ctx);
3477 /* This is called when resuming from a signal handler, so it shouldn't return */
3478 restore_context (&ctx);
3479 g_assert_not_reached ();
3482 static void
3483 resume_from_signal_handler (void *sigctx, void *func)
3485 DebuggerTlsData *tls;
3486 MonoContext ctx;
3488 /* Save the original context in TLS */
3489 // FIXME: This might not work on an altstack ?
3490 tls = TlsGetValue (debugger_tls_id);
3491 g_assert (tls);
3493 // FIXME: MonoContext usually doesn't include the fp registers, so these are
3494 // clobbered by a single step/breakpoint event. If this turns out to be a problem,
3495 // clob:c could be added to op_seq_point.
3497 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
3498 memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
3499 MONO_CONTEXT_SET_IP (&ctx, func);
3500 mono_arch_monoctx_to_sigctx (&ctx, sigctx);
3502 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
3503 mono_ppc_set_func_into_sigctx (sigctx, func);
3504 #endif
3507 void
3508 mono_debugger_agent_breakpoint_hit (void *sigctx)
3511 * We are called from a signal handler, and running code there causes all kinds of
3512 * problems, like the original signal is disabled, libgc can't handle altstack, etc.
3513 * So set up the signal context to return to the real breakpoint handler function.
3516 resume_from_signal_handler (sigctx, process_breakpoint);
3519 static void
3520 process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
3522 MonoJitInfo *ji;
3523 guint8 *ip;
3524 GPtrArray *reqs;
3525 int il_offset, suspend_policy;
3526 MonoDomain *domain;
3527 GSList *events;
3529 // FIXME: Speed this up
3531 ip = MONO_CONTEXT_GET_IP (ctx);
3533 /* Skip the instruction causing the single step */
3534 mono_arch_skip_single_step (ctx);
3536 if (suspend_count > 0) {
3537 process_suspend (tls, ctx);
3538 return;
3541 if (!ss_req)
3542 // FIXME: A suspend race
3543 return;
3545 if (mono_thread_internal_current () != ss_req->thread)
3546 return;
3548 if (log_level > 0) {
3549 const char *depth = NULL;
3551 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
3553 switch (ss_req->depth) {
3554 case STEP_DEPTH_OVER:
3555 depth = "over";
3556 break;
3557 case STEP_DEPTH_OUT:
3558 depth = "out";
3559 break;
3560 case STEP_DEPTH_INTO:
3561 depth = "into";
3562 break;
3563 default:
3564 g_assert_not_reached ();
3567 DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_req->depth == STEP_DEPTH_OVER ? "over" : "out", mono_method_full_name (ji->method, TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
3571 * We implement step over/out by single stepping until we reach the same
3572 * frame/parent frame.
3573 * FIXME:
3574 * - this is slow
3575 * - stack growing upward
3576 * - localloc
3577 * - exceptions
3579 if (ss_req->depth != STEP_DEPTH_INTO) {
3580 if (ss_req->depth == STEP_DEPTH_OVER && MONO_CONTEXT_GET_SP (ctx) < ss_req->last_sp)
3581 return;
3582 if (ss_req->depth == STEP_DEPTH_OUT && MONO_CONTEXT_GET_SP (ctx) <= ss_req->last_sp)
3583 return;
3585 ss_req->last_sp = MONO_CONTEXT_GET_SP (ctx);
3588 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
3589 g_assert (ji);
3590 g_assert (ji->method);
3592 if (ji->method->wrapper_type && ji->method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
3593 return;
3596 * FIXME:
3597 * Stopping in memset makes half-initialized vtypes visible.
3598 * Stopping in memcpy makes half-copied vtypes visible.
3600 if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
3601 return;
3604 * The ip points to the instruction causing the single step event, convert it
3605 * to the offset stored in seq_points.
3607 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
3608 ip = mono_arch_get_ip_for_single_step (ji, ctx);
3609 #else
3610 g_assert_not_reached ();
3611 #endif
3614 * mono_debug_lookup_source_location () doesn't work for IL offset 0 for
3615 * example, so do things by hand.
3617 il_offset = compute_il_offset (domain, ji->method, (guint8*)ip - (guint8*)ji->code_start);
3619 if (il_offset == -1)
3620 return;
3622 if (ss_req->size == STEP_SIZE_LINE) {
3623 /* Step until a different source line is reached */
3624 MonoDebugMethodInfo *minfo;
3626 minfo = mono_debug_lookup_method (ji->method);
3628 if (minfo) {
3629 MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, il_offset);
3631 if (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line) {
3632 mono_debug_free_source_location (loc);
3633 return;
3635 if (!loc)
3637 * Step until we reach a location with line number info,
3638 * otherwise the client can't show a location.
3639 * This can happen for example with statics initialized inline
3640 * outside of a cctor.
3642 return;
3644 if (loc) {
3645 ss_req->last_method = ji->method;
3646 ss_req->last_line = loc->row;
3647 mono_debug_free_source_location (loc);
3652 // FIXME: Has to lock earlier
3654 reqs = g_ptr_array_new ();
3656 mono_loader_lock ();
3658 g_ptr_array_add (reqs, ss_req->req);
3660 events = create_event_list (EVENT_KIND_STEP, reqs, ji, NULL, &suspend_policy);
3662 g_ptr_array_free (reqs, TRUE);
3664 mono_loader_unlock ();
3666 process_event (EVENT_KIND_STEP, ji->method, il_offset, ctx, events, suspend_policy);
3669 static void
3670 process_single_step (void)
3672 DebuggerTlsData *tls;
3673 MonoContext ctx;
3674 static void (*restore_context) (void *);
3676 if (!restore_context)
3677 restore_context = mono_get_restore_context ();
3679 tls = TlsGetValue (debugger_tls_id);
3680 memcpy (&ctx, &tls->handler_ctx, sizeof (MonoContext));
3682 process_single_step_inner (tls, &ctx);
3684 /* This is called when resuming from a signal handler, so it shouldn't return */
3685 restore_context (&ctx);
3686 g_assert_not_reached ();
3690 * mono_debugger_agent_single_step_event:
3692 * Called from a signal handler to handle a single step event.
3694 void
3695 mono_debugger_agent_single_step_event (void *sigctx)
3697 /* Resume to process_single_step through the signal context */
3699 // FIXME: Since step out/over is implemented using step in, the step in case should
3700 // be as fast as possible. Move the relevant code from process_single_step_inner ()
3701 // here
3703 if (GetCurrentThreadId () == debugger_thread_id) {
3705 * This could happen despite our best effors when the runtime calls
3706 * assembly/type resolve hooks.
3707 * FIXME: Breakpoints too.
3709 MonoContext ctx;
3711 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
3712 mono_arch_skip_single_step (&ctx);
3713 mono_arch_monoctx_to_sigctx (&ctx, sigctx);
3714 return;
3717 resume_from_signal_handler (sigctx, process_single_step);
3721 * start_single_stepping:
3723 * Turn on single stepping. Can be called multiple times, for example,
3724 * by a single step event request + a suspend.
3726 static void
3727 start_single_stepping (void)
3729 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
3730 int val = InterlockedIncrement (&ss_count);
3732 if (val == 1)
3733 mono_arch_start_single_stepping ();
3735 if (ss_req != NULL && ss_invoke_addr == NULL) {
3736 DebuggerTlsData *tls;
3738 mono_loader_lock ();
3740 tls = mono_g_hash_table_lookup (thread_to_tls, ss_req->thread);
3741 ss_invoke_addr = tls->invoke_addr;
3743 mono_loader_unlock ();
3745 #else
3746 g_assert_not_reached ();
3747 #endif
3750 static void
3751 stop_single_stepping (void)
3753 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
3754 int val = InterlockedDecrement (&ss_count);
3756 if (val == 0)
3757 mono_arch_stop_single_stepping ();
3758 #else
3759 g_assert_not_reached ();
3760 #endif
3764 * ss_stop:
3766 * Stop the single stepping operation given by SS_REQ.
3768 static void
3769 ss_stop (SingleStepReq *ss_req)
3771 gboolean use_bps = FALSE;
3773 if (ss_req->bps) {
3774 GSList *l;
3776 use_bps = TRUE;
3778 for (l = ss_req->bps; l; l = l->next) {
3779 clear_breakpoint (l->data);
3781 g_slist_free (ss_req->bps);
3782 ss_req->bps = NULL;
3785 if (ss_req->global) {
3786 stop_single_stepping ();
3787 ss_req->global = FALSE;
3792 * ss_start:
3794 * Start the single stepping operation given by SS_REQ from the sequence point SP.
3796 static void
3797 ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls)
3799 gboolean use_bp = FALSE;
3800 int i, frame_index;
3801 SeqPoint *next_sp;
3802 MonoBreakpoint *bp;
3804 /* Stop the previous operation */
3805 ss_stop (ss_req);
3808 * Implement single stepping using breakpoints if possible.
3810 if (ss_req->depth == STEP_DEPTH_OVER) {
3811 frame_index = 1;
3813 * Find the first sequence point in the current or in a previous frame which
3814 * is not the last in its method.
3816 while (sp && sp->next_len == 0) {
3817 sp = NULL;
3818 if (tls && frame_index < tls->frame_count) {
3819 StackFrame *frame = tls->frames [frame_index];
3821 method = frame->method;
3822 if (frame->il_offset != -1) {
3823 sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info);
3825 frame_index ++;
3829 if (sp && sp->next_len > 0) {
3830 use_bp = TRUE;
3831 for (i = 0; i < sp->next_len; ++i) {
3832 next_sp = &info->seq_points [sp->next [i]];
3834 bp = set_breakpoint (method, next_sp->il_offset, ss_req->req);
3835 ss_req->bps = g_slist_append (ss_req->bps, bp);
3840 if (!ss_req->bps) {
3841 ss_req->global = TRUE;
3842 start_single_stepping ();
3843 } else {
3844 ss_req->global = FALSE;
3849 * Start single stepping of thread THREAD
3851 static ErrorCode
3852 ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
3854 DebuggerTlsData *tls;
3855 MonoSeqPointInfo *info;
3856 SeqPoint *sp = NULL;
3857 MonoMethod *method = NULL;
3859 if (suspend_count == 0)
3860 return ERR_NOT_SUSPENDED;
3862 wait_for_suspend ();
3864 // FIXME: Multiple requests
3865 if (ss_req) {
3866 DEBUG (0, printf ("Received a single step request while the previous one was still active.\n"));
3867 return ERR_NOT_IMPLEMENTED;
3870 ss_req = g_new0 (SingleStepReq, 1);
3871 ss_req->req = req;
3872 ss_req->thread = thread;
3873 ss_req->size = size;
3874 ss_req->depth = depth;
3875 req->info = ss_req;
3877 mono_loader_lock ();
3878 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
3879 mono_loader_unlock ();
3880 g_assert (tls);
3881 g_assert (tls->has_context);
3882 ss_req->start_sp = ss_req->last_sp = MONO_CONTEXT_GET_SP (&tls->ctx);
3884 if (ss_req->size == STEP_SIZE_LINE) {
3885 StackFrame *frame;
3886 MonoDebugMethodInfo *minfo;
3888 /* Compute the initial line info */
3889 compute_frame_info (thread, tls);
3891 g_assert (tls->frame_count);
3892 frame = tls->frames [0];
3894 ss_req->last_method = frame->method;
3895 ss_req->last_line = -1;
3897 minfo = mono_debug_lookup_method (frame->method);
3898 if (minfo && frame->il_offset != -1) {
3899 MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, frame->il_offset);
3901 if (loc) {
3902 ss_req->last_line = loc->row;
3903 g_free (loc);
3908 if (ss_req->depth == STEP_DEPTH_OVER) {
3909 StackFrame *frame;
3911 compute_frame_info (thread, tls);
3913 g_assert (tls->frame_count);
3914 frame = tls->frames [0];
3916 if (frame->il_offset != -1) {
3917 /* FIXME: Sort the table and use a binary search */
3918 sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info);
3919 g_assert (sp);
3920 method = frame->method;
3924 ss_start (ss_req, method, sp, info, NULL, tls);
3926 return 0;
3929 static void
3930 ss_destroy (SingleStepReq *req)
3932 // FIXME: Locking
3933 g_assert (ss_req == req);
3935 ss_stop (ss_req);
3937 g_free (ss_req);
3938 ss_req = NULL;
3941 void
3942 mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx,
3943 MonoContext *catch_ctx)
3945 int suspend_policy;
3946 GSList *events;
3947 MonoJitInfo *ji;
3948 EventInfo ei;
3950 if (thread_to_tls != NULL) {
3951 MonoInternalThread *thread = mono_thread_internal_current ();
3952 DebuggerTlsData *tls;
3954 mono_loader_lock ();
3955 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
3956 mono_loader_unlock ();
3958 if (tls && tls->abort_requested)
3959 return;
3962 memset (&ei, 0, sizeof (EventInfo));
3964 /* Just-In-Time debugging */
3965 if (!catch_ctx) {
3966 if (agent_config.onuncaught && !inited) {
3967 finish_agent_init (FALSE);
3970 * Send an unsolicited EXCEPTION event with a dummy request id.
3972 events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
3973 ei.exc = (MonoObject*)exc;
3974 process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
3975 return;
3977 } else if (agent_config.onthrow && !inited) {
3978 GSList *l;
3979 gboolean found = FALSE;
3981 for (l = agent_config.onthrow; l; l = l->next) {
3982 char *ex_type = l->data;
3983 char *f = mono_type_full_name (&exc->object.vtable->klass->byval_arg);
3985 if (!strcmp (ex_type, "") || !strcmp (ex_type, f))
3986 found = TRUE;
3988 g_free (f);
3991 if (found) {
3992 finish_agent_init (FALSE);
3995 * Send an unsolicited EXCEPTION event with a dummy request id.
3997 events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
3998 ei.exc = (MonoObject*)exc;
3999 process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
4000 return;
4004 if (!inited)
4005 return;
4007 ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL);
4009 ei.exc = (MonoObject*)exc;
4010 ei.caught = catch_ctx != NULL;
4012 mono_loader_lock ();
4013 events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy);
4014 mono_loader_unlock ();
4016 process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy);
4020 * buffer_add_value_full:
4022 * Add the encoding of the value at ADDR described by T to the buffer.
4023 * AS_VTYPE determines whenever to treat primitive types as primitive types or
4024 * vtypes.
4026 static void
4027 buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
4028 gboolean as_vtype)
4030 MonoObject *obj;
4032 if (t->byref) {
4033 g_assert (*(void**)addr);
4034 addr = *(void**)addr;
4037 if (as_vtype) {
4038 switch (t->type) {
4039 case MONO_TYPE_BOOLEAN:
4040 case MONO_TYPE_I1:
4041 case MONO_TYPE_U1:
4042 case MONO_TYPE_CHAR:
4043 case MONO_TYPE_I2:
4044 case MONO_TYPE_U2:
4045 case MONO_TYPE_I4:
4046 case MONO_TYPE_U4:
4047 case MONO_TYPE_R4:
4048 case MONO_TYPE_I8:
4049 case MONO_TYPE_U8:
4050 case MONO_TYPE_R8:
4051 case MONO_TYPE_I:
4052 case MONO_TYPE_U:
4053 case MONO_TYPE_PTR:
4054 goto handle_vtype;
4055 break;
4056 default:
4057 break;
4061 switch (t->type) {
4062 case MONO_TYPE_VOID:
4063 buffer_add_byte (buf, t->type);
4064 break;
4065 case MONO_TYPE_BOOLEAN:
4066 case MONO_TYPE_I1:
4067 case MONO_TYPE_U1:
4068 buffer_add_byte (buf, t->type);
4069 buffer_add_int (buf, *(gint8*)addr);
4070 break;
4071 case MONO_TYPE_CHAR:
4072 case MONO_TYPE_I2:
4073 case MONO_TYPE_U2:
4074 buffer_add_byte (buf, t->type);
4075 buffer_add_int (buf, *(gint16*)addr);
4076 break;
4077 case MONO_TYPE_I4:
4078 case MONO_TYPE_U4:
4079 case MONO_TYPE_R4:
4080 buffer_add_byte (buf, t->type);
4081 buffer_add_int (buf, *(gint32*)addr);
4082 break;
4083 case MONO_TYPE_I8:
4084 case MONO_TYPE_U8:
4085 case MONO_TYPE_R8:
4086 buffer_add_byte (buf, t->type);
4087 buffer_add_long (buf, *(gint64*)addr);
4088 break;
4089 case MONO_TYPE_I:
4090 case MONO_TYPE_U:
4091 /* Treat it as a vtype */
4092 goto handle_vtype;
4093 case MONO_TYPE_PTR: {
4094 gssize val = *(gssize*)addr;
4096 buffer_add_byte (buf, t->type);
4097 buffer_add_long (buf, val);
4098 break;
4100 handle_ref:
4101 case MONO_TYPE_STRING:
4102 case MONO_TYPE_SZARRAY:
4103 case MONO_TYPE_OBJECT:
4104 case MONO_TYPE_CLASS:
4105 case MONO_TYPE_ARRAY:
4106 obj = *(MonoObject**)addr;
4108 if (!obj) {
4109 buffer_add_byte (buf, VALUE_TYPE_ID_NULL);
4110 } else {
4111 if (obj->vtable->klass->valuetype) {
4112 t = &obj->vtable->klass->byval_arg;
4113 addr = mono_object_unbox (obj);
4114 goto handle_vtype;
4115 } else if (obj->vtable->klass->rank) {
4116 buffer_add_byte (buf, obj->vtable->klass->byval_arg.type);
4117 } else if (obj->vtable->klass->byval_arg.type == MONO_TYPE_GENERICINST) {
4118 buffer_add_byte (buf, MONO_TYPE_CLASS);
4119 } else {
4120 buffer_add_byte (buf, obj->vtable->klass->byval_arg.type);
4122 buffer_add_objid (buf, obj);
4124 break;
4125 handle_vtype:
4126 case MONO_TYPE_VALUETYPE: {
4127 int nfields;
4128 gpointer iter;
4129 MonoClassField *f;
4130 MonoClass *klass = mono_class_from_mono_type (t);
4132 buffer_add_byte (buf, MONO_TYPE_VALUETYPE);
4133 buffer_add_byte (buf, klass->enumtype);
4134 buffer_add_typeid (buf, domain, klass);
4136 nfields = 0;
4137 iter = NULL;
4138 while ((f = mono_class_get_fields (klass, &iter))) {
4139 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
4140 continue;
4141 if (mono_field_is_deleted (f))
4142 continue;
4143 nfields ++;
4145 buffer_add_int (buf, nfields);
4147 iter = NULL;
4148 while ((f = mono_class_get_fields (klass, &iter))) {
4149 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
4150 continue;
4151 if (mono_field_is_deleted (f))
4152 continue;
4153 buffer_add_value_full (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain, FALSE);
4155 break;
4157 case MONO_TYPE_GENERICINST:
4158 if (mono_type_generic_inst_is_valuetype (t)) {
4159 goto handle_vtype;
4160 } else {
4161 goto handle_ref;
4163 break;
4164 default:
4165 NOT_IMPLEMENTED;
4169 static void
4170 buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
4172 buffer_add_value_full (buf, t, addr, domain, FALSE);
4175 static ErrorCode
4176 decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
4178 int err;
4179 int type = decode_byte (buf, &buf, limit);
4181 if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
4182 !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
4183 !(t->type == MONO_TYPE_U && type == MONO_TYPE_VALUETYPE) &&
4184 !(t->type == MONO_TYPE_PTR && type == MONO_TYPE_I8) &&
4185 !(t->type == MONO_TYPE_GENERICINST && type == MONO_TYPE_VALUETYPE)) {
4186 char *name = mono_type_full_name (t);
4187 DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type));
4188 g_free (name);
4189 return ERR_INVALID_ARGUMENT;
4192 switch (t->type) {
4193 case MONO_TYPE_BOOLEAN:
4194 *(guint8*)addr = decode_int (buf, &buf, limit);
4195 break;
4196 case MONO_TYPE_CHAR:
4197 *(gunichar2*)addr = decode_int (buf, &buf, limit);
4198 break;
4199 case MONO_TYPE_I1:
4200 *(gint8*)addr = decode_int (buf, &buf, limit);
4201 break;
4202 case MONO_TYPE_U1:
4203 *(guint8*)addr = decode_int (buf, &buf, limit);
4204 break;
4205 case MONO_TYPE_I2:
4206 *(gint16*)addr = decode_int (buf, &buf, limit);
4207 break;
4208 case MONO_TYPE_U2:
4209 *(guint16*)addr = decode_int (buf, &buf, limit);
4210 break;
4211 case MONO_TYPE_I4:
4212 *(gint32*)addr = decode_int (buf, &buf, limit);
4213 break;
4214 case MONO_TYPE_U4:
4215 *(guint32*)addr = decode_int (buf, &buf, limit);
4216 break;
4217 case MONO_TYPE_I8:
4218 *(gint64*)addr = decode_long (buf, &buf, limit);
4219 break;
4220 case MONO_TYPE_U8:
4221 *(guint64*)addr = decode_long (buf, &buf, limit);
4222 break;
4223 case MONO_TYPE_R4:
4224 *(guint32*)addr = decode_int (buf, &buf, limit);
4225 break;
4226 case MONO_TYPE_R8:
4227 *(guint64*)addr = decode_long (buf, &buf, limit);
4228 break;
4229 case MONO_TYPE_PTR:
4230 /* We send these as I8, so we get them back as such */
4231 g_assert (type == MONO_TYPE_I8);
4232 *(gssize*)addr = decode_long (buf, &buf, limit);
4233 break;
4234 case MONO_TYPE_GENERICINST:
4235 if (MONO_TYPE_ISSTRUCT (t)) {
4236 /* The client sends these as a valuetype */
4237 goto handle_vtype;
4238 } else {
4239 goto handle_ref;
4241 break;
4242 case MONO_TYPE_I:
4243 case MONO_TYPE_U:
4244 /* We send these as vtypes, so we get them back as such */
4245 g_assert (type == MONO_TYPE_VALUETYPE);
4246 /* Fall through */
4247 handle_vtype:
4248 case MONO_TYPE_VALUETYPE: {
4249 gboolean is_enum = decode_byte (buf, &buf, limit);
4250 MonoClass *klass;
4251 MonoClassField *f;
4252 int nfields;
4253 gpointer iter = NULL;
4254 MonoDomain *d;
4256 /* Enums are sent as a normal vtype */
4257 if (is_enum)
4258 return ERR_NOT_IMPLEMENTED;
4259 klass = decode_typeid (buf, &buf, limit, &d, &err);
4260 if (err)
4261 return err;
4263 if (klass != mono_class_from_mono_type (t))
4264 return ERR_INVALID_ARGUMENT;
4266 nfields = decode_int (buf, &buf, limit);
4267 while ((f = mono_class_get_fields (klass, &iter))) {
4268 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
4269 continue;
4270 if (mono_field_is_deleted (f))
4271 continue;
4272 err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit);
4273 if (err)
4274 return err;
4275 nfields --;
4277 g_assert (nfields == 0);
4278 break;
4280 handle_ref:
4281 default:
4282 if (MONO_TYPE_IS_REFERENCE (t)) {
4283 if (type == MONO_TYPE_OBJECT) {
4284 int objid = decode_objid (buf, &buf, limit);
4285 int err;
4286 MonoObject *obj;
4288 err = get_object (objid, (MonoObject**)&obj);
4289 if (err)
4290 return err;
4292 if (obj && !mono_class_is_assignable_from (mono_class_from_mono_type (t), obj->vtable->klass))
4293 return ERR_INVALID_ARGUMENT;
4294 if (obj && obj->vtable->domain != domain)
4295 return ERR_INVALID_ARGUMENT;
4297 mono_gc_wbarrier_generic_store (addr, obj);
4298 } else if (type == VALUE_TYPE_ID_NULL) {
4299 *(MonoObject**)addr = NULL;
4300 } else {
4301 return ERR_INVALID_ARGUMENT;
4303 } else {
4304 NOT_IMPLEMENTED;
4306 break;
4309 *endbuf = buf;
4311 return 0;
4314 static void
4315 add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
4317 guint32 flags;
4318 int reg;
4319 guint8 *addr;
4320 gpointer reg_val;
4322 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
4323 reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
4325 switch (flags) {
4326 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
4327 reg_val = mono_arch_context_get_int_reg (ctx, reg);
4329 buffer_add_value_full (buf, t, &reg_val, domain, as_vtype);
4330 break;
4331 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
4332 addr = mono_arch_context_get_int_reg (ctx, reg);
4333 addr += (gint32)var->offset;
4335 //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
4337 buffer_add_value_full (buf, t, addr, domain, as_vtype);
4338 break;
4339 case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
4340 NOT_IMPLEMENTED;
4341 break;
4342 default:
4343 g_assert_not_reached ();
4347 static void
4348 set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, guint8 *val)
4350 guint32 flags;
4351 int reg, size;
4352 guint8 *addr;
4354 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
4355 reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
4357 if (MONO_TYPE_IS_REFERENCE (t))
4358 size = sizeof (gpointer);
4359 else
4360 size = mono_class_value_size (mono_class_from_mono_type (t), NULL);
4362 switch (flags) {
4363 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
4364 // FIXME: Can't set registers, so we disable linears
4365 NOT_IMPLEMENTED;
4366 break;
4367 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
4368 addr = mono_arch_context_get_int_reg (ctx, reg);
4369 addr += (gint32)var->offset;
4371 //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
4373 // FIXME: Write barriers
4374 memcpy (addr, val, size);
4375 break;
4376 case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
4377 NOT_IMPLEMENTED;
4378 break;
4379 default:
4380 g_assert_not_reached ();
4384 static void
4385 clear_event_request (int req_id, int etype)
4387 int i;
4389 mono_loader_lock ();
4390 for (i = 0; i < event_requests->len; ++i) {
4391 EventRequest *req = g_ptr_array_index (event_requests, i);
4393 if (req->id == req_id && req->event_kind == etype) {
4394 if (req->event_kind == EVENT_KIND_BREAKPOINT)
4395 clear_breakpoint (req->info);
4396 if (req->event_kind == EVENT_KIND_STEP)
4397 ss_destroy (req->info);
4398 if (req->event_kind == EVENT_KIND_METHOD_ENTRY)
4399 clear_breakpoint (req->info);
4400 if (req->event_kind == EVENT_KIND_METHOD_EXIT)
4401 clear_breakpoint (req->info);
4402 g_ptr_array_remove_index_fast (event_requests, i);
4403 g_free (req);
4404 break;
4407 mono_loader_unlock ();
4410 static gboolean
4411 event_req_matches_assembly (EventRequest *req, MonoAssembly *assembly)
4413 if (req->event_kind == EVENT_KIND_BREAKPOINT)
4414 return breakpoint_matches_assembly (req->info, assembly);
4415 else
4416 // FIXME:
4417 return FALSE;
4421 * clear_event_requests_for_assembly:
4423 * Clear all events requests which reference ASSEMBLY.
4425 static void
4426 clear_event_requests_for_assembly (MonoAssembly *assembly)
4428 int i;
4429 gboolean found;
4431 mono_loader_lock ();
4432 found = TRUE;
4433 while (found) {
4434 found = FALSE;
4435 for (i = 0; i < event_requests->len; ++i) {
4436 EventRequest *req = g_ptr_array_index (event_requests, i);
4438 if (event_req_matches_assembly (req, assembly)) {
4439 clear_event_request (req->id, req->event_kind);
4440 found = TRUE;
4441 break;
4445 mono_loader_unlock ();
4448 static void
4449 add_thread (gpointer key, gpointer value, gpointer user_data)
4451 MonoInternalThread *thread = value;
4452 Buffer *buf = user_data;
4454 buffer_add_objid (buf, (MonoObject*)thread);
4457 static ErrorCode
4458 do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
4460 guint8 *p = invoke->p;
4461 guint8 *end = invoke->endp;
4462 MonoMethod *m;
4463 int i, err, nargs;
4464 MonoMethodSignature *sig;
4465 guint8 **arg_buf;
4466 void **args;
4467 MonoObject *this, *res, *exc;
4468 MonoDomain *domain;
4469 guint8 *this_buf;
4470 #ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
4471 MonoLMFExt ext;
4472 #endif
4474 if (invoke->method) {
4476 * Invoke this method directly, currently only Environment.Exit () is supported.
4478 this = NULL;
4479 DEBUG (1, printf ("[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (invoke->method, TRUE), this ? this->vtable->klass->name : "<null>"));
4480 mono_runtime_invoke (invoke->method, NULL, invoke->args, &exc);
4481 g_assert_not_reached ();
4484 m = decode_methodid (p, &p, end, &domain, &err);
4485 if (err)
4486 return err;
4487 sig = mono_method_signature (m);
4489 if (m->klass->valuetype)
4490 this_buf = g_alloca (mono_class_instance_size (m->klass));
4491 else
4492 this_buf = g_alloca (sizeof (MonoObject*));
4493 if (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) {
4494 /* Should be null */
4495 int type = decode_byte (p, &p, end);
4496 if (type != VALUE_TYPE_ID_NULL)
4497 return ERR_INVALID_ARGUMENT;
4498 memset (this_buf, 0, mono_class_instance_size (m->klass));
4499 } else {
4500 err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
4501 if (err)
4502 return err;
4505 if (!m->klass->valuetype)
4506 this = *(MonoObject**)this_buf;
4507 else
4508 this = NULL;
4510 DEBUG (1, printf ("[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "<null>"));
4512 if (this && this->vtable->domain != domain)
4513 NOT_IMPLEMENTED;
4515 if (!m->klass->valuetype && !(m->flags & METHOD_ATTRIBUTE_STATIC) && !this) {
4516 if (!strcmp (m->name, ".ctor")) {
4517 if (m->klass->flags & TYPE_ATTRIBUTE_ABSTRACT)
4518 return ERR_INVALID_ARGUMENT;
4519 else
4520 this = mono_object_new (domain, m->klass);
4521 } else {
4522 return ERR_INVALID_ARGUMENT;
4526 if (this && !mono_class_is_assignable_from (m->klass, this->vtable->klass))
4527 return ERR_INVALID_ARGUMENT;
4529 nargs = decode_int (p, &p, end);
4530 if (nargs != sig->param_count)
4531 return ERR_INVALID_ARGUMENT;
4532 /* Use alloca to get gc tracking */
4533 arg_buf = g_alloca (nargs * sizeof (gpointer));
4534 memset (arg_buf, 0, nargs * sizeof (gpointer));
4535 args = g_alloca (nargs * sizeof (gpointer));
4536 for (i = 0; i < nargs; ++i) {
4537 if (MONO_TYPE_IS_REFERENCE (sig->params [i])) {
4538 err = decode_value (sig->params [i], domain, (guint8*)&args [i], p, &p, end);
4539 if (err)
4540 break;
4542 if (args [i] && ((MonoObject*)args [i])->vtable->domain != domain)
4543 NOT_IMPLEMENTED;
4544 } else {
4545 arg_buf [i] = g_alloca (mono_class_instance_size (mono_class_from_mono_type (sig->params [i])));
4546 err = decode_value (sig->params [i], domain, arg_buf [i], p, &p, end);
4547 if (err)
4548 break;
4549 args [i] = arg_buf [i];
4553 if (i < nargs)
4554 return err;
4556 if (invoke->flags & INVOKE_FLAG_DISABLE_BREAKPOINTS)
4557 tls->disable_breakpoints = TRUE;
4558 else
4559 tls->disable_breakpoints = FALSE;
4562 * Add an LMF frame to link the stack frames on the invoke method with our caller.
4564 /* FIXME: Move this to arch specific code */
4565 #ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
4566 if (invoke->has_ctx) {
4567 MonoLMF **lmf_addr;
4569 lmf_addr = mono_get_lmf_addr ();
4571 /* Setup our lmf */
4572 memset (&ext, 0, sizeof (ext));
4573 #ifdef TARGET_AMD64
4574 ext.lmf.previous_lmf = *(lmf_addr);
4575 /* Mark that this is a MonoLMFExt */
4576 ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
4577 ext.lmf.rsp = (gssize)&ext;
4578 #elif defined(TARGET_X86)
4579 ext.lmf.previous_lmf = (gsize)*(lmf_addr);
4580 /* Mark that this is a MonoLMFExt */
4581 ext.lmf.previous_lmf = (gsize)(gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
4582 ext.lmf.ebp = (gssize)&ext;
4583 #elif defined(TARGET_ARM)
4584 ext.lmf.previous_lmf = *(lmf_addr);
4585 /* Mark that this is a MonoLMFExt */
4586 ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
4587 ext.lmf.ebp = (gssize)&ext;
4588 #elif defined(TARGET_POWERPC)
4589 ext.lmf.previous_lmf = *(lmf_addr);
4590 /* Mark that this is a MonoLMFExt */
4591 ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
4592 ext.lmf.ebp = (gssize)&ext;
4593 #else
4594 g_assert_not_reached ();
4595 #endif
4597 ext.debugger_invoke = TRUE;
4598 memcpy (&ext.ctx, &invoke->ctx, sizeof (MonoContext));
4600 mono_set_lmf ((MonoLMF*)&ext);
4602 #endif
4604 if (m->klass->valuetype)
4605 res = mono_runtime_invoke (m, this_buf, args, &exc);
4606 else
4607 res = mono_runtime_invoke (m, this, args, &exc);
4608 if (exc) {
4609 buffer_add_byte (buf, 0);
4610 buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &exc, domain);
4611 } else {
4612 buffer_add_byte (buf, 1);
4613 if (sig->ret->type == MONO_TYPE_VOID) {
4614 if (!strcmp (m->name, ".ctor") && !m->klass->valuetype) {
4615 buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &this, domain);
4617 else
4618 buffer_add_value (buf, &mono_defaults.void_class->byval_arg, NULL, domain);
4619 } else if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
4620 buffer_add_value (buf, sig->ret, &res, domain);
4621 } else if (mono_class_from_mono_type (sig->ret)->valuetype) {
4622 g_assert (res);
4623 buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
4624 } else {
4625 NOT_IMPLEMENTED;
4629 tls->disable_breakpoints = FALSE;
4631 #ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
4632 if (invoke->has_ctx)
4633 mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
4634 #endif
4636 // FIXME: byref arguments
4637 // FIXME: varargs
4638 return ERR_NONE;
4642 * invoke_method:
4644 * Invoke the method given by tls->pending_invoke in the current thread.
4646 static void
4647 invoke_method (void)
4649 DebuggerTlsData *tls;
4650 InvokeData *invoke;
4651 int id;
4652 int err;
4653 Buffer buf;
4654 static void (*restore_context) (void *);
4655 MonoContext restore_ctx;
4657 if (!restore_context)
4658 restore_context = mono_get_restore_context ();
4660 tls = TlsGetValue (debugger_tls_id);
4661 g_assert (tls);
4664 * Store the `InvokeData *' in `tls->invoke' until we're done with
4665 * the invocation, so CMD_VM_ABORT_INVOKE can check it.
4668 mono_loader_lock ();
4670 invoke = tls->pending_invoke;
4671 g_assert (invoke);
4672 tls->pending_invoke = NULL;
4674 invoke->last_invoke = tls->invoke;
4675 tls->invoke = invoke;
4677 mono_loader_unlock ();
4679 tls->frames_up_to_date = FALSE;
4681 id = invoke->id;
4683 buffer_init (&buf, 128);
4685 err = do_invoke_method (tls, &buf, invoke);
4687 /* Start suspending before sending the reply */
4688 if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED))
4689 suspend_vm ();
4691 send_reply_packet (id, err, &buf);
4693 buffer_free (&buf);
4695 memcpy (&restore_ctx, &invoke->ctx, sizeof (MonoContext));
4697 if (invoke->has_ctx)
4698 save_thread_context (&restore_ctx);
4700 if (invoke->flags & INVOKE_FLAG_SINGLE_THREADED) {
4701 g_assert (tls->resume_count);
4702 tls->resume_count -= invoke->suspend_count;
4705 DEBUG (1, printf ("[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count));
4708 * Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE:
4710 * It is possible that ves_icall_System_Threading_Thread_Abort () was called
4711 * after the mono_runtime_invoke() already returned, but it doesn't matter
4712 * because we reset the abort here.
4715 mono_loader_lock ();
4717 if (tls->abort_requested)
4718 mono_thread_internal_reset_abort (tls->thread);
4720 tls->invoke = tls->invoke->last_invoke;
4721 tls->abort_requested = FALSE;
4723 mono_loader_unlock ();
4725 g_free (invoke->p);
4726 g_free (invoke);
4728 suspend_current ();
4731 static gboolean
4732 is_really_suspended (gpointer key, gpointer value, gpointer user_data)
4734 MonoThread *thread = value;
4735 DebuggerTlsData *tls;
4736 gboolean res;
4738 mono_loader_lock ();
4739 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
4740 g_assert (tls);
4741 res = tls->really_suspended;
4742 mono_loader_unlock ();
4744 return res;
4747 static ErrorCode
4748 vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
4750 switch (command) {
4751 case CMD_VM_VERSION: {
4752 char *build_info, *version;
4754 build_info = mono_get_runtime_build_info ();
4755 version = g_strdup_printf ("mono %s", build_info);
4757 buffer_add_string (buf, version); /* vm version */
4758 buffer_add_int (buf, MAJOR_VERSION);
4759 buffer_add_int (buf, MINOR_VERSION);
4760 g_free (build_info);
4761 g_free (version);
4762 break;
4764 case CMD_VM_SET_PROTOCOL_VERSION: {
4765 major_version = decode_int (p, &p, end);
4766 minor_version = decode_int (p, &p, end);
4767 protocol_version_set = TRUE;
4768 DEBUG(1, fprintf (log_file, "[dbg] Protocol version %d.%d, client protocol version %d.%d.\n", MAJOR_VERSION, MINOR_VERSION, major_version, minor_version));
4769 break;
4771 case CMD_VM_ALL_THREADS: {
4772 // FIXME: Domains
4773 mono_loader_lock ();
4774 buffer_add_int (buf, mono_g_hash_table_size (tid_to_thread_obj));
4775 mono_g_hash_table_foreach (tid_to_thread_obj, add_thread, buf);
4776 mono_loader_unlock ();
4777 break;
4779 case CMD_VM_SUSPEND:
4780 suspend_vm ();
4781 wait_for_suspend ();
4782 break;
4783 case CMD_VM_RESUME:
4784 if (suspend_count == 0)
4785 return ERR_NOT_SUSPENDED;
4786 resume_vm ();
4787 break;
4788 case CMD_VM_DISPOSE:
4789 /* Clear all event requests */
4790 mono_loader_lock ();
4791 while (event_requests->len > 0) {
4792 EventRequest *req = g_ptr_array_index (event_requests, 0);
4794 clear_event_request (req->id, req->event_kind);
4796 mono_loader_unlock ();
4798 // FIXME: Count resumes
4799 resume_vm ();
4800 disconnected = TRUE;
4801 break;
4802 case CMD_VM_EXIT: {
4803 MonoInternalThread *thread;
4804 DebuggerTlsData *tls;
4805 MonoClass *env_class;
4806 MonoMethod *exit_method;
4807 gpointer *args;
4808 int exit_code;
4810 exit_code = decode_int (p, &p, end);
4812 // FIXME: What if there is a VM_DEATH event request with SUSPEND_ALL ?
4814 /* Have to send a reply before exiting */
4815 send_reply_packet (id, 0, buf);
4817 /* Clear all event requests */
4818 mono_loader_lock ();
4819 while (event_requests->len > 0) {
4820 EventRequest *req = g_ptr_array_index (event_requests, 0);
4822 clear_event_request (req->id, req->event_kind);
4824 mono_loader_unlock ();
4827 * The JDWP documentation says that the shutdown is not orderly. It doesn't
4828 * specify whenever a VM_DEATH event is sent. We currently do an orderly
4829 * shutdown by hijacking a thread to execute Environment.Exit (). This is
4830 * better than doing the shutdown ourselves, since it avoids various races.
4833 suspend_vm ();
4834 wait_for_suspend ();
4836 env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
4837 g_assert (env_class);
4838 exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
4839 g_assert (exit_method);
4841 mono_loader_lock ();
4842 thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL);
4843 mono_loader_unlock ();
4845 if (thread) {
4846 mono_loader_lock ();
4847 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
4848 mono_loader_unlock ();
4850 args = g_new0 (gpointer, 1);
4851 args [0] = g_malloc (sizeof (int));
4852 *(int*)(args [0]) = exit_code;
4854 tls->pending_invoke = g_new0 (InvokeData, 1);
4855 tls->pending_invoke->method = exit_method;
4856 tls->pending_invoke->args = args;
4858 while (suspend_count > 0)
4859 resume_vm ();
4860 } else {
4862 * No thread found, do it ourselves.
4863 * FIXME: This can race with normal shutdown etc.
4865 while (suspend_count > 0)
4866 resume_vm ();
4868 mono_runtime_set_shutting_down ();
4870 mono_threads_set_shutting_down ();
4872 /* Suspend all managed threads since the runtime is going away */
4873 DEBUG(1, fprintf (log_file, "Suspending all threads...\n"));
4874 mono_thread_suspend_all_other_threads ();
4875 DEBUG(1, fprintf (log_file, "Shutting down the runtime...\n"));
4876 mono_runtime_quit ();
4877 #ifdef HOST_WIN32
4878 shutdown (conn_fd, SD_BOTH);
4879 #else
4880 shutdown (conn_fd, SHUT_RDWR);
4881 #endif
4882 DEBUG(1, fprintf (log_file, "Exiting...\n"));
4884 exit (exit_code);
4886 break;
4888 case CMD_VM_INVOKE_METHOD: {
4889 int objid = decode_objid (p, &p, end);
4890 MonoThread *thread;
4891 DebuggerTlsData *tls;
4892 int err, flags;
4894 err = get_object (objid, (MonoObject**)&thread);
4895 if (err)
4896 return err;
4898 flags = decode_int (p, &p, end);
4900 // Wait for suspending if it already started
4901 if (suspend_count)
4902 wait_for_suspend ();
4903 if (!is_suspended ())
4904 return ERR_NOT_SUSPENDED;
4906 mono_loader_lock ();
4907 tls = mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL (thread));
4908 mono_loader_unlock ();
4909 g_assert (tls);
4911 if (!tls->really_suspended)
4912 /* The thread is still running native code, can't do invokes */
4913 return ERR_NOT_SUSPENDED;
4916 * Store the invoke data into tls, the thread will execute it after it is
4917 * resumed.
4919 if (tls->pending_invoke)
4920 NOT_IMPLEMENTED;
4921 tls->pending_invoke = g_new0 (InvokeData, 1);
4922 tls->pending_invoke->id = id;
4923 tls->pending_invoke->flags = flags;
4924 tls->pending_invoke->p = g_malloc (end - p);
4925 memcpy (tls->pending_invoke->p, p, end - p);
4926 tls->pending_invoke->endp = tls->pending_invoke->p + (end - p);
4927 tls->pending_invoke->suspend_count = suspend_count;
4929 if (flags & INVOKE_FLAG_SINGLE_THREADED)
4930 resume_thread (THREAD_TO_INTERNAL (thread));
4931 else
4932 resume_vm ();
4933 break;
4935 case CMD_VM_ABORT_INVOKE: {
4936 int objid = decode_objid (p, &p, end);
4937 MonoThread *thread;
4938 DebuggerTlsData *tls;
4939 int invoke_id, err;
4941 err = get_object (objid, (MonoObject**)&thread);
4942 if (err)
4943 return err;
4945 invoke_id = decode_int (p, &p, end);
4947 mono_loader_lock ();
4948 tls = mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL (thread));
4949 g_assert (tls);
4951 if (tls->abort_requested) {
4952 mono_loader_unlock ();
4953 break;
4957 * Check whether we're still inside the mono_runtime_invoke() and that it's
4958 * actually the correct invocation.
4960 * Careful, we do not stop the thread that's doing the invocation, so we can't
4961 * inspect its stack. However, invoke_method() also acquires the loader lock
4962 * when it's done, so we're safe here.
4966 if (!tls->invoke || (tls->invoke->id != invoke_id)) {
4967 mono_loader_unlock ();
4968 return ERR_NO_INVOCATION;
4971 tls->abort_requested = TRUE;
4973 ves_icall_System_Threading_Thread_Abort (THREAD_TO_INTERNAL (thread), NULL);
4974 mono_loader_unlock ();
4975 break;
4978 default:
4979 return ERR_NOT_IMPLEMENTED;
4982 return ERR_NONE;
4985 static ErrorCode
4986 event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
4988 int err;
4990 switch (command) {
4991 case CMD_EVENT_REQUEST_SET: {
4992 EventRequest *req;
4993 int i, event_kind, suspend_policy, nmodifiers, mod;
4994 MonoMethod *method;
4995 long location = 0;
4996 MonoThread *step_thread;
4997 int size = 0, depth = 0, step_thread_id = 0;
4998 MonoDomain *domain;
5000 event_kind = decode_byte (p, &p, end);
5001 suspend_policy = decode_byte (p, &p, end);
5002 nmodifiers = decode_byte (p, &p, end);
5004 req = g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier)));
5005 req->id = InterlockedIncrement (&event_request_id);
5006 req->event_kind = event_kind;
5007 req->suspend_policy = suspend_policy;
5008 req->nmodifiers = nmodifiers;
5010 method = NULL;
5011 for (i = 0; i < nmodifiers; ++i) {
5012 mod = decode_byte (p, &p, end);
5014 req->modifiers [i].kind = mod;
5015 if (mod == MOD_KIND_COUNT) {
5016 req->modifiers [i].data.count = decode_int (p, &p, end);
5017 } else if (mod == MOD_KIND_LOCATION_ONLY) {
5018 method = decode_methodid (p, &p, end, &domain, &err);
5019 if (err)
5020 return err;
5021 location = decode_long (p, &p, end);
5022 } else if (mod == MOD_KIND_STEP) {
5023 step_thread_id = decode_id (p, &p, end);
5024 size = decode_int (p, &p, end);
5025 depth = decode_int (p, &p, end);
5026 } else if (mod == MOD_KIND_THREAD_ONLY) {
5027 int id = decode_id (p, &p, end);
5029 err = get_object (id, (MonoObject**)&req->modifiers [i].data.thread);
5030 if (err) {
5031 g_free (req);
5032 return err;
5034 } else if (mod == MOD_KIND_EXCEPTION_ONLY) {
5035 MonoClass *exc_class = decode_typeid (p, &p, end, &domain, &err);
5037 if (err)
5038 return err;
5039 req->modifiers [i].caught = decode_byte (p, &p, end);
5040 req->modifiers [i].uncaught = decode_byte (p, &p, end);
5041 if (exc_class) {
5042 req->modifiers [i].data.exc_class = exc_class;
5044 if (!mono_class_is_assignable_from (mono_defaults.exception_class, exc_class)) {
5045 g_free (req);
5046 return ERR_INVALID_ARGUMENT;
5049 } else if (mod == MOD_KIND_ASSEMBLY_ONLY) {
5050 int n = decode_int (p, &p, end);
5051 int j;
5053 req->modifiers [i].data.assemblies = g_new0 (MonoAssembly*, n);
5054 for (j = 0; j < n; ++j) {
5055 req->modifiers [i].data.assemblies [j] = decode_assemblyid (p, &p, end, &domain, &err);
5056 if (err) {
5057 g_free (req->modifiers [i].data.assemblies);
5058 return err;
5061 } else {
5062 g_free (req);
5063 return ERR_NOT_IMPLEMENTED;
5067 if (req->event_kind == EVENT_KIND_BREAKPOINT) {
5068 g_assert (method);
5070 req->info = set_breakpoint (method, location, req);
5071 } else if (req->event_kind == EVENT_KIND_STEP) {
5072 g_assert (step_thread_id);
5074 err = get_object (step_thread_id, (MonoObject**)&step_thread);
5075 if (err) {
5076 g_free (req);
5077 return err;
5080 err = ss_create (THREAD_TO_INTERNAL (step_thread), size, depth, req);
5081 if (err) {
5082 g_free (req);
5083 return err;
5085 } else if (req->event_kind == EVENT_KIND_METHOD_ENTRY) {
5086 req->info = set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req);
5087 } else if (req->event_kind == EVENT_KIND_METHOD_EXIT) {
5088 req->info = set_breakpoint (NULL, METHOD_EXIT_IL_OFFSET, req);
5089 } else if (req->event_kind == EVENT_KIND_EXCEPTION) {
5090 } else {
5091 if (req->nmodifiers) {
5092 g_free (req);
5093 return ERR_NOT_IMPLEMENTED;
5097 mono_loader_lock ();
5098 g_ptr_array_add (event_requests, req);
5099 mono_loader_unlock ();
5101 buffer_add_int (buf, req->id);
5102 break;
5104 case CMD_EVENT_REQUEST_CLEAR: {
5105 int etype = decode_byte (p, &p, end);
5106 int req_id = decode_int (p, &p, end);
5108 // FIXME: Make a faster mapping from req_id to request
5109 mono_loader_lock ();
5110 clear_event_request (req_id, etype);
5111 mono_loader_unlock ();
5112 break;
5114 case CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS: {
5115 int i;
5117 mono_loader_lock ();
5118 i = 0;
5119 while (i < event_requests->len) {
5120 EventRequest *req = g_ptr_array_index (event_requests, i);
5122 if (req->event_kind == EVENT_KIND_BREAKPOINT) {
5123 clear_breakpoint (req->info);
5125 g_ptr_array_remove_index_fast (event_requests, i);
5126 g_free (req);
5127 } else {
5128 i ++;
5131 mono_loader_unlock ();
5132 break;
5134 default:
5135 return ERR_NOT_IMPLEMENTED;
5138 return ERR_NONE;
5141 static ErrorCode
5142 domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
5144 int err;
5145 MonoDomain *domain;
5147 switch (command) {
5148 case CMD_APPDOMAIN_GET_ROOT_DOMAIN: {
5149 buffer_add_domainid (buf, mono_get_root_domain ());
5150 break;
5152 case CMD_APPDOMAIN_GET_FRIENDLY_NAME: {
5153 domain = decode_domainid (p, &p, end, NULL, &err);
5154 if (err)
5155 return err;
5156 buffer_add_string (buf, domain->friendly_name);
5157 break;
5159 case CMD_APPDOMAIN_GET_ASSEMBLIES: {
5160 GSList *tmp;
5161 MonoAssembly *ass;
5162 int count;
5164 domain = decode_domainid (p, &p, end, NULL, &err);
5165 if (err)
5166 return err;
5167 mono_loader_lock ();
5168 count = 0;
5169 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
5170 count ++;
5172 buffer_add_int (buf, count);
5173 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
5174 ass = tmp->data;
5175 buffer_add_assemblyid (buf, domain, ass);
5177 mono_loader_unlock ();
5178 break;
5180 case CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY: {
5181 domain = decode_domainid (p, &p, end, NULL, &err);
5182 if (err)
5183 return err;
5185 buffer_add_assemblyid (buf, domain, domain->entry_assembly);
5186 break;
5188 case CMD_APPDOMAIN_GET_CORLIB: {
5189 domain = decode_domainid (p, &p, end, NULL, &err);
5190 if (err)
5191 return err;
5193 buffer_add_assemblyid (buf, domain, domain->domain->mbr.obj.vtable->klass->image->assembly);
5194 break;
5196 case CMD_APPDOMAIN_CREATE_STRING: {
5197 char *s;
5198 MonoString *o;
5200 domain = decode_domainid (p, &p, end, NULL, &err);
5201 if (err)
5202 return err;
5203 s = decode_string (p, &p, end);
5205 o = mono_string_new (domain, s);
5206 buffer_add_objid (buf, (MonoObject*)o);
5207 break;
5209 case CMD_APPDOMAIN_CREATE_BOXED_VALUE: {
5210 MonoClass *klass;
5211 MonoDomain *domain2;
5212 MonoObject *o;
5214 domain = decode_domainid (p, &p, end, NULL, &err);
5215 if (err)
5216 return err;
5217 klass = decode_typeid (p, &p, end, &domain2, &err);
5218 if (err)
5219 return err;
5221 // FIXME:
5222 g_assert (domain == domain2);
5224 o = mono_object_new (domain, klass);
5226 err = decode_value (&klass->byval_arg, domain, mono_object_unbox (o), p, &p, end);
5227 if (err)
5228 return err;
5230 buffer_add_objid (buf, o);
5231 break;
5233 default:
5234 return ERR_NOT_IMPLEMENTED;
5237 return ERR_NONE;
5240 static ErrorCode
5241 assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
5243 int err;
5244 MonoAssembly *ass;
5245 MonoDomain *domain;
5247 ass = decode_assemblyid (p, &p, end, &domain, &err);
5248 if (err)
5249 return err;
5251 switch (command) {
5252 case CMD_ASSEMBLY_GET_LOCATION: {
5253 buffer_add_string (buf, mono_image_get_filename (ass->image));
5254 break;
5256 case CMD_ASSEMBLY_GET_ENTRY_POINT: {
5257 guint32 token;
5258 MonoMethod *m;
5260 if (ass->image->dynamic) {
5261 buffer_add_id (buf, 0);
5262 } else {
5263 token = mono_image_get_entry_point (ass->image);
5264 if (token == 0) {
5265 buffer_add_id (buf, 0);
5266 } else {
5267 m = mono_get_method (ass->image, token, NULL);
5268 buffer_add_methodid (buf, domain, m);
5271 break;
5273 case CMD_ASSEMBLY_GET_MANIFEST_MODULE: {
5274 buffer_add_moduleid (buf, domain, ass->image);
5275 break;
5277 case CMD_ASSEMBLY_GET_OBJECT: {
5278 MonoObject *o = (MonoObject*)mono_assembly_get_object (mono_domain_get (), ass);
5279 buffer_add_objid (buf, o);
5280 break;
5282 case CMD_ASSEMBLY_GET_TYPE: {
5283 char *s = decode_string (p, &p, end);
5284 gboolean ignorecase = decode_byte (p, &p, end);
5285 MonoTypeNameParse info;
5286 MonoType *t;
5287 gboolean type_resolve;
5289 if (!mono_reflection_parse_type (s, &info)) {
5290 t = NULL;
5291 } else {
5292 if (info.assembly.name)
5293 NOT_IMPLEMENTED;
5294 t = mono_reflection_get_type (ass->image, &info, ignorecase, &type_resolve);
5296 buffer_add_typeid (buf, domain, t ? mono_class_from_mono_type (t) : NULL);
5297 mono_reflection_free_type_info (&info);
5298 g_free (s);
5300 break;
5302 case CMD_ASSEMBLY_GET_NAME: {
5303 gchar *name;
5304 MonoAssembly *mass = ass;
5306 name = g_strdup_printf (
5307 "%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
5308 mass->aname.name,
5309 mass->aname.major, mass->aname.minor, mass->aname.build, mass->aname.revision,
5310 mass->aname.culture && *mass->aname.culture? mass->aname.culture: "neutral",
5311 mass->aname.public_key_token [0] ? (char *)mass->aname.public_key_token : "null",
5312 (mass->aname.flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
5314 buffer_add_string (buf, name);
5315 g_free (name);
5316 break;
5318 default:
5319 return ERR_NOT_IMPLEMENTED;
5322 return ERR_NONE;
5325 static ErrorCode
5326 module_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
5328 int err;
5329 MonoDomain *domain;
5331 switch (command) {
5332 case CMD_MODULE_GET_INFO: {
5333 MonoImage *image = decode_moduleid (p, &p, end, &domain, &err);
5334 char *basename;
5336 basename = g_path_get_basename (image->name);
5337 buffer_add_string (buf, basename); // name
5338 buffer_add_string (buf, image->module_name); // scopename
5339 buffer_add_string (buf, image->name); // fqname
5340 buffer_add_string (buf, mono_image_get_guid (image)); // guid
5341 buffer_add_assemblyid (buf, domain, image->assembly); // assembly
5342 g_free (basename);
5343 break;
5345 default:
5346 return ERR_NOT_IMPLEMENTED;
5349 return ERR_NONE;
5352 static void
5353 buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val)
5355 if (val && val->vtable->klass == mono_defaults.monotype_class) {
5356 /* Special case these so the client doesn't have to handle Type objects */
5358 buffer_add_byte (buf, VALUE_TYPE_ID_TYPE);
5359 buffer_add_typeid (buf, domain, mono_class_from_mono_type (((MonoReflectionType*)val)->type));
5360 } else if (MONO_TYPE_IS_REFERENCE (t))
5361 buffer_add_value (buf, t, &val, domain);
5362 else
5363 buffer_add_value (buf, t, mono_object_unbox (val), domain);
5366 static void
5367 buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass *attr_klass, MonoCustomAttrInfo *cinfo)
5369 int i, j;
5370 int nattrs = 0;
5372 if (!cinfo) {
5373 buffer_add_int (buf, 0);
5374 return;
5377 for (i = 0; i < cinfo->num_attrs; ++i) {
5378 if (!attr_klass || mono_class_has_parent (cinfo->attrs [i].ctor->klass, attr_klass))
5379 nattrs ++;
5381 buffer_add_int (buf, nattrs);
5383 for (i = 0; i < cinfo->num_attrs; ++i) {
5384 MonoCustomAttrEntry *attr = &cinfo->attrs [i];
5385 if (!attr_klass || mono_class_has_parent (attr->ctor->klass, attr_klass)) {
5386 MonoArray *typed_args, *named_args;
5387 MonoType *t;
5388 CattrNamedArg *arginfo;
5390 mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo);
5392 buffer_add_methodid (buf, domain, attr->ctor);
5394 /* Ctor args */
5395 if (typed_args) {
5396 buffer_add_int (buf, mono_array_length (typed_args));
5397 for (j = 0; j < mono_array_length (typed_args); ++j) {
5398 MonoObject *val = mono_array_get (typed_args, MonoObject*, j);
5400 t = mono_method_signature (attr->ctor)->params [j];
5402 buffer_add_cattr_arg (buf, t, domain, val);
5404 } else {
5405 buffer_add_int (buf, 0);
5408 /* Named args */
5409 if (named_args) {
5410 buffer_add_int (buf, mono_array_length (named_args));
5412 for (j = 0; j < mono_array_length (named_args); ++j) {
5413 MonoObject *val = mono_array_get (named_args, MonoObject*, j);
5415 if (arginfo [j].prop) {
5416 buffer_add_byte (buf, 0x54);
5417 buffer_add_propertyid (buf, domain, arginfo [j].prop);
5418 } else if (arginfo [j].field) {
5419 buffer_add_byte (buf, 0x53);
5420 } else {
5421 g_assert_not_reached ();
5424 buffer_add_cattr_arg (buf, arginfo [j].type, domain, val);
5426 } else {
5427 buffer_add_int (buf, 0);
5433 static ErrorCode
5434 type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
5436 MonoClass *klass;
5437 MonoDomain *domain;
5438 MonoClass *nested;
5439 MonoType *type;
5440 gpointer iter;
5441 guint8 b;
5442 int err, nnested;
5443 char *name;
5445 klass = decode_typeid (p, &p, end, &domain, &err);
5446 if (err)
5447 return err;
5449 switch (command) {
5450 case CMD_TYPE_GET_INFO: {
5451 buffer_add_string (buf, klass->name_space);
5452 buffer_add_string (buf, klass->name);
5453 // FIXME: byref
5454 name = mono_type_get_name_full (&klass->byval_arg, MONO_TYPE_NAME_FORMAT_FULL_NAME);
5455 buffer_add_string (buf, name);
5456 g_free (name);
5457 buffer_add_assemblyid (buf, domain, klass->image->assembly);
5458 buffer_add_moduleid (buf, domain, klass->image);
5459 buffer_add_typeid (buf, domain, klass->parent);
5460 if (klass->rank || klass->byval_arg.type == MONO_TYPE_PTR)
5461 buffer_add_typeid (buf, domain, klass->element_class);
5462 else
5463 buffer_add_id (buf, 0);
5464 buffer_add_int (buf, klass->type_token);
5465 buffer_add_byte (buf, klass->rank);
5466 buffer_add_int (buf, klass->flags);
5467 b = 0;
5468 type = &klass->byval_arg;
5469 // FIXME: Can't decide whenever a class represents a byref type
5470 if (FALSE)
5471 b |= (1 << 0);
5472 if (type->type == MONO_TYPE_PTR)
5473 b |= (1 << 1);
5474 if (!type->byref && (((type->type >= MONO_TYPE_BOOLEAN) && (type->type <= MONO_TYPE_R8)) || (type->type == MONO_TYPE_I) || (type->type == MONO_TYPE_U)))
5475 b |= (1 << 2);
5476 if (type->type == MONO_TYPE_VALUETYPE)
5477 b |= (1 << 3);
5478 if (klass->enumtype)
5479 b |= (1 << 4);
5480 buffer_add_byte (buf, b);
5481 nnested = 0;
5482 iter = NULL;
5483 while ((nested = mono_class_get_nested_types (klass, &iter)))
5484 nnested ++;
5485 buffer_add_int (buf, nnested);
5486 iter = NULL;
5487 while ((nested = mono_class_get_nested_types (klass, &iter)))
5488 buffer_add_typeid (buf, domain, nested);
5489 break;
5491 case CMD_TYPE_GET_METHODS: {
5492 int nmethods;
5493 int i = 0;
5494 gpointer iter = NULL;
5495 MonoMethod *m;
5497 nmethods = mono_class_num_methods (klass);
5499 buffer_add_int (buf, nmethods);
5501 while ((m = mono_class_get_methods (klass, &iter))) {
5502 buffer_add_methodid (buf, domain, m);
5503 i ++;
5505 g_assert (i == nmethods);
5506 break;
5508 case CMD_TYPE_GET_FIELDS: {
5509 int nfields;
5510 int i = 0;
5511 gpointer iter = NULL;
5512 MonoClassField *f;
5514 nfields = mono_class_num_fields (klass);
5516 buffer_add_int (buf, nfields);
5518 while ((f = mono_class_get_fields (klass, &iter))) {
5519 buffer_add_fieldid (buf, domain, f);
5520 buffer_add_string (buf, f->name);
5521 buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type));
5522 buffer_add_int (buf, f->type->attrs);
5523 i ++;
5525 g_assert (i == nfields);
5526 break;
5528 case CMD_TYPE_GET_PROPERTIES: {
5529 int nprops;
5530 int i = 0;
5531 gpointer iter = NULL;
5532 MonoProperty *p;
5534 nprops = mono_class_num_properties (klass);
5536 buffer_add_int (buf, nprops);
5538 while ((p = mono_class_get_properties (klass, &iter))) {
5539 buffer_add_propertyid (buf, domain, p);
5540 buffer_add_string (buf, p->name);
5541 buffer_add_methodid (buf, domain, p->get);
5542 buffer_add_methodid (buf, domain, p->set);
5543 buffer_add_int (buf, p->attrs);
5544 i ++;
5546 g_assert (i == nprops);
5547 break;
5549 case CMD_TYPE_GET_CATTRS: {
5550 MonoClass *attr_klass = decode_typeid (p, &p, end, NULL, &err);
5551 MonoCustomAttrInfo *cinfo;
5553 cinfo = mono_custom_attrs_from_class (klass);
5555 buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
5556 break;
5558 case CMD_TYPE_GET_FIELD_CATTRS: {
5559 MonoClass *attr_klass;
5560 MonoCustomAttrInfo *cinfo;
5561 MonoClassField *field;
5563 field = decode_fieldid (p, &p, end, NULL, &err);
5564 if (err)
5565 return err;
5566 attr_klass = decode_typeid (p, &p, end, NULL, &err);
5567 if (err)
5568 return err;
5570 cinfo = mono_custom_attrs_from_field (klass, field);
5572 buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
5573 break;
5575 case CMD_TYPE_GET_PROPERTY_CATTRS: {
5576 MonoClass *attr_klass;
5577 MonoCustomAttrInfo *cinfo;
5578 MonoProperty *prop;
5580 prop = decode_propertyid (p, &p, end, NULL, &err);
5581 if (err)
5582 return err;
5583 attr_klass = decode_typeid (p, &p, end, NULL, &err);
5584 if (err)
5585 return err;
5587 cinfo = mono_custom_attrs_from_property (klass, prop);
5589 buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
5590 break;
5592 case CMD_TYPE_GET_VALUES: {
5593 guint8 *val;
5594 MonoClassField *f;
5595 MonoVTable *vtable;
5596 MonoClass *k;
5597 int len, i;
5598 gboolean found;
5600 len = decode_int (p, &p, end);
5601 for (i = 0; i < len; ++i) {
5602 f = decode_fieldid (p, &p, end, NULL, &err);
5603 if (err)
5604 return err;
5606 if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC))
5607 return ERR_INVALID_FIELDID;
5608 if (mono_class_field_is_special_static (f))
5609 return ERR_INVALID_FIELDID;
5611 /* Check that the field belongs to the object */
5612 found = FALSE;
5613 for (k = klass; k; k = k->parent) {
5614 if (k == f->parent) {
5615 found = TRUE;
5616 break;
5619 if (!found)
5620 return ERR_INVALID_FIELDID;
5622 vtable = mono_class_vtable (domain, f->parent);
5623 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
5624 mono_field_static_get_value (vtable, f, val);
5625 buffer_add_value (buf, f->type, val, domain);
5626 g_free (val);
5628 break;
5630 case CMD_TYPE_SET_VALUES: {
5631 guint8 *val;
5632 MonoClassField *f;
5633 MonoVTable *vtable;
5634 MonoClass *k;
5635 int len, i;
5636 gboolean found;
5638 len = decode_int (p, &p, end);
5639 for (i = 0; i < len; ++i) {
5640 f = decode_fieldid (p, &p, end, NULL, &err);
5641 if (err)
5642 return err;
5644 if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC))
5645 return ERR_INVALID_FIELDID;
5646 if (mono_class_field_is_special_static (f))
5647 return ERR_INVALID_FIELDID;
5649 /* Check that the field belongs to the object */
5650 found = FALSE;
5651 for (k = klass; k; k = k->parent) {
5652 if (k == f->parent) {
5653 found = TRUE;
5654 break;
5657 if (!found)
5658 return ERR_INVALID_FIELDID;
5660 // FIXME: Check for literal/const
5662 vtable = mono_class_vtable (domain, f->parent);
5663 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
5664 err = decode_value (f->type, domain, val, p, &p, end);
5665 if (err) {
5666 g_free (val);
5667 return err;
5669 if (MONO_TYPE_IS_REFERENCE (f->type))
5670 mono_field_static_set_value (vtable, f, *(gpointer*)val);
5671 else
5672 mono_field_static_set_value (vtable, f, val);
5673 g_free (val);
5675 break;
5677 case CMD_TYPE_GET_OBJECT: {
5678 MonoObject *o = (MonoObject*)mono_type_get_object (mono_domain_get (), &klass->byval_arg);
5679 buffer_add_objid (buf, o);
5680 break;
5682 case CMD_TYPE_GET_SOURCE_FILES: {
5683 gpointer iter = NULL;
5684 MonoMethod *method;
5685 char *source_file, *base;
5686 GPtrArray *files;
5687 int i;
5689 files = g_ptr_array_new ();
5691 while ((method = mono_class_get_methods (klass, &iter))) {
5692 MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
5694 if (minfo) {
5695 mono_debug_symfile_get_line_numbers (minfo, &source_file, NULL, NULL, NULL);
5696 if (!source_file)
5697 continue;
5699 for (i = 0; i < files->len; ++i)
5700 if (!strcmp (g_ptr_array_index (files, i), source_file))
5701 break;
5702 if (i == files->len)
5703 g_ptr_array_add (files, g_strdup (source_file));
5704 g_free (source_file);
5708 buffer_add_int (buf, files->len);
5709 for (i = 0; i < files->len; ++i) {
5710 source_file = g_ptr_array_index (files, i);
5711 base = g_path_get_basename (source_file);
5712 buffer_add_string (buf, base);
5713 g_free (base);
5714 g_free (source_file);
5716 g_ptr_array_free (files, TRUE);
5717 break;
5719 case CMD_TYPE_IS_ASSIGNABLE_FROM: {
5720 MonoClass *oklass = decode_typeid (p, &p, end, NULL, &err);
5722 if (err)
5723 return err;
5724 if (mono_class_is_assignable_from (klass, oklass))
5725 buffer_add_byte (buf, 1);
5726 else
5727 buffer_add_byte (buf, 0);
5728 break;
5730 default:
5731 return ERR_NOT_IMPLEMENTED;
5734 return ERR_NONE;
5737 static ErrorCode
5738 method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
5740 int err;
5741 MonoDomain *domain;
5742 MonoMethod *method;
5743 MonoMethodHeader *header;
5745 method = decode_methodid (p, &p, end, &domain, &err);
5746 if (err)
5747 return err;
5749 switch (command) {
5750 case CMD_METHOD_GET_NAME: {
5751 buffer_add_string (buf, method->name);
5752 break;
5754 case CMD_METHOD_GET_DECLARING_TYPE: {
5755 buffer_add_typeid (buf, domain, method->klass);
5756 break;
5758 case CMD_METHOD_GET_DEBUG_INFO: {
5759 MonoDebugMethodInfo *minfo;
5760 char *source_file;
5761 int i, n_il_offsets;
5762 int *il_offsets;
5763 int *line_numbers;
5765 header = mono_method_get_header (method);
5766 if (!header) {
5767 buffer_add_int (buf, 0);
5768 buffer_add_string (buf, "");
5769 buffer_add_int (buf, 0);
5770 break;
5773 minfo = mono_debug_lookup_method (method);
5774 if (!minfo) {
5775 buffer_add_int (buf, header->code_size);
5776 buffer_add_string (buf, "");
5777 buffer_add_int (buf, 0);
5778 mono_metadata_free_mh (header);
5779 break;
5782 mono_debug_symfile_get_line_numbers (minfo, &source_file, &n_il_offsets, &il_offsets, &line_numbers);
5783 buffer_add_int (buf, header->code_size);
5784 buffer_add_string (buf, source_file);
5785 buffer_add_int (buf, n_il_offsets);
5786 //printf ("Line number table for method %s:\n", mono_method_full_name (method, TRUE));
5787 for (i = 0; i < n_il_offsets; ++i) {
5788 //printf ("IL%d -> %d\n", il_offsets [i], line_numbers [i]);
5789 buffer_add_int (buf, il_offsets [i]);
5790 buffer_add_int (buf, line_numbers [i]);
5792 g_free (source_file);
5793 g_free (il_offsets);
5794 g_free (line_numbers);
5795 mono_metadata_free_mh (header);
5796 break;
5798 case CMD_METHOD_GET_PARAM_INFO: {
5799 MonoMethodSignature *sig = mono_method_signature (method);
5800 guint32 i;
5801 char **names;
5803 /* FIXME: mono_class_from_mono_type () and byrefs */
5805 /* FIXME: Use a smaller encoding */
5806 buffer_add_int (buf, sig->call_convention);
5807 buffer_add_int (buf, sig->param_count);
5808 buffer_add_int (buf, sig->generic_param_count);
5809 buffer_add_typeid (buf, domain, mono_class_from_mono_type (sig->ret));
5810 for (i = 0; i < sig->param_count; ++i) {
5811 /* FIXME: vararg */
5812 buffer_add_typeid (buf, domain, mono_class_from_mono_type (sig->params [i]));
5815 /* Emit parameter names */
5816 names = g_new (char *, sig->param_count);
5817 mono_method_get_param_names (method, (const char **) names);
5818 for (i = 0; i < sig->param_count; ++i)
5819 buffer_add_string (buf, names [i]);
5820 g_free (names);
5822 break;
5824 case CMD_METHOD_GET_LOCALS_INFO: {
5825 int i, j, num_locals;
5826 char **local_names;
5827 int *local_indexes;
5829 header = mono_method_get_header (method);
5830 g_assert (header);
5832 buffer_add_int (buf, header->num_locals);
5834 /* Types */
5835 for (i = 0; i < header->num_locals; ++i)
5836 buffer_add_typeid (buf, domain, mono_class_from_mono_type (header->locals [i]));
5838 /* Names */
5839 num_locals = mono_debug_lookup_locals (method, &local_names, &local_indexes);
5840 for (i = 0; i < header->num_locals; ++i) {
5841 for (j = 0; j < num_locals; ++j)
5842 if (local_indexes [j] == i)
5843 break;
5844 if (j < num_locals)
5845 buffer_add_string (buf, local_names [j]);
5846 else
5847 buffer_add_string (buf, "");
5849 g_free (local_names);
5850 g_free (local_indexes);
5852 /* Live ranges */
5853 /* FIXME: This works because we set debug_options.mdb_optimizations */
5854 for (i = 0; i < header->num_locals; ++i) {
5855 buffer_add_int (buf, 0);
5856 buffer_add_int (buf, header->code_size);
5858 mono_metadata_free_mh (header);
5860 break;
5862 case CMD_METHOD_GET_INFO:
5863 buffer_add_int (buf, method->flags);
5864 buffer_add_int (buf, method->iflags);
5865 buffer_add_int (buf, method->token);
5866 break;
5867 case CMD_METHOD_GET_BODY: {
5868 int i;
5870 header = mono_method_get_header (method);
5871 if (!header) {
5872 buffer_add_int (buf, 0);
5873 } else {
5874 buffer_add_int (buf, header->code_size);
5875 for (i = 0; i < header->code_size; ++i)
5876 buffer_add_byte (buf, header->code [i]);
5878 mono_metadata_free_mh (header);
5879 break;
5881 case CMD_METHOD_RESOLVE_TOKEN: {
5882 guint32 token = decode_int (p, &p, end);
5884 // FIXME: Generics
5885 switch (mono_metadata_token_code (token)) {
5886 case MONO_TOKEN_STRING: {
5887 MonoString *s;
5888 char *s2;
5890 s = mono_ldstr (domain, method->klass->image, mono_metadata_token_index (token));
5891 g_assert (s);
5893 s2 = mono_string_to_utf8 (s);
5895 buffer_add_byte (buf, TOKEN_TYPE_STRING);
5896 buffer_add_string (buf, s2);
5897 g_free (s2);
5898 break;
5900 default: {
5901 gpointer val;
5902 MonoClass *handle_class;
5904 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
5905 val = mono_method_get_wrapper_data (method, token);
5906 handle_class = mono_method_get_wrapper_data (method, token + 1);
5908 if (handle_class == NULL) {
5909 // Can't figure out the token type
5910 buffer_add_byte (buf, TOKEN_TYPE_UNKNOWN);
5911 break;
5913 } else {
5914 val = mono_ldtoken (method->klass->image, token, &handle_class, NULL);
5915 g_assert (val);
5918 if (handle_class == mono_defaults.typehandle_class) {
5919 buffer_add_byte (buf, TOKEN_TYPE_TYPE);
5920 buffer_add_typeid (buf, domain, mono_class_from_mono_type ((MonoType*)val));
5921 } else if (handle_class == mono_defaults.fieldhandle_class) {
5922 buffer_add_byte (buf, TOKEN_TYPE_FIELD);
5923 buffer_add_fieldid (buf, domain, val);
5924 } else if (handle_class == mono_defaults.methodhandle_class) {
5925 buffer_add_byte (buf, TOKEN_TYPE_METHOD);
5926 buffer_add_methodid (buf, domain, val);
5927 } else if (handle_class == mono_defaults.string_class) {
5928 char *s;
5930 s = mono_string_to_utf8 (val);
5931 buffer_add_byte (buf, TOKEN_TYPE_STRING);
5932 buffer_add_string (buf, s);
5933 g_free (s);
5934 } else {
5935 g_assert_not_reached ();
5937 break;
5940 break;
5942 default:
5943 return ERR_NOT_IMPLEMENTED;
5946 return ERR_NONE;
5949 static ErrorCode
5950 thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
5952 int objid = decode_objid (p, &p, end);
5953 int err;
5954 MonoThread *thread_obj;
5955 MonoInternalThread *thread;
5957 err = get_object (objid, (MonoObject**)&thread_obj);
5958 if (err)
5959 return err;
5961 thread = THREAD_TO_INTERNAL (thread_obj);
5963 switch (command) {
5964 case CMD_THREAD_GET_NAME: {
5965 guint32 name_len;
5966 gunichar2 *s = mono_thread_get_name (thread, &name_len);
5968 if (!s) {
5969 buffer_add_int (buf, 0);
5970 } else {
5971 char *name;
5972 glong len;
5974 name = g_utf16_to_utf8 (s, name_len, NULL, &len, NULL);
5975 g_assert (name);
5976 buffer_add_int (buf, len);
5977 buffer_add_data (buf, (guint8*)name, len);
5978 g_free (s);
5980 break;
5982 case CMD_THREAD_GET_FRAME_INFO: {
5983 DebuggerTlsData *tls;
5984 int i, start_frame, length;
5986 // Wait for suspending if it already started
5987 if (suspend_count)
5988 wait_for_suspend ();
5989 if (!is_suspended ())
5990 return ERR_NOT_SUSPENDED;
5992 start_frame = decode_int (p, &p, end);
5993 length = decode_int (p, &p, end);
5995 if (start_frame != 0 || length != -1)
5996 return ERR_NOT_IMPLEMENTED;
5998 mono_loader_lock ();
5999 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
6000 mono_loader_unlock ();
6001 g_assert (tls);
6003 compute_frame_info (thread, tls);
6005 buffer_add_int (buf, tls->frame_count);
6006 for (i = 0; i < tls->frame_count; ++i) {
6007 buffer_add_int (buf, tls->frames [i]->id);
6008 buffer_add_methodid (buf, tls->frames [i]->domain, tls->frames [i]->method);
6009 buffer_add_int (buf, tls->frames [i]->il_offset);
6011 * Instead of passing the frame type directly to the client, we associate
6012 * it with the previous frame using a set of flags. This avoids lots of
6013 * conditional code in the client, since a frame whose type isn't
6014 * FRAME_TYPE_MANAGED has no method, location, etc.
6016 buffer_add_byte (buf, tls->frames [i]->flags);
6019 break;
6021 case CMD_THREAD_GET_STATE:
6022 buffer_add_int (buf, thread->state);
6023 break;
6024 case CMD_THREAD_GET_INFO:
6025 buffer_add_byte (buf, thread->threadpool_thread);
6026 break;
6027 default:
6028 return ERR_NOT_IMPLEMENTED;
6031 return ERR_NONE;
6034 static ErrorCode
6035 frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
6037 int objid;
6038 int err;
6039 MonoThread *thread_obj;
6040 MonoInternalThread *thread;
6041 int pos, i, len;
6042 DebuggerTlsData *tls;
6043 StackFrame *frame;
6044 MonoDebugMethodJitInfo *jit;
6045 MonoDebugVarInfo *var;
6046 MonoMethodSignature *sig;
6047 gssize id;
6048 MonoMethodHeader *header;
6050 objid = decode_objid (p, &p, end);
6051 err = get_object (objid, (MonoObject**)&thread_obj);
6052 if (err)
6053 return err;
6055 thread = THREAD_TO_INTERNAL (thread_obj);
6057 id = decode_id (p, &p, end);
6059 mono_loader_lock ();
6060 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
6061 mono_loader_unlock ();
6062 g_assert (tls);
6064 for (i = 0; i < tls->frame_count; ++i) {
6065 if (tls->frames [i]->id == id)
6066 break;
6068 if (i == tls->frame_count)
6069 return ERR_INVALID_FRAMEID;
6071 frame = tls->frames [i];
6073 if (!frame->has_ctx)
6074 // FIXME:
6075 return ERR_INVALID_FRAMEID;
6077 if (!frame->jit) {
6078 frame->jit = mono_debug_find_method (frame->method, frame->domain);
6079 g_assert (frame->jit);
6081 jit = frame->jit;
6083 sig = mono_method_signature (frame->method);
6085 switch (command) {
6086 case CMD_STACK_FRAME_GET_VALUES: {
6087 len = decode_int (p, &p, end);
6088 header = mono_method_get_header (frame->method);
6090 for (i = 0; i < len; ++i) {
6091 pos = decode_int (p, &p, end);
6093 if (pos < 0) {
6094 pos = - pos - 1;
6096 g_assert (pos >= 0 && pos < jit->num_params);
6098 var = &jit->params [pos];
6100 add_var (buf, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
6101 } else {
6102 g_assert (pos >= 0 && pos < jit->num_locals);
6104 var = &jit->locals [pos];
6106 add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
6109 mono_metadata_free_mh (header);
6110 break;
6112 case CMD_STACK_FRAME_GET_THIS: {
6113 if (frame->method->klass->valuetype) {
6114 if (!sig->hasthis) {
6115 MonoObject *p = NULL;
6116 buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
6117 } else {
6118 add_var (buf, &frame->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
6120 } else {
6121 if (!sig->hasthis) {
6122 MonoObject *p = NULL;
6123 buffer_add_value (buf, &frame->method->klass->byval_arg, &p, frame->domain);
6124 } else {
6125 add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
6128 break;
6130 case CMD_STACK_FRAME_SET_VALUES: {
6131 guint8 *val_buf;
6132 MonoType *t;
6133 MonoDebugVarInfo *var;
6135 len = decode_int (p, &p, end);
6136 header = mono_method_get_header (frame->method);
6138 for (i = 0; i < len; ++i) {
6139 pos = decode_int (p, &p, end);
6141 if (pos < 0) {
6142 pos = - pos - 1;
6144 g_assert (pos >= 0 && pos < jit->num_params);
6146 t = sig->params [pos];
6147 var = &jit->params [pos];
6148 } else {
6149 g_assert (pos >= 0 && pos < jit->num_locals);
6151 t = header->locals [pos];
6152 var = &jit->locals [pos];
6155 if (MONO_TYPE_IS_REFERENCE (t))
6156 val_buf = g_alloca (sizeof (MonoObject*));
6157 else
6158 val_buf = g_alloca (mono_class_instance_size (mono_class_from_mono_type (t)));
6159 err = decode_value (t, frame->domain, val_buf, p, &p, end);
6160 if (err)
6161 return err;
6163 set_var (t, var, &frame->ctx, frame->domain, val_buf);
6165 mono_metadata_free_mh (header);
6166 break;
6168 default:
6169 return ERR_NOT_IMPLEMENTED;
6172 return ERR_NONE;
6175 static ErrorCode
6176 array_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
6178 MonoArray *arr;
6179 int objid, err, index, len, i, esize;
6180 gpointer elem;
6182 objid = decode_objid (p, &p, end);
6183 err = get_object (objid, (MonoObject**)&arr);
6184 if (err)
6185 return err;
6187 switch (command) {
6188 case CMD_ARRAY_REF_GET_LENGTH:
6189 buffer_add_int (buf, arr->obj.vtable->klass->rank);
6190 if (!arr->bounds) {
6191 buffer_add_int (buf, arr->max_length);
6192 buffer_add_int (buf, 0);
6193 } else {
6194 for (i = 0; i < arr->obj.vtable->klass->rank; ++i) {
6195 buffer_add_int (buf, arr->bounds [i].length);
6196 buffer_add_int (buf, arr->bounds [i].lower_bound);
6199 break;
6200 case CMD_ARRAY_REF_GET_VALUES:
6201 index = decode_int (p, &p, end);
6202 len = decode_int (p, &p, end);
6204 g_assert (index >= 0 && len >= 0);
6205 // Reordered to avoid integer overflow
6206 g_assert (!(index > arr->max_length - len));
6208 esize = mono_array_element_size (arr->obj.vtable->klass);
6209 for (i = index; i < index + len; ++i) {
6210 elem = (gpointer*)((char*)arr->vector + (i * esize));
6211 buffer_add_value (buf, &arr->obj.vtable->klass->element_class->byval_arg, elem, arr->obj.vtable->domain);
6213 break;
6214 case CMD_ARRAY_REF_SET_VALUES:
6215 index = decode_int (p, &p, end);
6216 len = decode_int (p, &p, end);
6218 g_assert (index >= 0 && len >= 0);
6219 // Reordered to avoid integer overflow
6220 g_assert (!(index > arr->max_length - len));
6222 esize = mono_array_element_size (arr->obj.vtable->klass);
6223 for (i = index; i < index + len; ++i) {
6224 elem = (gpointer*)((char*)arr->vector + (i * esize));
6226 decode_value (&arr->obj.vtable->klass->element_class->byval_arg, arr->obj.vtable->domain, elem, p, &p, end);
6228 break;
6229 default:
6230 return ERR_NOT_IMPLEMENTED;
6233 return ERR_NONE;
6236 static ErrorCode
6237 string_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
6239 int objid, err;
6240 MonoString *str;
6241 char *s;
6243 objid = decode_objid (p, &p, end);
6244 err = get_object (objid, (MonoObject**)&str);
6245 if (err)
6246 return err;
6248 switch (command) {
6249 case CMD_STRING_REF_GET_VALUE:
6250 s = mono_string_to_utf8 (str);
6251 buffer_add_string (buf, s);
6252 g_free (s);
6253 break;
6254 default:
6255 return ERR_NOT_IMPLEMENTED;
6258 return ERR_NONE;
6261 static ErrorCode
6262 object_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
6264 int objid, err;
6265 MonoObject *obj;
6266 int len, i;
6267 MonoClassField *f;
6268 MonoClass *k;
6269 gboolean found;
6271 if (command == CMD_OBJECT_REF_IS_COLLECTED) {
6272 objid = decode_objid (p, &p, end);
6273 err = get_object (objid, &obj);
6274 if (err)
6275 buffer_add_int (buf, 1);
6276 else
6277 buffer_add_int (buf, 0);
6278 return 0;
6281 objid = decode_objid (p, &p, end);
6282 err = get_object (objid, &obj);
6283 if (err)
6284 return err;
6286 switch (command) {
6287 case CMD_OBJECT_REF_GET_TYPE:
6288 buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass);
6289 break;
6290 case CMD_OBJECT_REF_GET_VALUES:
6291 len = decode_int (p, &p, end);
6293 for (i = 0; i < len; ++i) {
6294 MonoClassField *f = decode_fieldid (p, &p, end, NULL, &err);
6295 if (err)
6296 return err;
6298 /* Check that the field belongs to the object */
6299 found = FALSE;
6300 for (k = obj->vtable->klass; k; k = k->parent) {
6301 if (k == f->parent) {
6302 found = TRUE;
6303 break;
6306 if (!found)
6307 return ERR_INVALID_FIELDID;
6309 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) {
6310 guint8 *val;
6311 MonoVTable *vtable;
6313 if (mono_class_field_is_special_static (f))
6314 return ERR_INVALID_FIELDID;
6316 g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC);
6317 vtable = mono_class_vtable (obj->vtable->domain, f->parent);
6318 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
6319 mono_field_static_get_value (vtable, f, val);
6320 buffer_add_value (buf, f->type, val, obj->vtable->domain);
6321 g_free (val);
6322 } else {
6323 buffer_add_value (buf, f->type, (guint8*)obj + f->offset, obj->vtable->domain);
6326 break;
6327 case CMD_OBJECT_REF_SET_VALUES:
6328 len = decode_int (p, &p, end);
6330 for (i = 0; i < len; ++i) {
6331 f = decode_fieldid (p, &p, end, NULL, &err);
6332 if (err)
6333 return err;
6335 /* Check that the field belongs to the object */
6336 found = FALSE;
6337 for (k = obj->vtable->klass; k; k = k->parent) {
6338 if (k == f->parent) {
6339 found = TRUE;
6340 break;
6343 if (!found)
6344 return ERR_INVALID_FIELDID;
6346 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) {
6347 guint8 *val;
6348 MonoVTable *vtable;
6350 if (mono_class_field_is_special_static (f))
6351 return ERR_INVALID_FIELDID;
6353 g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC);
6354 vtable = mono_class_vtable (obj->vtable->domain, f->parent);
6356 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
6357 err = decode_value (f->type, obj->vtable->domain, val, p, &p, end);
6358 if (err) {
6359 g_free (val);
6360 return err;
6362 mono_field_static_set_value (vtable, f, val);
6363 g_free (val);
6364 } else {
6365 err = decode_value (f->type, obj->vtable->domain, (guint8*)obj + f->offset, p, &p, end);
6366 if (err)
6367 return err;
6370 break;
6371 case CMD_OBJECT_REF_GET_ADDRESS:
6372 buffer_add_long (buf, (gssize)obj);
6373 break;
6374 case CMD_OBJECT_REF_GET_DOMAIN:
6375 buffer_add_domainid (buf, obj->vtable->domain);
6376 break;
6377 default:
6378 return ERR_NOT_IMPLEMENTED;
6381 return ERR_NONE;
6384 static const char*
6385 command_set_to_string (CommandSet command_set)
6387 switch (command_set) {
6388 case CMD_SET_VM:
6389 return "VM";
6390 case CMD_SET_OBJECT_REF:
6391 return "OBJECT_REF";
6392 case CMD_SET_STRING_REF:
6393 return "STRING_REF";
6394 case CMD_SET_THREAD:
6395 return "THREAD";
6396 case CMD_SET_ARRAY_REF:
6397 return "ARRAY_REF";
6398 case CMD_SET_EVENT_REQUEST:
6399 return "EVENT_REQUEST";
6400 case CMD_SET_STACK_FRAME:
6401 return "STACK_FRAME";
6402 case CMD_SET_APPDOMAIN:
6403 return "APPDOMAIN";
6404 case CMD_SET_ASSEMBLY:
6405 return "ASSEMBLY";
6406 case CMD_SET_METHOD:
6407 return "METHOD";
6408 case CMD_SET_TYPE:
6409 return "TYPE";
6410 case CMD_SET_MODULE:
6411 return "MODULE";
6412 case CMD_SET_EVENT:
6413 return "EVENT";
6414 default:
6415 return "";
6420 * debugger_thread:
6422 * This thread handles communication with the debugger client using a JDWP
6423 * like protocol.
6425 static guint32 WINAPI
6426 debugger_thread (void *arg)
6428 int res, len, id, flags, command_set, command;
6429 guint8 header [HEADER_LENGTH];
6430 guint8 *data, *p, *end;
6431 Buffer buf;
6432 ErrorCode err;
6433 gboolean no_reply;
6435 DEBUG (1, fprintf (log_file, "[dbg] Agent thread started, pid=%p\n", (gpointer)GetCurrentThreadId ()));
6437 debugger_thread_id = GetCurrentThreadId ();
6439 mono_jit_thread_attach (mono_get_root_domain ());
6441 mono_thread_internal_current ()->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
6443 mono_set_is_debugger_attached (TRUE);
6445 while (TRUE) {
6446 res = recv_length (conn_fd, header, HEADER_LENGTH, 0);
6448 /* This will break if the socket is closed during shutdown too */
6449 if (res != HEADER_LENGTH)
6450 break;
6452 p = header;
6453 end = header + HEADER_LENGTH;
6455 len = decode_int (p, &p, end);
6456 id = decode_int (p, &p, end);
6457 flags = decode_byte (p, &p, end);
6458 command_set = decode_byte (p, &p, end);
6459 command = decode_byte (p, &p, end);
6461 g_assert (flags == 0);
6463 DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%d), id=%d.\n", command_set_to_string (command_set), command, id));
6465 data = g_malloc (len - HEADER_LENGTH);
6466 if (len - HEADER_LENGTH > 0)
6468 res = recv_length (conn_fd, data, len - HEADER_LENGTH, 0);
6469 if (res != len - HEADER_LENGTH)
6470 break;
6473 p = data;
6474 end = data + (len - HEADER_LENGTH);
6476 buffer_init (&buf, 128);
6478 err = ERR_NONE;
6479 no_reply = FALSE;
6481 /* Process the request */
6482 switch (command_set) {
6483 case CMD_SET_VM:
6484 err = vm_commands (command, id, p, end, &buf);
6485 if (!err && command == CMD_VM_INVOKE_METHOD)
6486 /* Sent after the invoke is complete */
6487 no_reply = TRUE;
6488 break;
6489 case CMD_SET_EVENT_REQUEST:
6490 err = event_commands (command, p, end, &buf);
6491 break;
6492 case CMD_SET_APPDOMAIN:
6493 err = domain_commands (command, p, end, &buf);
6494 break;
6495 case CMD_SET_ASSEMBLY:
6496 err = assembly_commands (command, p, end, &buf);
6497 break;
6498 case CMD_SET_MODULE:
6499 err = module_commands (command, p, end, &buf);
6500 break;
6501 case CMD_SET_TYPE:
6502 err = type_commands (command, p, end, &buf);
6503 break;
6504 case CMD_SET_METHOD:
6505 err = method_commands (command, p, end, &buf);
6506 break;
6507 case CMD_SET_THREAD:
6508 err = thread_commands (command, p, end, &buf);
6509 break;
6510 case CMD_SET_STACK_FRAME:
6511 err = frame_commands (command, p, end, &buf);
6512 break;
6513 case CMD_SET_ARRAY_REF:
6514 err = array_commands (command, p, end, &buf);
6515 break;
6516 case CMD_SET_STRING_REF:
6517 err = string_commands (command, p, end, &buf);
6518 break;
6519 case CMD_SET_OBJECT_REF:
6520 err = object_commands (command, p, end, &buf);
6521 break;
6522 default:
6523 err = ERR_NOT_IMPLEMENTED;
6526 if (!no_reply)
6527 send_reply_packet (id, err, &buf);
6529 g_free (data);
6530 buffer_free (&buf);
6532 if (command_set == CMD_SET_VM && command == CMD_VM_DISPOSE)
6533 break;
6536 mono_set_is_debugger_attached (FALSE);
6538 mono_mutex_lock (&debugger_thread_exited_mutex);
6539 debugger_thread_exited = TRUE;
6540 mono_cond_signal (&debugger_thread_exited_cond);
6541 mono_mutex_unlock (&debugger_thread_exited_mutex);
6543 DEBUG (1, printf ("[dbg] Debugger thread exited.\n"));
6545 return 0;
6548 #else /* DISABLE_DEBUGGER_AGENT */
6550 void
6551 mono_debugger_agent_parse_options (char *options)
6553 g_error ("This runtime is configure with the debugger agent disabled.");
6556 void
6557 mono_debugger_agent_init (void)
6561 void
6562 mono_debugger_agent_breakpoint_hit (void *sigctx)
6566 void
6567 mono_debugger_agent_single_step_event (void *sigctx)
6571 void
6572 mono_debugger_agent_free_domain_info (MonoDomain *domain)
6576 gboolean
6577 mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
6579 return FALSE;
6582 void
6583 mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *throw_ctx,
6584 MonoContext *catch_ctx)
6587 #endif