[sdb] Add ability to buffer reply packets in the wire protocol when running on high...
[mono-project.git] / mono / mini / debugger-agent.c
blob9b7a721944be14cc44c6e22e103fb69dc48d0a2e
1 /*
2 * debugger-agent.c: Soft Debugger back-end module
4 * Author:
5 * Zoltan Varga (vargaz@gmail.com)
7 * Copyright 2009-2010 Novell, Inc.
8 * Copyright 2011 Xamarin Inc.
9 */
11 #include <config.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #ifdef HAVE_SYS_TYPES_H
16 #include <sys/types.h>
17 #endif
18 #ifdef HAVE_SYS_SELECT_H
19 #include <sys/select.h>
20 #endif
21 #ifdef HAVE_SYS_SOCKET_H
22 #include <sys/socket.h>
23 #endif
24 #ifdef HAVE_NETINET_TCP_H
25 #include <netinet/tcp.h>
26 #endif
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
29 #endif
30 #ifdef HAVE_NETDB_H
31 #include <netdb.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #include <errno.h>
37 #include <glib.h>
39 #ifdef HAVE_PTHREAD_H
40 #include <pthread.h>
41 #endif
43 #ifdef HAVE_UCONTEXT_H
44 #include <ucontext.h>
45 #endif
47 #ifdef HOST_WIN32
48 #ifdef _MSC_VER
49 #include <winsock2.h>
50 #endif
51 #include <ws2tcpip.h>
52 #ifdef __GNUC__
53 /* cygwin's headers do not seem to define these */
54 void WSAAPI freeaddrinfo (struct addrinfo*);
55 int WSAAPI getaddrinfo (const char*,const char*,const struct addrinfo*,
56 struct addrinfo**);
57 int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
58 char*,DWORD,int);
59 #endif
60 #endif
62 #ifdef PLATFORM_ANDROID
63 #include <linux/in.h>
64 #include <linux/tcp.h>
65 #include <sys/endian.h>
66 #endif
68 #include <mono/metadata/mono-debug.h>
69 #include <mono/metadata/mono-debug-debugger.h>
70 #include <mono/metadata/debug-mono-symfile.h>
71 #include <mono/metadata/gc-internal.h>
72 #include <mono/metadata/environment.h>
73 #include <mono/metadata/threads-types.h>
74 #include <mono/metadata/socket-io.h>
75 #include <mono/metadata/assembly.h>
76 #include <mono/metadata/runtime.h>
77 #include <mono/metadata/threadpool.h>
78 #include <mono/metadata/verify-internals.h>
79 #include <mono/utils/mono-semaphore.h>
80 #include <mono/utils/mono-error-internals.h>
81 #include <mono/utils/mono-stack-unwinding.h>
82 #include <mono/utils/mono-time.h>
83 #include <mono/utils/mono-threads.h>
84 #include "debugger-agent.h"
85 #include "mini.h"
88 On iOS we can't use System.Environment.Exit () as it will do the wrong
89 shutdown sequence.
91 #if !defined (TARGET_IOS)
92 #define TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
93 #endif
96 #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
97 #define DISABLE_DEBUGGER_AGENT 1
98 #endif
100 #ifdef DISABLE_SOFT_DEBUG
101 #define DISABLE_DEBUGGER_AGENT 1
102 #endif
104 #ifndef DISABLE_DEBUGGER_AGENT
106 #include <mono/utils/mono-mutex.h>
108 /* Definitions to make backporting to 2.6 easier */
109 //#define MonoInternalThread MonoThread
110 //#define mono_thread_internal_current mono_thread_current
111 #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
113 typedef struct {
114 gboolean enabled;
115 char *transport;
116 char *address;
117 int log_level;
118 char *log_file;
119 gboolean suspend;
120 gboolean server;
121 gboolean onuncaught;
122 GSList *onthrow;
123 int timeout;
124 char *launch;
125 gboolean embedding;
126 gboolean defer;
127 int keepalive;
128 gboolean setpgid;
129 } AgentConfig;
131 typedef struct
133 int id;
134 guint32 il_offset, native_offset;
135 MonoDomain *domain;
136 MonoMethod *method;
138 * If method is gshared, this is the actual instance, otherwise this is equal to
139 * method.
141 MonoMethod *actual_method;
143 * This is the method which is visible to debugger clients. Same as method,
144 * except for native-to-managed wrappers.
146 MonoMethod *api_method;
147 MonoContext ctx;
148 MonoDebugMethodJitInfo *jit;
149 MonoJitInfo *ji;
150 int flags;
151 mgreg_t *reg_locations [MONO_MAX_IREGS];
153 * Whenever ctx is set. This is FALSE for the last frame of running threads, since
154 * the frame can become invalid.
156 gboolean has_ctx;
157 } StackFrame;
159 typedef struct _InvokeData InvokeData;
161 struct _InvokeData
163 int id;
164 int flags;
165 guint8 *p;
166 guint8 *endp;
167 /* This is the context which needs to be restored after the invoke */
168 MonoContext ctx;
169 gboolean has_ctx;
171 * If this is set, invoke this method with the arguments given by ARGS.
173 MonoMethod *method;
174 gpointer *args;
175 guint32 suspend_count;
176 int nmethods;
178 InvokeData *last_invoke;
181 typedef struct {
182 MonoThreadUnwindState context;
184 /* This is computed on demand when it is requested using the wire protocol */
185 /* It is freed up when the thread is resumed */
186 int frame_count;
187 StackFrame **frames;
189 * Whenever the frame info is up-to-date. If not, compute_frame_info () will need to
190 * re-compute it.
192 gboolean frames_up_to_date;
194 * Points to data about a pending invoke which needs to be executed after the thread
195 * resumes.
197 InvokeData *pending_invoke;
199 * Set to TRUE if this thread is suspended in suspend_current () or it is executing
200 * native code.
202 gboolean suspended;
204 * Signals whenever the thread is in the process of suspending, i.e. it will suspend
205 * within a finite amount of time.
207 gboolean suspending;
209 * Set to TRUE if this thread is suspended in suspend_current ().
211 gboolean really_suspended;
212 /* Used to pass the context to the breakpoint/single step handler */
213 MonoContext handler_ctx;
214 /* Whenever thread_stop () was called for this thread */
215 gboolean terminated;
217 /* Number of thread interruptions not yet processed */
218 gint32 interrupt_count;
220 /* Whenever to disable breakpoints (used during invokes) */
221 gboolean disable_breakpoints;
224 * Number of times this thread has been resumed using resume_thread ().
226 guint32 resume_count;
228 MonoInternalThread *thread;
231 * Information about the frame which transitioned to native code for running
232 * threads.
234 StackFrameInfo async_last_frame;
237 * The context where the stack walk can be started for running threads.
239 MonoThreadUnwindState async_state;
242 * The context used for filter clauses
244 MonoThreadUnwindState filter_state;
247 * The callee address of the last mono_runtime_invoke call
249 gpointer invoke_addr;
251 gboolean abort_requested;
254 * The current mono_runtime_invoke invocation.
256 InvokeData *invoke;
259 * The context where single stepping should resume while the thread is suspended because
260 * of an EXCEPTION event.
262 MonoThreadUnwindState catch_state;
265 * The context which needs to be restored after handling a single step/breakpoint
266 * event. This is the same as the ctx at step/breakpoint site, but includes changes
267 * to caller saved registers done by set_var ().
269 MonoContext restore_ctx;
270 } DebuggerTlsData;
272 typedef struct {
273 const char *name;
274 void (*connect) (const char *address);
275 void (*close1) (void);
276 void (*close2) (void);
277 gboolean (*send) (void *buf, int len);
278 int (*recv) (void *buf, int len);
279 } DebuggerTransport;
282 * Wire Protocol definitions
285 #define HEADER_LENGTH 11
287 #define MAJOR_VERSION 2
288 #define MINOR_VERSION 34
290 typedef enum {
291 CMD_SET_VM = 1,
292 CMD_SET_OBJECT_REF = 9,
293 CMD_SET_STRING_REF = 10,
294 CMD_SET_THREAD = 11,
295 CMD_SET_ARRAY_REF = 13,
296 CMD_SET_EVENT_REQUEST = 15,
297 CMD_SET_STACK_FRAME = 16,
298 CMD_SET_APPDOMAIN = 20,
299 CMD_SET_ASSEMBLY = 21,
300 CMD_SET_METHOD = 22,
301 CMD_SET_TYPE = 23,
302 CMD_SET_MODULE = 24,
303 CMD_SET_FIELD = 25,
304 CMD_SET_EVENT = 64
305 } CommandSet;
307 typedef enum {
308 EVENT_KIND_VM_START = 0,
309 EVENT_KIND_VM_DEATH = 1,
310 EVENT_KIND_THREAD_START = 2,
311 EVENT_KIND_THREAD_DEATH = 3,
312 EVENT_KIND_APPDOMAIN_CREATE = 4,
313 EVENT_KIND_APPDOMAIN_UNLOAD = 5,
314 EVENT_KIND_METHOD_ENTRY = 6,
315 EVENT_KIND_METHOD_EXIT = 7,
316 EVENT_KIND_ASSEMBLY_LOAD = 8,
317 EVENT_KIND_ASSEMBLY_UNLOAD = 9,
318 EVENT_KIND_BREAKPOINT = 10,
319 EVENT_KIND_STEP = 11,
320 EVENT_KIND_TYPE_LOAD = 12,
321 EVENT_KIND_EXCEPTION = 13,
322 EVENT_KIND_KEEPALIVE = 14,
323 EVENT_KIND_USER_BREAK = 15,
324 EVENT_KIND_USER_LOG = 16
325 } EventKind;
327 typedef enum {
328 SUSPEND_POLICY_NONE = 0,
329 SUSPEND_POLICY_EVENT_THREAD = 1,
330 SUSPEND_POLICY_ALL = 2
331 } SuspendPolicy;
333 typedef enum {
334 ERR_NONE = 0,
335 ERR_INVALID_OBJECT = 20,
336 ERR_INVALID_FIELDID = 25,
337 ERR_INVALID_FRAMEID = 30,
338 ERR_NOT_IMPLEMENTED = 100,
339 ERR_NOT_SUSPENDED = 101,
340 ERR_INVALID_ARGUMENT = 102,
341 ERR_UNLOADED = 103,
342 ERR_NO_INVOCATION = 104,
343 ERR_ABSENT_INFORMATION = 105,
344 ERR_NO_SEQ_POINT_AT_IL_OFFSET = 106,
345 ERR_LOADER_ERROR = 200, /*XXX extend the protocol to pass this information down the pipe */
346 } ErrorCode;
348 typedef enum {
349 MOD_KIND_COUNT = 1,
350 MOD_KIND_THREAD_ONLY = 3,
351 MOD_KIND_LOCATION_ONLY = 7,
352 MOD_KIND_EXCEPTION_ONLY = 8,
353 MOD_KIND_STEP = 10,
354 MOD_KIND_ASSEMBLY_ONLY = 11,
355 MOD_KIND_SOURCE_FILE_ONLY = 12,
356 MOD_KIND_TYPE_NAME_ONLY = 13
357 } ModifierKind;
359 typedef enum {
360 STEP_DEPTH_INTO = 0,
361 STEP_DEPTH_OVER = 1,
362 STEP_DEPTH_OUT = 2
363 } StepDepth;
365 typedef enum {
366 STEP_SIZE_MIN = 0,
367 STEP_SIZE_LINE = 1
368 } StepSize;
370 typedef enum {
371 STEP_FILTER_NONE = 0,
372 STEP_FILTER_STATIC_CTOR = 1,
373 STEP_FILTER_DEBUGGER_HIDDEN = 2,
374 STEP_FILTER_DEBUGGER_STEP_THROUGH = 4,
375 STEP_FILTER_DEBUGGER_NON_USER_CODE = 8
376 } StepFilter;
378 typedef enum {
379 TOKEN_TYPE_STRING = 0,
380 TOKEN_TYPE_TYPE = 1,
381 TOKEN_TYPE_FIELD = 2,
382 TOKEN_TYPE_METHOD = 3,
383 TOKEN_TYPE_UNKNOWN = 4
384 } DebuggerTokenType;
386 typedef enum {
387 VALUE_TYPE_ID_NULL = 0xf0,
388 VALUE_TYPE_ID_TYPE = 0xf1,
389 VALUE_TYPE_ID_PARENT_VTYPE = 0xf2
390 } ValueTypeId;
392 typedef enum {
393 FRAME_FLAG_DEBUGGER_INVOKE = 1,
394 FRAME_FLAG_NATIVE_TRANSITION = 2
395 } StackFrameFlags;
397 typedef enum {
398 INVOKE_FLAG_DISABLE_BREAKPOINTS = 1,
399 INVOKE_FLAG_SINGLE_THREADED = 2
400 } InvokeFlags;
402 typedef enum {
403 BINDING_FLAGS_IGNORE_CASE = 0x70000000,
404 } BindingFlagsExtensions;
406 typedef enum {
407 CMD_VM_VERSION = 1,
408 CMD_VM_ALL_THREADS = 2,
409 CMD_VM_SUSPEND = 3,
410 CMD_VM_RESUME = 4,
411 CMD_VM_EXIT = 5,
412 CMD_VM_DISPOSE = 6,
413 CMD_VM_INVOKE_METHOD = 7,
414 CMD_VM_SET_PROTOCOL_VERSION = 8,
415 CMD_VM_ABORT_INVOKE = 9,
416 CMD_VM_SET_KEEPALIVE = 10,
417 CMD_VM_GET_TYPES_FOR_SOURCE_FILE = 11,
418 CMD_VM_GET_TYPES = 12,
419 CMD_VM_INVOKE_METHODS = 13,
420 CMD_VM_START_BUFFERING = 14,
421 CMD_VM_STOP_BUFFERING = 15
422 } CmdVM;
424 typedef enum {
425 CMD_THREAD_GET_FRAME_INFO = 1,
426 CMD_THREAD_GET_NAME = 2,
427 CMD_THREAD_GET_STATE = 3,
428 CMD_THREAD_GET_INFO = 4,
429 CMD_THREAD_GET_ID = 5,
430 CMD_THREAD_GET_TID = 6,
431 CMD_THREAD_SET_IP = 7
432 } CmdThread;
434 typedef enum {
435 CMD_EVENT_REQUEST_SET = 1,
436 CMD_EVENT_REQUEST_CLEAR = 2,
437 CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS = 3
438 } CmdEvent;
440 typedef enum {
441 CMD_COMPOSITE = 100
442 } CmdComposite;
444 typedef enum {
445 CMD_APPDOMAIN_GET_ROOT_DOMAIN = 1,
446 CMD_APPDOMAIN_GET_FRIENDLY_NAME = 2,
447 CMD_APPDOMAIN_GET_ASSEMBLIES = 3,
448 CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4,
449 CMD_APPDOMAIN_CREATE_STRING = 5,
450 CMD_APPDOMAIN_GET_CORLIB = 6,
451 CMD_APPDOMAIN_CREATE_BOXED_VALUE = 7
452 } CmdAppDomain;
454 typedef enum {
455 CMD_ASSEMBLY_GET_LOCATION = 1,
456 CMD_ASSEMBLY_GET_ENTRY_POINT = 2,
457 CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3,
458 CMD_ASSEMBLY_GET_OBJECT = 4,
459 CMD_ASSEMBLY_GET_TYPE = 5,
460 CMD_ASSEMBLY_GET_NAME = 6
461 } CmdAssembly;
463 typedef enum {
464 CMD_MODULE_GET_INFO = 1,
465 } CmdModule;
467 typedef enum {
468 CMD_FIELD_GET_INFO = 1,
469 } CmdField;
471 typedef enum {
472 CMD_METHOD_GET_NAME = 1,
473 CMD_METHOD_GET_DECLARING_TYPE = 2,
474 CMD_METHOD_GET_DEBUG_INFO = 3,
475 CMD_METHOD_GET_PARAM_INFO = 4,
476 CMD_METHOD_GET_LOCALS_INFO = 5,
477 CMD_METHOD_GET_INFO = 6,
478 CMD_METHOD_GET_BODY = 7,
479 CMD_METHOD_RESOLVE_TOKEN = 8,
480 CMD_METHOD_GET_CATTRS = 9,
481 CMD_METHOD_MAKE_GENERIC_METHOD = 10
482 } CmdMethod;
484 typedef enum {
485 CMD_TYPE_GET_INFO = 1,
486 CMD_TYPE_GET_METHODS = 2,
487 CMD_TYPE_GET_FIELDS = 3,
488 CMD_TYPE_GET_VALUES = 4,
489 CMD_TYPE_GET_OBJECT = 5,
490 CMD_TYPE_GET_SOURCE_FILES = 6,
491 CMD_TYPE_SET_VALUES = 7,
492 CMD_TYPE_IS_ASSIGNABLE_FROM = 8,
493 CMD_TYPE_GET_PROPERTIES = 9,
494 CMD_TYPE_GET_CATTRS = 10,
495 CMD_TYPE_GET_FIELD_CATTRS = 11,
496 CMD_TYPE_GET_PROPERTY_CATTRS = 12,
497 CMD_TYPE_GET_SOURCE_FILES_2 = 13,
498 CMD_TYPE_GET_VALUES_2 = 14,
499 CMD_TYPE_GET_METHODS_BY_NAME_FLAGS = 15,
500 CMD_TYPE_GET_INTERFACES = 16,
501 CMD_TYPE_GET_INTERFACE_MAP = 17,
502 CMD_TYPE_IS_INITIALIZED = 18,
503 CMD_TYPE_CREATE_INSTANCE = 19
504 } CmdType;
506 typedef enum {
507 CMD_STACK_FRAME_GET_VALUES = 1,
508 CMD_STACK_FRAME_GET_THIS = 2,
509 CMD_STACK_FRAME_SET_VALUES = 3
510 } CmdStackFrame;
512 typedef enum {
513 CMD_ARRAY_REF_GET_LENGTH = 1,
514 CMD_ARRAY_REF_GET_VALUES = 2,
515 CMD_ARRAY_REF_SET_VALUES = 3,
516 } CmdArray;
518 typedef enum {
519 CMD_STRING_REF_GET_VALUE = 1,
520 CMD_STRING_REF_GET_LENGTH = 2,
521 CMD_STRING_REF_GET_CHARS = 3
522 } CmdString;
524 typedef enum {
525 CMD_OBJECT_REF_GET_TYPE = 1,
526 CMD_OBJECT_REF_GET_VALUES = 2,
527 CMD_OBJECT_REF_IS_COLLECTED = 3,
528 CMD_OBJECT_REF_GET_ADDRESS = 4,
529 CMD_OBJECT_REF_GET_DOMAIN = 5,
530 CMD_OBJECT_REF_SET_VALUES = 6,
531 CMD_OBJECT_REF_GET_INFO = 7,
532 } CmdObject;
534 typedef struct {
535 ModifierKind kind;
536 union {
537 int count; /* For kind == MOD_KIND_COUNT */
538 MonoInternalThread *thread; /* For kind == MOD_KIND_THREAD_ONLY */
539 MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */
540 MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */
541 GHashTable *source_files; /* For kind == MONO_KIND_SOURCE_FILE_ONLY */
542 GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */
543 StepFilter filter; /* For kind == MOD_KIND_STEP */
544 } data;
545 gboolean caught, uncaught, subclasses; /* For kind == MOD_KIND_EXCEPTION_ONLY */
546 } Modifier;
548 typedef struct{
549 int id;
550 int event_kind;
551 int suspend_policy;
552 int nmodifiers;
553 gpointer info;
554 Modifier modifiers [MONO_ZERO_LEN_ARRAY];
555 } EventRequest;
558 * Describes a single step request.
560 typedef struct {
561 EventRequest *req;
562 MonoInternalThread *thread;
563 StepDepth depth;
564 StepSize size;
565 StepFilter filter;
566 gpointer last_sp;
567 gpointer start_sp;
568 MonoMethod *last_method;
569 int last_line;
570 /* Whenever single stepping is performed using start/stop_single_stepping () */
571 gboolean global;
572 /* The list of breakpoints used to implement step-over */
573 GSList *bps;
574 /* The number of frames at the start of a step-over */
575 int nframes;
576 } SingleStepReq;
579 * Contains additional information for an event
581 typedef struct {
582 /* For EVENT_KIND_EXCEPTION */
583 MonoObject *exc;
584 MonoContext catch_ctx;
585 gboolean caught;
586 /* For EVENT_KIND_USER_LOG */
587 int level;
588 char *category, *message;
589 /* For EVENT_KIND_TYPE_LOAD */
590 MonoClass *klass;
591 } EventInfo;
593 /* Dummy structure used for the profiler callbacks */
594 typedef struct {
595 void* dummy;
596 } DebuggerProfiler;
598 typedef struct {
599 guint8 *buf, *p, *end;
600 } Buffer;
602 typedef struct ReplyPacket {
603 int id;
604 int error;
605 Buffer *data;
606 } ReplyPacket;
608 #define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; fflush (log_file); } } while (0)
610 #ifdef HOST_WIN32
611 #define get_last_sock_error() WSAGetLastError()
612 #define MONO_EWOULDBLOCK WSAEWOULDBLOCK
613 #define MONO_EINTR WSAEINTR
614 #else
615 #define get_last_sock_error() errno
616 #define MONO_EWOULDBLOCK EWOULDBLOCK
617 #define MONO_EINTR EINTR
618 #endif
620 #define CHECK_PROTOCOL_VERSION(major,minor) \
621 (protocol_version_set && (major_version > (major) || (major_version == (major) && minor_version >= (minor))))
624 * Globals
627 static AgentConfig agent_config;
630 * Whenever the agent is fully initialized.
631 * When using the onuncaught or onthrow options, only some parts of the agent are
632 * initialized on startup, and the full initialization which includes connection
633 * establishment and the startup of the agent thread is only done in response to
634 * an event.
636 static gint32 inited;
638 #ifndef DISABLE_SOCKET_TRANSPORT
639 static int conn_fd;
640 static int listen_fd;
641 #endif
643 static int packet_id = 0;
645 static int objref_id = 0;
647 static int event_request_id = 0;
649 static int frame_id = 0;
651 static GPtrArray *event_requests;
653 static MonoNativeTlsKey debugger_tls_id;
655 static gboolean vm_start_event_sent, vm_death_event_sent, disconnected;
657 /* Maps MonoInternalThread -> DebuggerTlsData */
658 static MonoGHashTable *thread_to_tls;
660 /* Maps tid -> MonoInternalThread */
661 static MonoGHashTable *tid_to_thread;
663 /* Maps tid -> MonoThread (not MonoInternalThread) */
664 static MonoGHashTable *tid_to_thread_obj;
666 static gsize debugger_thread_id;
668 static HANDLE debugger_thread_handle;
670 static int log_level;
672 static gboolean embedding;
674 static FILE *log_file;
676 /* Assemblies whose assembly load event has no been sent yet */
677 static GPtrArray *pending_assembly_loads;
679 /* Whenever the debugger thread has exited */
680 static gboolean debugger_thread_exited;
682 /* Cond variable used to wait for debugger_thread_exited becoming true */
683 static mono_cond_t debugger_thread_exited_cond;
685 /* Mutex for the cond var above */
686 static mono_mutex_t debugger_thread_exited_mutex;
688 static DebuggerProfiler debugger_profiler;
690 /* The single step request instance */
691 static SingleStepReq *ss_req = NULL;
692 static gpointer ss_invoke_addr = NULL;
694 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
695 /* Number of single stepping operations in progress */
696 static int ss_count;
697 #endif
699 /* The protocol version of the client */
700 static int major_version, minor_version;
702 /* Whenever the variables above are set by the client */
703 static gboolean protocol_version_set;
705 /* A hash table containing all active domains */
706 static GHashTable *domains;
708 /* The number of times the runtime is suspended */
709 static gint32 suspend_count;
711 /* Whenever to buffer reply messages and send them together */
712 static gboolean buffer_replies;
714 /* Buffered reply packets */
715 static ReplyPacket reply_packets [128];
716 int nreply_packets;
718 static void transport_init (void);
719 static void transport_connect (const char *address);
720 static gboolean transport_handshake (void);
721 static void register_transport (DebuggerTransport *trans);
723 static guint32 WINAPI debugger_thread (void *arg);
725 static void runtime_initialized (MonoProfiler *prof);
727 static void runtime_shutdown (MonoProfiler *prof);
729 static void thread_startup (MonoProfiler *prof, uintptr_t tid);
731 static void thread_end (MonoProfiler *prof, uintptr_t tid);
733 static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result);
735 static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain);
737 static void emit_appdomain_load (gpointer key, gpointer value, gpointer user_data);
739 static void emit_thread_start (gpointer key, gpointer value, gpointer user_data);
741 static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_data);
743 static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result);
745 static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly);
747 static void emit_assembly_load (gpointer assembly, gpointer user_data);
749 static void emit_type_load (gpointer key, gpointer type, gpointer user_data);
751 static void start_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
753 static void end_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
755 static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result);
757 static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *jinfo);
759 static void start_single_stepping (void);
761 static void stop_single_stepping (void);
763 static void suspend_current (void);
765 static void clear_event_requests_for_assembly (MonoAssembly *assembly);
767 static void clear_types_for_assembly (MonoAssembly *assembly);
769 static void clear_breakpoints_for_domain (MonoDomain *domain);
771 static void process_profiler_event (EventKind event, gpointer arg);
773 /* Submodule init/cleanup */
774 static void breakpoints_init (void);
775 static void breakpoints_cleanup (void);
777 static void objrefs_init (void);
778 static void objrefs_cleanup (void);
780 static void ids_init (void);
781 static void ids_cleanup (void);
783 static void suspend_init (void);
785 static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch);
786 static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
787 static void ss_destroy (SingleStepReq *req);
789 static void start_debugger_thread (void);
790 static void stop_debugger_thread (void);
792 static void finish_agent_init (gboolean on_startup);
794 static void process_profiler_event (EventKind event, gpointer arg);
796 static void invalidate_frames (DebuggerTlsData *tls);
798 #ifndef DISABLE_SOCKET_TRANSPORT
799 static void
800 register_socket_transport (void);
801 #endif
803 static int
804 parse_address (char *address, char **host, int *port)
806 char *pos = strchr (address, ':');
808 if (pos == NULL || pos == address)
809 return 1;
811 *host = g_malloc (pos - address + 1);
812 strncpy (*host, address, pos - address);
813 (*host) [pos - address] = '\0';
815 *port = atoi (pos + 1);
817 return 0;
820 static void
821 print_usage (void)
823 fprintf (stderr, "Usage: mono --debugger-agent=[<option>=<value>,...] ...\n");
824 fprintf (stderr, "Available options:\n");
825 fprintf (stderr, " transport=<transport>\t\tTransport to use for connecting to the debugger (mandatory, possible values: 'dt_socket')\n");
826 fprintf (stderr, " address=<hostname>:<port>\tAddress to connect to (mandatory)\n");
827 fprintf (stderr, " loglevel=<n>\t\t\tLog level (defaults to 0)\n");
828 fprintf (stderr, " logfile=<file>\t\tFile to log to (defaults to stdout)\n");
829 fprintf (stderr, " suspend=y/n\t\t\tWhether to suspend after startup.\n");
830 fprintf (stderr, " timeout=<n>\t\t\tTimeout for connecting in milliseconds.\n");
831 fprintf (stderr, " server=y/n\t\t\tWhether to listen for a client connection.\n");
832 fprintf (stderr, " keepalive=<n>\t\t\tSend keepalive events every n milliseconds.\n");
833 fprintf (stderr, " setpgid=y/n\t\t\tWhether to call setpid(0, 0) after startup.\n");
834 fprintf (stderr, " help\t\t\t\tPrint this help.\n");
837 static gboolean
838 parse_flag (const char *option, char *flag)
840 if (!strcmp (flag, "y"))
841 return TRUE;
842 else if (!strcmp (flag, "n"))
843 return FALSE;
844 else {
845 fprintf (stderr, "debugger-agent: The valid values for the '%s' option are 'y' and 'n'.\n", option);
846 exit (1);
847 return FALSE;
851 void
852 mono_debugger_agent_parse_options (char *options)
854 char **args, **ptr;
855 char *host;
856 int port;
857 const char *extra;
859 #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
860 fprintf (stderr, "--debugger-agent is not supported on this platform.\n");
861 exit (1);
862 #endif
864 extra = g_getenv ("MONO_SDB_ENV_OPTIONS");
865 if (extra)
866 options = g_strdup_printf ("%s,%s", options, extra);
868 agent_config.enabled = TRUE;
869 agent_config.suspend = TRUE;
870 agent_config.server = FALSE;
871 agent_config.defer = FALSE;
872 agent_config.address = NULL;
874 //agent_config.log_level = 10;
876 args = g_strsplit (options, ",", -1);
877 for (ptr = args; ptr && *ptr; ptr ++) {
878 char *arg = *ptr;
880 if (strncmp (arg, "transport=", 10) == 0) {
881 agent_config.transport = g_strdup (arg + 10);
882 } else if (strncmp (arg, "address=", 8) == 0) {
883 agent_config.address = g_strdup (arg + 8);
884 } else if (strncmp (arg, "loglevel=", 9) == 0) {
885 agent_config.log_level = atoi (arg + 9);
886 } else if (strncmp (arg, "logfile=", 8) == 0) {
887 agent_config.log_file = g_strdup (arg + 8);
888 } else if (strncmp (arg, "suspend=", 8) == 0) {
889 agent_config.suspend = parse_flag ("suspend", arg + 8);
890 } else if (strncmp (arg, "server=", 7) == 0) {
891 agent_config.server = parse_flag ("server", arg + 7);
892 } else if (strncmp (arg, "onuncaught=", 11) == 0) {
893 agent_config.onuncaught = parse_flag ("onuncaught", arg + 11);
894 } else if (strncmp (arg, "onthrow=", 8) == 0) {
895 /* We support multiple onthrow= options */
896 agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (arg + 8));
897 } else if (strncmp (arg, "onthrow", 7) == 0) {
898 agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (""));
899 } else if (strncmp (arg, "help", 4) == 0) {
900 print_usage ();
901 exit (0);
902 } else if (strncmp (arg, "timeout=", 8) == 0) {
903 agent_config.timeout = atoi (arg + 8);
904 } else if (strncmp (arg, "launch=", 7) == 0) {
905 agent_config.launch = g_strdup (arg + 7);
906 } else if (strncmp (arg, "embedding=", 10) == 0) {
907 agent_config.embedding = atoi (arg + 10) == 1;
908 } else if (strncmp (arg, "keepalive=", 10) == 0) {
909 agent_config.keepalive = atoi (arg + 10);
910 } else if (strncmp (arg, "setpgid=", 8) == 0) {
911 agent_config.setpgid = parse_flag ("setpgid", arg + 8);
912 } else {
913 print_usage ();
914 exit (1);
918 if (agent_config.server && !agent_config.suspend) {
919 /* Waiting for deferred attachment */
920 agent_config.defer = TRUE;
921 if (agent_config.address == NULL) {
922 agent_config.address = g_strdup_printf ("0.0.0.0:%u", 56000 + (getpid () % 1000));
926 //agent_config.log_level = 0;
928 if (agent_config.transport == NULL) {
929 fprintf (stderr, "debugger-agent: The 'transport' option is mandatory.\n");
930 exit (1);
933 if (agent_config.address == NULL && !agent_config.server) {
934 fprintf (stderr, "debugger-agent: The 'address' option is mandatory.\n");
935 exit (1);
938 // FIXME:
939 if (!strcmp (agent_config.transport, "dt_socket")) {
940 if (agent_config.address && parse_address (agent_config.address, &host, &port)) {
941 fprintf (stderr, "debugger-agent: The format of the 'address' options is '<host>:<port>'\n");
942 exit (1);
947 void
948 mono_debugger_agent_init (void)
950 if (!agent_config.enabled)
951 return;
953 transport_init ();
955 /* Need to know whenever a thread has acquired the loader mutex */
956 mono_loader_lock_track_ownership (TRUE);
958 event_requests = g_ptr_array_new ();
960 mono_mutex_init (&debugger_thread_exited_mutex);
961 mono_cond_init (&debugger_thread_exited_cond, NULL);
963 mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
964 mono_profiler_set_events (MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS);
965 mono_profiler_install_runtime_initialized (runtime_initialized);
966 mono_profiler_install_appdomain (NULL, appdomain_load, NULL, appdomain_unload);
967 mono_profiler_install_thread (thread_startup, thread_end);
968 mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL);
969 mono_profiler_install_jit_end (jit_end);
970 mono_profiler_install_method_invoke (start_runtime_invoke, end_runtime_invoke);
972 mono_native_tls_alloc (&debugger_tls_id, NULL);
974 thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
975 MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls);
977 tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
978 MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread);
980 tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
981 MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj);
983 pending_assembly_loads = g_ptr_array_new ();
984 domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
986 log_level = agent_config.log_level;
988 embedding = agent_config.embedding;
989 disconnected = TRUE;
991 if (agent_config.log_file) {
992 log_file = fopen (agent_config.log_file, "w+");
993 if (!log_file) {
994 fprintf (stderr, "Unable to create log file '%s': %s.\n", agent_config.log_file, strerror (errno));
995 exit (1);
997 } else {
998 log_file = stdout;
1001 ids_init ();
1002 objrefs_init ();
1003 breakpoints_init ();
1004 suspend_init ();
1006 mini_get_debug_options ()->gen_seq_points = TRUE;
1008 * This is needed because currently we don't handle liveness info.
1010 mini_get_debug_options ()->mdb_optimizations = TRUE;
1012 #ifndef MONO_ARCH_HAVE_CONTEXT_SET_INT_REG
1013 /* This is needed because we can't set local variables in registers yet */
1014 mono_disable_optimizations (MONO_OPT_LINEARS);
1015 #endif
1018 * The stack walk done from thread_interrupt () needs to be signal safe, but it
1019 * isn't, since it can call into mono_aot_find_jit_info () which is not signal
1020 * safe (#3411). So load AOT info eagerly when the debugger is running as a
1021 * workaround.
1023 mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE;
1025 #ifdef HAVE_SETPGID
1026 if (agent_config.setpgid)
1027 setpgid (0, 0);
1028 #endif
1030 if (!agent_config.onuncaught && !agent_config.onthrow)
1031 finish_agent_init (TRUE);
1035 * finish_agent_init:
1037 * Finish the initialization of the agent. This involves connecting the transport
1038 * and starting the agent thread. This is either done at startup, or
1039 * in response to some event like an unhandled exception.
1041 static void
1042 finish_agent_init (gboolean on_startup)
1044 int res;
1046 if (InterlockedCompareExchange (&inited, 1, 0) == 1)
1047 return;
1049 if (agent_config.launch) {
1050 char *argv [16];
1052 // FIXME: Generated address
1053 // FIXME: Races with transport_connect ()
1055 argv [0] = agent_config.launch;
1056 argv [1] = agent_config.transport;
1057 argv [2] = agent_config.address;
1058 argv [3] = NULL;
1060 res = g_spawn_async_with_pipes (NULL, argv, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1061 if (!res) {
1062 fprintf (stderr, "Failed to execute '%s'.\n", agent_config.launch);
1063 exit (1);
1067 transport_connect (agent_config.address);
1069 if (!on_startup) {
1070 /* Do some which is usually done after sending the VMStart () event */
1071 vm_start_event_sent = TRUE;
1072 start_debugger_thread ();
1076 static void
1077 mono_debugger_agent_cleanup (void)
1079 if (!inited)
1080 return;
1082 stop_debugger_thread ();
1084 breakpoints_cleanup ();
1085 objrefs_cleanup ();
1086 ids_cleanup ();
1088 mono_mutex_destroy (&debugger_thread_exited_mutex);
1089 mono_cond_destroy (&debugger_thread_exited_cond);
1093 * SOCKET TRANSPORT
1096 #ifndef DISABLE_SOCKET_TRANSPORT
1099 * recv_length:
1101 * recv() + handle incomplete reads and EINTR
1103 static int
1104 socket_transport_recv (void *buf, int len)
1106 int res;
1107 int total = 0;
1108 int fd = conn_fd;
1109 int flags = 0;
1110 static gint32 last_keepalive;
1111 gint32 msecs;
1113 do {
1114 again:
1115 res = recv (fd, (char *) buf + total, len - total, flags);
1116 if (res > 0)
1117 total += res;
1118 if (agent_config.keepalive) {
1119 gboolean need_keepalive = FALSE;
1120 if (res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) {
1121 need_keepalive = TRUE;
1122 } else if (res == -1) {
1123 /* This could happen if recv () is interrupted repeatedly */
1124 msecs = mono_msec_ticks ();
1125 if (msecs - last_keepalive >= agent_config.keepalive) {
1126 need_keepalive = TRUE;
1127 last_keepalive = msecs;
1130 if (need_keepalive) {
1131 process_profiler_event (EVENT_KIND_KEEPALIVE, NULL);
1132 goto again;
1135 } while ((res > 0 && total < len) || (res == -1 && get_last_sock_error () == MONO_EINTR));
1136 return total;
1139 #ifndef TARGET_PS3
1140 #define HAVE_GETADDRINFO 1
1141 #endif
1143 static void
1144 set_keepalive (void)
1146 struct timeval tv;
1147 int result;
1149 if (!agent_config.keepalive || !conn_fd)
1150 return;
1152 tv.tv_sec = agent_config.keepalive / 1000;
1153 tv.tv_usec = (agent_config.keepalive % 1000) * 1000;
1155 result = setsockopt (conn_fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof(struct timeval));
1156 g_assert (result >= 0);
1159 static int
1160 socket_transport_accept (int socket_fd)
1162 conn_fd = accept (socket_fd, NULL, NULL);
1163 if (conn_fd == -1) {
1164 fprintf (stderr, "debugger-agent: Unable to listen on %d\n", socket_fd);
1165 } else {
1166 DEBUG (1, fprintf (log_file, "Accepted connection from client, connection fd=%d.\n", conn_fd));
1169 return conn_fd;
1172 static gboolean
1173 socket_transport_send (void *data, int len)
1175 int res;
1177 do {
1178 res = send (conn_fd, data, len, 0);
1179 } while (res == -1 && get_last_sock_error () == MONO_EINTR);
1180 if (res != len)
1181 return FALSE;
1182 else
1183 return TRUE;
1187 * socket_transport_connect:
1189 * Connect/Listen on HOST:PORT. If HOST is NULL, generate an address and listen on it.
1191 static void
1192 socket_transport_connect (const char *address)
1194 #ifdef HAVE_GETADDRINFO
1195 struct addrinfo hints;
1196 struct addrinfo *result, *rp;
1197 #else
1198 struct hostent *result;
1199 #endif
1200 int sfd = -1, s, res;
1201 char port_string [128];
1202 char *host;
1203 int port;
1205 if (agent_config.address) {
1206 res = parse_address (agent_config.address, &host, &port);
1207 g_assert (res == 0);
1208 } else {
1209 host = NULL;
1210 port = 0;
1213 conn_fd = -1;
1214 listen_fd = -1;
1216 if (host) {
1217 sprintf (port_string, "%d", port);
1219 mono_network_init ();
1221 /* Obtain address(es) matching host/port */
1222 #ifdef HAVE_GETADDRINFO
1223 memset (&hints, 0, sizeof (struct addrinfo));
1224 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
1225 hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
1226 hints.ai_flags = 0;
1227 hints.ai_protocol = 0; /* Any protocol */
1229 s = getaddrinfo (host, port_string, &hints, &result);
1230 if (s != 0) {
1231 fprintf (stderr, "debugger-agent: Unable to resolve %s:%d: %s\n", host, port, gai_strerror (s));
1232 exit (1);
1234 #else
1235 /* The PS3 doesn't even have _r or hstrerror () */
1236 result = gethostbyname (host);
1237 if (!result) {
1238 fprintf (stderr, "debugger-agent: Unable to resolve %s:%d: %d\n", host, port, h_errno);
1240 #endif
1243 if (agent_config.server) {
1244 #ifdef HAVE_GETADDRINFO
1245 /* Wait for a connection */
1246 if (!host) {
1247 struct sockaddr_in addr;
1248 socklen_t addrlen;
1250 /* No address, generate one */
1251 sfd = socket (AF_INET, SOCK_STREAM, 0);
1252 g_assert (sfd);
1254 /* This will bind the socket to a random port */
1255 res = listen (sfd, 16);
1256 if (res == -1) {
1257 fprintf (stderr, "debugger-agent: Unable to setup listening socket: %s\n", strerror (get_last_sock_error ()));
1258 exit (1);
1260 listen_fd = sfd;
1262 addrlen = sizeof (addr);
1263 memset (&addr, 0, sizeof (addr));
1264 res = getsockname (sfd, (struct sockaddr*)&addr, &addrlen);
1265 g_assert (res == 0);
1267 host = (char*)"127.0.0.1";
1268 port = ntohs (addr.sin_port);
1270 /* Emit the address to stdout */
1271 /* FIXME: Should print another interface, not localhost */
1272 printf ("%s:%d\n", host, port);
1273 } else {
1274 /* Listen on the provided address */
1275 for (rp = result; rp != NULL; rp = rp->ai_next) {
1276 int n = 1;
1278 sfd = socket (rp->ai_family, rp->ai_socktype,
1279 rp->ai_protocol);
1280 if (sfd == -1)
1281 continue;
1283 if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
1284 continue;
1286 res = bind (sfd, rp->ai_addr, rp->ai_addrlen);
1287 if (res == -1)
1288 continue;
1290 res = listen (sfd, 16);
1291 if (res == -1)
1292 continue;
1293 listen_fd = sfd;
1294 break;
1297 #ifndef HOST_WIN32
1299 * this function is not present on win2000 which we still support, and the
1300 * workaround described here:
1301 * http://msdn.microsoft.com/en-us/library/ms737931(VS.85).aspx
1302 * only works with MSVC.
1304 #ifdef HAVE_GETADDRINFO
1305 freeaddrinfo (result);
1306 #endif
1307 #endif
1310 if (agent_config.defer)
1311 return;
1313 DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
1315 if (agent_config.timeout) {
1316 fd_set readfds;
1317 struct timeval tv;
1319 tv.tv_sec = 0;
1320 tv.tv_usec = agent_config.timeout * 1000;
1321 FD_ZERO (&readfds);
1322 FD_SET (sfd, &readfds);
1323 res = select (sfd + 1, &readfds, NULL, NULL, &tv);
1324 if (res == 0) {
1325 fprintf (stderr, "debugger-agent: Timed out waiting to connect.\n");
1326 exit (1);
1330 conn_fd = socket_transport_accept (sfd);
1331 if (conn_fd == -1)
1332 exit (1);
1334 DEBUG (1, fprintf (log_file, "Accepted connection from client, socket fd=%d.\n", conn_fd));
1335 #else
1336 NOT_IMPLEMENTED;
1337 #endif /* HAVE_GETADDRINFO */
1338 } else {
1339 /* Connect to the specified address */
1340 #ifdef HAVE_GETADDRINFO
1341 /* FIXME: Respect the timeout */
1342 for (rp = result; rp != NULL; rp = rp->ai_next) {
1343 sfd = socket (rp->ai_family, rp->ai_socktype,
1344 rp->ai_protocol);
1345 if (sfd == -1)
1346 continue;
1348 if (connect (sfd, rp->ai_addr, rp->ai_addrlen) != -1)
1349 break; /* Success */
1351 close (sfd);
1354 if (rp == 0) {
1355 fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
1356 exit (1);
1358 #else
1359 sfd = socket (result->h_addrtype, SOCK_STREAM, 0);
1360 if (sfd == -1)
1361 g_assert_not_reached ();
1362 res = connect (sfd, (void*)result->h_addr_list [0], result->h_length);
1363 if (res == -1)
1364 g_assert_not_reached ();
1365 #endif
1367 conn_fd = sfd;
1369 #ifndef HOST_WIN32
1370 /* See the comment above */
1371 #ifdef HAVE_GETADDRINFO
1372 freeaddrinfo (result);
1373 #endif
1374 #endif
1377 if (!transport_handshake ())
1378 exit (1);
1381 static void
1382 socket_transport_close1 (void)
1384 /* This will interrupt the agent thread */
1385 /* Close the read part only so it can still send back replies */
1386 /* Also shut down the connection listener so that we can exit normally */
1387 #ifdef HOST_WIN32
1388 /* SD_RECEIVE doesn't break the recv in the debugger thread */
1389 shutdown (conn_fd, SD_BOTH);
1390 shutdown (listen_fd, SD_BOTH);
1391 closesocket (listen_fd);
1392 #else
1393 shutdown (conn_fd, SHUT_RD);
1394 shutdown (listen_fd, SHUT_RDWR);
1395 close (listen_fd);
1396 #endif
1399 static void
1400 socket_transport_close2 (void)
1402 #ifdef HOST_WIN32
1403 shutdown (conn_fd, SD_BOTH);
1404 #else
1405 shutdown (conn_fd, SHUT_RDWR);
1406 #endif
1409 static void
1410 register_socket_transport (void)
1412 DebuggerTransport trans;
1414 trans.name = "dt_socket";
1415 trans.connect = socket_transport_connect;
1416 trans.close1 = socket_transport_close1;
1417 trans.close2 = socket_transport_close2;
1418 trans.send = socket_transport_send;
1419 trans.recv = socket_transport_recv;
1421 register_transport (&trans);
1424 #endif /* DISABLE_SOCKET_TRANSPORT */
1427 * TRANSPORT CODE
1430 #define MAX_TRANSPORTS 16
1432 static DebuggerTransport *transport;
1434 static DebuggerTransport transports [MAX_TRANSPORTS];
1435 static int ntransports;
1437 void
1438 mono_debugger_agent_register_transport (DebuggerTransport *trans);
1440 void
1441 mono_debugger_agent_register_transport (DebuggerTransport *trans)
1443 register_transport (trans);
1446 static void
1447 register_transport (DebuggerTransport *trans)
1449 g_assert (ntransports < MAX_TRANSPORTS);
1451 memcpy (&transports [ntransports], trans, sizeof (DebuggerTransport));
1452 ntransports ++;
1455 static void
1456 transport_init (void)
1458 int i;
1460 #ifndef DISABLE_SOCKET_TRANSPORT
1461 register_socket_transport ();
1462 #endif
1464 for (i = 0; i < ntransports; ++i) {
1465 if (!strcmp (agent_config.transport, transports [i].name))
1466 break;
1468 if (i == ntransports) {
1469 fprintf (stderr, "debugger-agent: The supported values for the 'transport' option are: ");
1470 for (i = 0; i < ntransports; ++i)
1471 fprintf (stderr, "%s'%s'", i > 0 ? ", " : "", transports [i].name);
1472 fprintf (stderr, "\n");
1473 exit (1);
1475 transport = &transports [i];
1478 void
1479 transport_connect (const char *address)
1481 transport->connect (address);
1484 static void
1485 transport_close1 (void)
1487 transport->close1 ();
1490 static void
1491 transport_close2 (void)
1493 transport->close2 ();
1496 static int
1497 transport_send (void *buf, int len)
1499 return transport->send (buf, len);
1502 static int
1503 transport_recv (void *buf, int len)
1505 return transport->recv (buf, len);
1508 gboolean
1509 mono_debugger_agent_transport_handshake (void)
1511 return transport_handshake ();
1514 static gboolean
1515 transport_handshake (void)
1517 char handshake_msg [128];
1518 guint8 buf [128];
1519 int res;
1521 disconnected = TRUE;
1523 /* Write handshake message */
1524 sprintf (handshake_msg, "DWP-Handshake");
1525 do {
1526 res = transport_send (handshake_msg, strlen (handshake_msg));
1527 } while (res == -1 && get_last_sock_error () == MONO_EINTR);
1528 g_assert (res != -1);
1530 /* Read answer */
1531 res = transport_recv (buf, strlen (handshake_msg));
1532 if ((res != strlen (handshake_msg)) || (memcmp (buf, handshake_msg, strlen (handshake_msg) != 0))) {
1533 fprintf (stderr, "debugger-agent: DWP handshake failed.\n");
1534 return FALSE;
1538 * To support older clients, the client sends its protocol version after connecting
1539 * using a command. Until that is received, default to our protocol version.
1541 major_version = MAJOR_VERSION;
1542 minor_version = MINOR_VERSION;
1543 protocol_version_set = FALSE;
1545 #ifndef DISABLE_SOCKET_TRANSPORT
1546 // FIXME: Move this somewhere else
1548 * Set TCP_NODELAY on the socket so the client receives events/command
1549 * results immediately.
1551 if (conn_fd) {
1552 int flag = 1;
1553 int result = setsockopt (conn_fd,
1554 IPPROTO_TCP,
1555 TCP_NODELAY,
1556 (char *) &flag,
1557 sizeof(int));
1558 g_assert (result >= 0);
1561 set_keepalive ();
1562 #endif
1564 disconnected = FALSE;
1565 return TRUE;
1568 static void
1569 stop_debugger_thread (void)
1571 if (!inited)
1572 return;
1574 transport_close1 ();
1577 * Wait for the thread to exit.
1579 * If we continue with the shutdown without waiting for it, then the client might
1580 * not receive an answer to its last command like a resume.
1581 * The WaitForSingleObject infrastructure doesn't seem to work during shutdown, so
1582 * use pthreads.
1584 //WaitForSingleObject (debugger_thread_handle, INFINITE);
1585 if (GetCurrentThreadId () != debugger_thread_id) {
1586 do {
1587 mono_mutex_lock (&debugger_thread_exited_mutex);
1588 if (!debugger_thread_exited) {
1589 #ifdef HOST_WIN32
1590 if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) {
1591 mono_mutex_unlock (&debugger_thread_exited_mutex);
1592 Sleep(1);
1593 mono_mutex_lock (&debugger_thread_exited_mutex);
1595 #else
1596 mono_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex);
1597 #endif
1599 mono_mutex_unlock (&debugger_thread_exited_mutex);
1600 } while (!debugger_thread_exited);
1603 transport_close2 ();
1606 static void
1607 start_debugger_thread (void)
1609 debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, 0, 0, NULL);
1610 g_assert (debugger_thread_handle);
1614 * Functions to decode protocol data
1617 static inline int
1618 decode_byte (guint8 *buf, guint8 **endbuf, guint8 *limit)
1620 *endbuf = buf + 1;
1621 g_assert (*endbuf <= limit);
1622 return buf [0];
1625 static inline int
1626 decode_int (guint8 *buf, guint8 **endbuf, guint8 *limit)
1628 *endbuf = buf + 4;
1629 g_assert (*endbuf <= limit);
1631 return (((int)buf [0]) << 24) | (((int)buf [1]) << 16) | (((int)buf [2]) << 8) | (((int)buf [3]) << 0);
1634 static inline gint64
1635 decode_long (guint8 *buf, guint8 **endbuf, guint8 *limit)
1637 guint32 high = decode_int (buf, &buf, limit);
1638 guint32 low = decode_int (buf, &buf, limit);
1640 *endbuf = buf;
1642 return ((((guint64)high) << 32) | ((guint64)low));
1645 static inline int
1646 decode_id (guint8 *buf, guint8 **endbuf, guint8 *limit)
1648 return decode_int (buf, endbuf, limit);
1651 static inline char*
1652 decode_string (guint8 *buf, guint8 **endbuf, guint8 *limit)
1654 int len = decode_int (buf, &buf, limit);
1655 char *s;
1657 if (len < 0) {
1658 *endbuf = buf;
1659 return NULL;
1662 s = g_malloc (len + 1);
1663 g_assert (s);
1665 memcpy (s, buf, len);
1666 s [len] = '\0';
1667 buf += len;
1668 *endbuf = buf;
1670 return s;
1674 * Functions to encode protocol data
1677 static inline void
1678 buffer_init (Buffer *buf, int size)
1680 buf->buf = g_malloc (size);
1681 buf->p = buf->buf;
1682 buf->end = buf->buf + size;
1685 static inline int
1686 buffer_len (Buffer *buf)
1688 return buf->p - buf->buf;
1691 static inline void
1692 buffer_make_room (Buffer *buf, int size)
1694 if (buf->end - buf->p < size) {
1695 int new_size = buf->end - buf->buf + size + 32;
1696 guint8 *p = g_realloc (buf->buf, new_size);
1697 size = buf->p - buf->buf;
1698 buf->buf = p;
1699 buf->p = p + size;
1700 buf->end = buf->buf + new_size;
1704 static inline void
1705 buffer_add_byte (Buffer *buf, guint8 val)
1707 buffer_make_room (buf, 1);
1708 buf->p [0] = val;
1709 buf->p++;
1712 static inline void
1713 buffer_add_short (Buffer *buf, guint32 val)
1715 buffer_make_room (buf, 2);
1716 buf->p [0] = (val >> 8) & 0xff;
1717 buf->p [1] = (val >> 0) & 0xff;
1718 buf->p += 2;
1721 static inline void
1722 buffer_add_int (Buffer *buf, guint32 val)
1724 buffer_make_room (buf, 4);
1725 buf->p [0] = (val >> 24) & 0xff;
1726 buf->p [1] = (val >> 16) & 0xff;
1727 buf->p [2] = (val >> 8) & 0xff;
1728 buf->p [3] = (val >> 0) & 0xff;
1729 buf->p += 4;
1732 static inline void
1733 buffer_add_long (Buffer *buf, guint64 l)
1735 buffer_add_int (buf, (l >> 32) & 0xffffffff);
1736 buffer_add_int (buf, (l >> 0) & 0xffffffff);
1739 static inline void
1740 buffer_add_id (Buffer *buf, int id)
1742 buffer_add_int (buf, (guint64)id);
1745 static inline void
1746 buffer_add_data (Buffer *buf, guint8 *data, int len)
1748 buffer_make_room (buf, len);
1749 memcpy (buf->p, data, len);
1750 buf->p += len;
1753 static inline void
1754 buffer_add_string (Buffer *buf, const char *str)
1756 int len;
1758 if (str == NULL) {
1759 buffer_add_int (buf, 0);
1760 } else {
1761 len = strlen (str);
1762 buffer_add_int (buf, len);
1763 buffer_add_data (buf, (guint8*)str, len);
1767 static inline void
1768 buffer_add_buffer (Buffer *buf, Buffer *data)
1770 buffer_add_data (buf, data->buf, buffer_len (data));
1773 static inline void
1774 buffer_free (Buffer *buf)
1776 g_free (buf->buf);
1779 static gboolean
1780 send_packet (int command_set, int command, Buffer *data)
1782 Buffer buf;
1783 int len, id;
1784 gboolean res;
1786 id = InterlockedIncrement (&packet_id);
1788 len = data->p - data->buf + 11;
1789 buffer_init (&buf, len);
1790 buffer_add_int (&buf, len);
1791 buffer_add_int (&buf, id);
1792 buffer_add_byte (&buf, 0); /* flags */
1793 buffer_add_byte (&buf, command_set);
1794 buffer_add_byte (&buf, command);
1795 memcpy (buf.buf + 11, data->buf, data->p - data->buf);
1797 res = transport_send (buf.buf, len);
1799 buffer_free (&buf);
1801 return res;
1804 static gboolean
1805 send_reply_packets (int npackets, ReplyPacket *packets)
1807 Buffer buf;
1808 int i, len;
1809 gboolean res;
1811 len = 0;
1812 for (i = 0; i < npackets; ++i)
1813 len += buffer_len (packets [i].data) + 11;
1814 buffer_init (&buf, len);
1815 for (i = 0; i < npackets; ++i) {
1816 buffer_add_int (&buf, buffer_len (packets [i].data) + 11);
1817 buffer_add_int (&buf, packets [i].id);
1818 buffer_add_byte (&buf, 0x80); /* flags */
1819 buffer_add_byte (&buf, (packets [i].error >> 8) & 0xff);
1820 buffer_add_byte (&buf, packets [i].error);
1821 buffer_add_buffer (&buf, packets [i].data);
1823 res = transport_send (buf.buf, len);
1825 buffer_free (&buf);
1827 return res;
1830 static gboolean
1831 send_reply_packet (int id, int error, Buffer *data)
1833 ReplyPacket packet;
1835 memset (&packet, 0, sizeof (ReplyPacket));
1836 packet.id = id;
1837 packet.error = error;
1838 packet.data = data;
1840 return send_reply_packets (1, &packet);
1843 static void
1844 send_buffered_reply_packets (void)
1846 int i;
1848 send_reply_packets (nreply_packets, reply_packets);
1849 for (i = 0; i < nreply_packets; ++i)
1850 buffer_free (reply_packets [i].data);
1851 DEBUG (1, fprintf (log_file, "[dbg] Sent %d buffered reply packets [at=%lx].\n", nreply_packets, (long)mono_100ns_ticks () / 10000));
1852 nreply_packets = 0;
1855 static void
1856 buffer_reply_packet (int id, int error, Buffer *data)
1858 ReplyPacket *p;
1860 if (nreply_packets == 128)
1861 send_buffered_reply_packets ();
1863 p = &reply_packets [nreply_packets];
1864 p->id = id;
1865 p->error = error;
1866 p->data = g_new0 (Buffer, 1);
1867 buffer_init (p->data, buffer_len (data));
1868 buffer_add_buffer (p->data, data);
1869 nreply_packets ++;
1873 * OBJECT IDS
1877 * Represents an object accessible by the debugger client.
1879 typedef struct {
1880 /* Unique id used in the wire protocol to refer to objects */
1881 int id;
1883 * A weakref gc handle pointing to the object. The gc handle is used to
1884 * detect if the object was garbage collected.
1886 guint32 handle;
1887 } ObjRef;
1889 /* Maps objid -> ObjRef */
1890 static GHashTable *objrefs;
1892 static void
1893 free_objref (gpointer value)
1895 ObjRef *o = value;
1897 mono_gchandle_free (o->handle);
1899 g_free (o);
1902 static void
1903 objrefs_init (void)
1905 objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref);
1908 static void
1909 objrefs_cleanup (void)
1911 g_hash_table_destroy (objrefs);
1912 objrefs = NULL;
1915 static GHashTable *obj_to_objref;
1916 static MonoGHashTable *suspended_objs;
1919 * Return an ObjRef for OBJ.
1921 static ObjRef*
1922 get_objref (MonoObject *obj)
1924 ObjRef *ref;
1925 GSList *reflist = NULL, *l;
1926 int hash = 0;
1928 if (obj == NULL)
1929 return 0;
1931 mono_loader_lock ();
1933 if (!obj_to_objref) {
1934 obj_to_objref = g_hash_table_new (NULL, NULL);
1935 suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
1936 MONO_GC_REGISTER_ROOT_FIXED (suspended_objs);
1939 if (suspend_count) {
1941 * Have to keep object refs created during suspensions alive for the duration of the suspension, so GCs during invokes don't collect them.
1943 mono_g_hash_table_insert (suspended_objs, obj, NULL);
1946 /* FIXME: The tables can grow indefinitely */
1948 if (mono_gc_is_moving ()) {
1950 * Objects can move, so use a hash table mapping hash codes to lists of
1951 * ObjRef structures.
1953 hash = mono_object_hash (obj);
1955 reflist = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (hash));
1956 for (l = reflist; l; l = l->next) {
1957 ref = l->data;
1958 if (ref && mono_gchandle_get_target (ref->handle) == obj) {
1959 mono_loader_unlock ();
1960 return ref;
1963 } else {
1964 /* Use a hash table with masked pointers to internalize object references */
1965 ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
1966 /* ref might refer to a different object with the same addr which was GCd */
1967 if (ref && mono_gchandle_get_target (ref->handle) == obj) {
1968 mono_loader_unlock ();
1969 return ref;
1973 ref = g_new0 (ObjRef, 1);
1974 ref->id = InterlockedIncrement (&objref_id);
1975 ref->handle = mono_gchandle_new_weakref (obj, FALSE);
1977 g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
1979 if (mono_gc_is_moving ()) {
1980 reflist = g_slist_append (reflist, ref);
1981 g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (hash), reflist);
1982 } else {
1983 g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
1986 mono_loader_unlock ();
1988 return ref;
1991 static gboolean
1992 true_pred (gpointer key, gpointer value, gpointer user_data)
1994 return TRUE;
1997 static void
1998 clear_suspended_objs (void)
2000 mono_loader_lock ();
2001 mono_g_hash_table_foreach_remove (suspended_objs, true_pred, NULL);
2002 mono_loader_unlock ();
2005 static inline int
2006 get_objid (MonoObject *obj)
2008 return get_objref (obj)->id;
2012 * Set OBJ to the object identified by OBJID.
2013 * Returns 0 or an error code if OBJID is invalid or the object has been garbage
2014 * collected.
2016 static ErrorCode
2017 get_object_allow_null (int objid, MonoObject **obj)
2019 ObjRef *ref;
2021 if (objid == 0) {
2022 *obj = NULL;
2023 return 0;
2026 if (!objrefs)
2027 return ERR_INVALID_OBJECT;
2029 mono_loader_lock ();
2031 ref = g_hash_table_lookup (objrefs, GINT_TO_POINTER (objid));
2033 if (ref) {
2034 *obj = mono_gchandle_get_target (ref->handle);
2035 mono_loader_unlock ();
2036 if (!(*obj))
2037 return ERR_INVALID_OBJECT;
2038 return 0;
2039 } else {
2040 mono_loader_unlock ();
2041 return ERR_INVALID_OBJECT;
2045 static ErrorCode
2046 get_object (int objid, MonoObject **obj)
2048 int err = get_object_allow_null (objid, obj);
2050 if (err)
2051 return err;
2052 if (!(*obj))
2053 return ERR_INVALID_OBJECT;
2054 return 0;
2057 static inline int
2058 decode_objid (guint8 *buf, guint8 **endbuf, guint8 *limit)
2060 return decode_id (buf, endbuf, limit);
2063 static inline void
2064 buffer_add_objid (Buffer *buf, MonoObject *o)
2066 buffer_add_id (buf, get_objid (o));
2070 * IDS
2073 typedef enum {
2074 ID_ASSEMBLY = 0,
2075 ID_MODULE = 1,
2076 ID_TYPE = 2,
2077 ID_METHOD = 3,
2078 ID_FIELD = 4,
2079 ID_DOMAIN = 5,
2080 ID_PROPERTY = 6,
2081 ID_NUM
2082 } IdType;
2085 * Represents a runtime structure accessible to the debugger client
2087 typedef struct {
2088 /* Unique id used in the wire protocol */
2089 int id;
2090 /* Domain of the runtime structure, NULL if the domain was unloaded */
2091 MonoDomain *domain;
2092 union {
2093 gpointer val;
2094 MonoClass *klass;
2095 MonoMethod *method;
2096 MonoImage *image;
2097 MonoAssembly *assembly;
2098 MonoClassField *field;
2099 MonoDomain *domain;
2100 MonoProperty *property;
2101 } data;
2102 } Id;
2104 typedef struct {
2105 /* Maps runtime structure -> Id */
2106 GHashTable *val_to_id [ID_NUM];
2107 /* Classes whose class load event has been sent */
2108 GHashTable *loaded_classes;
2109 /* Maps MonoClass->GPtrArray of file names */
2110 GHashTable *source_files;
2111 /* Maps source file basename -> GSList of classes */
2112 GHashTable *source_file_to_class;
2113 /* Same with ignore-case */
2114 GHashTable *source_file_to_class_ignorecase;
2115 } AgentDomainInfo;
2117 /* Maps id -> Id */
2118 static GPtrArray *ids [ID_NUM];
2120 static void
2121 ids_init (void)
2123 int i;
2125 for (i = 0; i < ID_NUM; ++i)
2126 ids [i] = g_ptr_array_new ();
2129 static void
2130 ids_cleanup (void)
2132 int i, j;
2134 for (i = 0; i < ID_NUM; ++i) {
2135 if (ids [i]) {
2136 for (j = 0; j < ids [i]->len; ++j)
2137 g_free (g_ptr_array_index (ids [i], j));
2138 g_ptr_array_free (ids [i], TRUE);
2140 ids [i] = NULL;
2144 void
2145 mono_debugger_agent_free_domain_info (MonoDomain *domain)
2147 AgentDomainInfo *info = domain_jit_info (domain)->agent_info;
2148 int i, j;
2149 GHashTableIter iter;
2150 GPtrArray *file_names;
2151 char *basename;
2152 GSList *l;
2154 if (info) {
2155 for (i = 0; i < ID_NUM; ++i)
2156 if (info->val_to_id [i])
2157 g_hash_table_destroy (info->val_to_id [i]);
2158 g_hash_table_destroy (info->loaded_classes);
2160 g_hash_table_iter_init (&iter, info->source_files);
2161 while (g_hash_table_iter_next (&iter, NULL, (void**)&file_names)) {
2162 for (i = 0; i < file_names->len; ++i)
2163 g_free (g_ptr_array_index (file_names, i));
2164 g_ptr_array_free (file_names, TRUE);
2167 g_hash_table_iter_init (&iter, info->source_file_to_class);
2168 while (g_hash_table_iter_next (&iter, (void**)&basename, (void**)&l)) {
2169 g_free (basename);
2170 g_slist_free (l);
2173 g_hash_table_iter_init (&iter, info->source_file_to_class_ignorecase);
2174 while (g_hash_table_iter_next (&iter, (void**)&basename, (void**)&l)) {
2175 g_free (basename);
2176 g_slist_free (l);
2179 g_free (info);
2182 domain_jit_info (domain)->agent_info = NULL;
2184 /* Clear ids referencing structures in the domain */
2185 for (i = 0; i < ID_NUM; ++i) {
2186 if (ids [i]) {
2187 for (j = 0; j < ids [i]->len; ++j) {
2188 Id *id = g_ptr_array_index (ids [i], j);
2189 if (id->domain == domain)
2190 id->domain = NULL;
2195 mono_loader_lock ();
2196 g_hash_table_remove (domains, domain);
2197 mono_loader_unlock ();
2200 static AgentDomainInfo*
2201 get_agent_domain_info (MonoDomain *domain)
2203 AgentDomainInfo *info = NULL;
2205 mono_domain_lock (domain);
2207 info = domain_jit_info (domain)->agent_info;
2208 if (!info) {
2209 info = domain_jit_info (domain)->agent_info = g_new0 (AgentDomainInfo, 1);
2210 info->loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL);
2211 info->source_files = g_hash_table_new (mono_aligned_addr_hash, NULL);
2212 info->source_file_to_class = g_hash_table_new (g_str_hash, g_str_equal);
2213 info->source_file_to_class_ignorecase = g_hash_table_new (g_str_hash, g_str_equal);
2216 mono_domain_unlock (domain);
2218 return info;
2221 static int
2222 get_id (MonoDomain *domain, IdType type, gpointer val)
2224 Id *id;
2225 AgentDomainInfo *info;
2227 if (val == NULL)
2228 return 0;
2230 mono_loader_lock ();
2232 mono_domain_lock (domain);
2234 info = get_agent_domain_info (domain);
2236 if (info->val_to_id [type] == NULL)
2237 info->val_to_id [type] = g_hash_table_new (mono_aligned_addr_hash, NULL);
2239 id = g_hash_table_lookup (info->val_to_id [type], val);
2240 if (id) {
2241 mono_domain_unlock (domain);
2242 mono_loader_unlock ();
2243 return id->id;
2246 id = g_new0 (Id, 1);
2247 /* Reserve id 0 */
2248 id->id = ids [type]->len + 1;
2249 id->domain = domain;
2250 id->data.val = val;
2252 g_hash_table_insert (info->val_to_id [type], val, id);
2254 mono_domain_unlock (domain);
2256 g_ptr_array_add (ids [type], id);
2258 mono_loader_unlock ();
2260 return id->id;
2263 static inline gpointer
2264 decode_ptr_id (guint8 *buf, guint8 **endbuf, guint8 *limit, IdType type, MonoDomain **domain, int *err)
2266 Id *res;
2268 int id = decode_id (buf, endbuf, limit);
2270 *err = 0;
2271 if (domain)
2272 *domain = NULL;
2274 if (id == 0)
2275 return NULL;
2277 // FIXME: error handling
2278 mono_loader_lock ();
2279 g_assert (id > 0 && id <= ids [type]->len);
2281 res = g_ptr_array_index (ids [type], GPOINTER_TO_INT (id - 1));
2282 mono_loader_unlock ();
2284 if (res->domain == NULL) {
2285 DEBUG (0, fprintf (log_file, "ERR_UNLOADED, id=%d, type=%d.\n", id, type));
2286 *err = ERR_UNLOADED;
2287 return NULL;
2290 if (domain)
2291 *domain = res->domain;
2293 return res->data.val;
2296 static inline void
2297 buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val)
2299 buffer_add_id (buf, get_id (domain, type, val));
2302 static inline MonoClass*
2303 decode_typeid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
2305 MonoClass *klass;
2307 klass = decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
2308 if (G_UNLIKELY (log_level >= 2) && klass) {
2309 char *s;
2311 s = mono_type_full_name (&klass->byval_arg);
2312 DEBUG(2, fprintf (log_file, "[dbg] recv class [%s]\n", s));
2313 g_free (s);
2315 return klass;
2318 static inline MonoAssembly*
2319 decode_assemblyid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
2321 return decode_ptr_id (buf, endbuf, limit, ID_ASSEMBLY, domain, err);
2324 static inline MonoImage*
2325 decode_moduleid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
2327 return decode_ptr_id (buf, endbuf, limit, ID_MODULE, domain, err);
2330 static inline MonoMethod*
2331 decode_methodid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
2333 MonoMethod *m;
2335 m = decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err);
2336 if (G_UNLIKELY (log_level >= 2) && m) {
2337 char *s;
2339 s = mono_method_full_name (m, TRUE);
2340 DEBUG(2, fprintf (log_file, "[dbg] recv method [%s]\n", s));
2341 g_free (s);
2343 return m;
2346 static inline MonoClassField*
2347 decode_fieldid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
2349 return decode_ptr_id (buf, endbuf, limit, ID_FIELD, domain, err);
2352 static inline MonoDomain*
2353 decode_domainid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
2355 return decode_ptr_id (buf, endbuf, limit, ID_DOMAIN, domain, err);
2358 static inline MonoProperty*
2359 decode_propertyid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
2361 return decode_ptr_id (buf, endbuf, limit, ID_PROPERTY, domain, err);
2364 static inline void
2365 buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass)
2367 buffer_add_ptr_id (buf, domain, ID_TYPE, klass);
2368 if (G_UNLIKELY (log_level >= 2) && klass) {
2369 char *s;
2371 s = mono_type_full_name (&klass->byval_arg);
2372 if (GetCurrentThreadId () == debugger_thread_id)
2373 DEBUG(2, fprintf (log_file, "[dbg] send class [%s]\n", s));
2374 else
2375 DEBUG(2, fprintf (log_file, "[%p] send class [%s]\n", (gpointer)GetCurrentThreadId (), s));
2376 g_free (s);
2380 static inline void
2381 buffer_add_methodid (Buffer *buf, MonoDomain *domain, MonoMethod *method)
2383 buffer_add_ptr_id (buf, domain, ID_METHOD, method);
2384 if (G_UNLIKELY (log_level >= 2) && method) {
2385 char *s;
2387 s = mono_method_full_name (method, 1);
2388 DEBUG(2, fprintf (log_file, "[dbg] send method [%s]\n", s));
2389 g_free (s);
2393 static inline void
2394 buffer_add_assemblyid (Buffer *buf, MonoDomain *domain, MonoAssembly *assembly)
2396 buffer_add_ptr_id (buf, domain, ID_ASSEMBLY, assembly);
2399 static inline void
2400 buffer_add_moduleid (Buffer *buf, MonoDomain *domain, MonoImage *image)
2402 buffer_add_ptr_id (buf, domain, ID_MODULE, image);
2405 static inline void
2406 buffer_add_fieldid (Buffer *buf, MonoDomain *domain, MonoClassField *field)
2408 buffer_add_ptr_id (buf, domain, ID_FIELD, field);
2411 static inline void
2412 buffer_add_propertyid (Buffer *buf, MonoDomain *domain, MonoProperty *property)
2414 buffer_add_ptr_id (buf, domain, ID_PROPERTY, property);
2417 static inline void
2418 buffer_add_domainid (Buffer *buf, MonoDomain *domain)
2420 buffer_add_ptr_id (buf, domain, ID_DOMAIN, domain);
2423 static void invoke_method (void);
2426 * SUSPEND/RESUME
2430 * save_thread_context:
2432 * Set CTX as the current threads context which is used for computing stack traces.
2433 * This function is signal-safe.
2435 static void
2436 save_thread_context (MonoContext *ctx)
2438 DebuggerTlsData *tls;
2440 tls = mono_native_tls_get_value (debugger_tls_id);
2441 g_assert (tls);
2443 if (ctx)
2444 mono_thread_state_init_from_monoctx (&tls->context, ctx);
2445 else
2446 mono_thread_state_init_from_current (&tls->context);
2449 /* Number of threads suspended */
2451 * If this is equal to the size of thread_to_tls, the runtime is considered
2452 * suspended.
2454 static gint32 threads_suspend_count;
2456 static mono_mutex_t suspend_mutex;
2458 /* Cond variable used to wait for suspend_count becoming 0 */
2459 static mono_cond_t suspend_cond;
2461 /* Semaphore used to wait for a thread becoming suspended */
2462 static MonoSemType suspend_sem;
2464 static void
2465 suspend_init (void)
2467 mono_mutex_init (&suspend_mutex);
2468 mono_cond_init (&suspend_cond, NULL);
2469 MONO_SEM_INIT (&suspend_sem, 0);
2472 typedef struct
2474 StackFrameInfo last_frame;
2475 gboolean last_frame_set;
2476 MonoContext ctx;
2477 gpointer lmf;
2478 } GetLastFrameUserData;
2480 static gboolean
2481 get_last_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
2483 GetLastFrameUserData *data = user_data;
2485 if (info->type == FRAME_TYPE_MANAGED_TO_NATIVE)
2486 return FALSE;
2488 if (!data->last_frame_set) {
2489 /* Store the last frame */
2490 memcpy (&data->last_frame, info, sizeof (StackFrameInfo));
2491 data->last_frame_set = TRUE;
2492 return FALSE;
2493 } else {
2494 /* Store the context/lmf for the frame above the last frame */
2495 memcpy (&data->ctx, ctx, sizeof (MonoContext));
2496 data->lmf = info->lmf;
2497 return TRUE;
2502 * thread_interrupt:
2504 * Process interruption of a thread. If SIGCTX is set, process the current thread. If
2505 * INFO is set, process the thread described by INFO.
2506 * This should be signal safe.
2508 static gboolean
2509 thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, MonoJitInfo *ji)
2511 gboolean res;
2512 gpointer ip;
2513 MonoNativeThreadId tid;
2516 * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
2517 * guarantee the signal handler will be called that many times. Instead of tracking
2518 * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
2519 * has been requested that hasn't been handled yet, otherwise we can have threads
2520 * refuse to die when VM_EXIT is called
2522 #if defined(__APPLE__)
2523 if (InterlockedCompareExchange (&tls->interrupt_count, 0, 1) == 0)
2524 return FALSE;
2525 #else
2527 * We use interrupt_count to determine whenever this interrupt should be processed
2528 * by us or the normal interrupt processing code in the signal handler.
2529 * There is no race here with notify_thread (), since the signal is sent after
2530 * incrementing interrupt_count.
2532 if (tls->interrupt_count == 0)
2533 return FALSE;
2535 InterlockedDecrement (&tls->interrupt_count);
2536 #endif
2538 if (sigctx)
2539 ip = mono_arch_ip_from_context (sigctx);
2540 else if (info)
2541 ip = MONO_CONTEXT_GET_IP (&info->suspend_state.ctx);
2542 else
2543 ip = NULL;
2545 if (info)
2546 tid = mono_thread_info_get_tid (info);
2547 else
2548 tid = (MonoNativeThreadId)GetCurrentThreadId ();
2550 // FIXME: Races when the thread leaves managed code before hitting a single step
2551 // event.
2553 if (ji) {
2554 /* Running managed code, will be suspended by the single step code */
2555 DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)(gsize)tid, jinfo_get_method (ji)->name, ip));
2556 return TRUE;
2557 } else {
2559 * Running native code, will be suspended when it returns to/enters
2560 * managed code. Treat it as already suspended.
2561 * This might interrupt the code in process_single_step_inner (), we use the
2562 * tls->suspending flag to avoid races when that happens.
2564 if (!tls->suspended && !tls->suspending) {
2565 MonoContext ctx;
2566 GetLastFrameUserData data;
2568 // FIXME: printf is not signal safe, but this is only used during
2569 // debugger debugging
2570 if (ip)
2571 DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %p, treating as suspended.\n", (gpointer)(gsize)tid, ip));
2572 //save_thread_context (&ctx);
2574 if (!tls->thread)
2575 /* Already terminated */
2576 return TRUE;
2579 * We are in a difficult position: we want to be able to provide stack
2580 * traces for this thread, but we can't use the current ctx+lmf, since
2581 * the thread is still running, so it might return to managed code,
2582 * making these invalid.
2583 * So we start a stack walk and save the first frame, along with the
2584 * parent frame's ctx+lmf. This (hopefully) works because the thread will be
2585 * suspended when it returns to managed code, so the parent's ctx should
2586 * remain valid.
2588 data.last_frame_set = FALSE;
2589 if (sigctx) {
2590 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
2592 * Don't pass MONO_UNWIND_ACTUAL_METHOD, its not signal safe, and
2593 * get_last_frame () doesn't need it, the last frame cannot be a ginst
2594 * since we are not in a JITted method.
2596 mono_walk_stack_with_ctx (get_last_frame, &ctx, MONO_UNWIND_NONE, &data);
2597 } else if (info) {
2598 mono_get_eh_callbacks ()->mono_walk_stack_with_state (get_last_frame, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, &data);
2600 if (data.last_frame_set) {
2601 memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo));
2602 res = mono_thread_state_init_from_monoctx (&tls->async_state, &ctx);
2603 g_assert (res);
2604 mono_thread_state_init_from_monoctx (&tls->context, &ctx);
2605 g_assert (res);
2607 memcpy (&tls->async_state.ctx, &data.ctx, sizeof (MonoContext));
2608 tls->async_state.unwind_data [MONO_UNWIND_DATA_LMF] = data.lmf;
2609 tls->async_state.unwind_data [MONO_UNWIND_DATA_JIT_TLS] = tls->thread->jit_data;
2610 } else {
2611 tls->async_state.valid = FALSE;
2614 mono_memory_barrier ();
2616 tls->suspended = TRUE;
2617 MONO_SEM_POST (&suspend_sem);
2619 return TRUE;
2624 * mono_debugger_agent_thread_interrupt:
2626 * Called by the abort signal handler.
2627 * Should be signal safe.
2629 gboolean
2630 mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
2632 DebuggerTlsData *tls;
2634 if (!inited)
2635 return FALSE;
2637 tls = mono_native_tls_get_value (debugger_tls_id);
2638 if (!tls) {
2639 DEBUG (1, fprintf (log_file, "[%p] Received interrupt with no TLS, continuing.\n", (gpointer)GetCurrentThreadId ()));
2640 return FALSE;
2643 return thread_interrupt (tls, NULL, sigctx, ji);
2646 #ifdef HOST_WIN32
2647 static void CALLBACK notify_thread_apc (ULONG_PTR param)
2649 //DebugBreak ();
2650 mono_debugger_agent_thread_interrupt (NULL, NULL);
2652 #endif /* HOST_WIN32 */
2655 * reset_native_thread_suspend_state:
2657 * Reset the suspended flag and state on native threads
2659 static void
2660 reset_native_thread_suspend_state (gpointer key, gpointer value, gpointer user_data)
2662 DebuggerTlsData *tls = value;
2664 if (!tls->really_suspended && tls->suspended) {
2665 tls->suspended = FALSE;
2667 * The thread might still be running if it was executing native code, so the state won't be invalided by
2668 * suspend_current ().
2670 tls->context.valid = FALSE;
2671 tls->async_state.valid = FALSE;
2672 invalidate_frames (tls);
2677 * notify_thread:
2679 * Notify a thread that it needs to suspend.
2681 static void
2682 notify_thread (gpointer key, gpointer value, gpointer user_data)
2684 MonoInternalThread *thread = key;
2685 DebuggerTlsData *tls = value;
2686 gsize tid = thread->tid;
2687 int res;
2689 if (GetCurrentThreadId () == tid || tls->terminated)
2690 return;
2692 DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
2695 * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
2696 * guarantee the signal handler will be called that many times. Instead of tracking
2697 * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
2698 * has been requested that hasn't been handled yet, otherwise we can have threads
2699 * refuse to die when VM_EXIT is called
2701 #if defined(__APPLE__)
2702 if (InterlockedCompareExchange (&tls->interrupt_count, 1, 0) == 1)
2703 return;
2704 #else
2706 * Maybe we could use the normal interrupt infrastructure, but that does a lot
2707 * of things like breaking waits etc. which we don't want.
2709 InterlockedIncrement (&tls->interrupt_count);
2710 #endif
2712 /* This is _not_ equivalent to ves_icall_System_Threading_Thread_Abort () */
2713 #ifdef HOST_WIN32
2714 QueueUserAPC (notify_thread_apc, thread->handle, NULL);
2715 #else
2716 if (mono_thread_info_new_interrupt_enabled ()) {
2717 MonoThreadInfo *info;
2718 MonoJitInfo *ji;
2720 info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE);
2721 if (!info) {
2722 DEBUG(1, fprintf (log_file, "[%p] mono_thread_info_suspend_sync () failed for %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
2724 * Attached thread which died without detaching.
2726 tls->terminated = TRUE;
2727 } else {
2728 ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
2730 thread_interrupt (tls, info, NULL, ji);
2732 mono_thread_info_finish_suspend_and_resume (info);
2734 } else {
2735 res = mono_thread_kill (thread, mono_thread_get_abort_signal ());
2736 if (res) {
2737 DEBUG(1, fprintf (log_file, "[%p] mono_thread_kill () failed for %p: %d...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid, res));
2739 * Attached thread which died without detaching.
2741 tls->terminated = TRUE;
2744 #endif
2747 static void
2748 process_suspend (DebuggerTlsData *tls, MonoContext *ctx)
2750 guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
2751 MonoJitInfo *ji;
2752 MonoMethod *method;
2754 if (mono_loader_lock_is_owned_by_self ()) {
2756 * Shortcut for the check in suspend_current (). This speeds up processing
2757 * when executing long running code inside the loader lock, i.e. assembly load
2758 * hooks.
2760 return;
2763 if (debugger_thread_id == GetCurrentThreadId ())
2764 return;
2766 /* Prevent races with mono_debugger_agent_thread_interrupt () */
2767 if (suspend_count - tls->resume_count > 0)
2768 tls->suspending = TRUE;
2770 DEBUG(1, fprintf (log_file, "[%p] Received single step event for suspending.\n", (gpointer)GetCurrentThreadId ()));
2772 if (suspend_count - tls->resume_count == 0) {
2774 * We are executing a single threaded invoke but the single step for
2775 * suspending is still active.
2776 * FIXME: This slows down single threaded invokes.
2778 DEBUG(1, fprintf (log_file, "[%p] Ignored during single threaded invoke.\n", (gpointer)GetCurrentThreadId ()));
2779 return;
2782 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
2784 /* Can't suspend in these methods */
2785 method = jinfo_get_method (ji);
2786 if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
2787 return;
2789 save_thread_context (ctx);
2791 suspend_current ();
2795 * suspend_vm:
2797 * Increase the suspend count of the VM. While the suspend count is greater
2798 * than 0, runtime threads are suspended at certain points during execution.
2800 static void
2801 suspend_vm (void)
2803 mono_loader_lock ();
2805 mono_mutex_lock (&suspend_mutex);
2807 suspend_count ++;
2809 DEBUG(1, fprintf (log_file, "[%p] Suspending vm...\n", (gpointer)GetCurrentThreadId ()));
2811 if (suspend_count == 1) {
2812 // FIXME: Is it safe to call this inside the lock ?
2813 start_single_stepping ();
2814 mono_g_hash_table_foreach (thread_to_tls, notify_thread, NULL);
2817 mono_mutex_unlock (&suspend_mutex);
2819 if (suspend_count == 1)
2821 * Suspend creation of new threadpool threads, since they cannot run
2823 mono_thread_pool_suspend ();
2825 mono_loader_unlock ();
2829 * resume_vm:
2831 * Decrease the suspend count of the VM. If the count reaches 0, runtime threads
2832 * are resumed.
2834 static void
2835 resume_vm (void)
2837 int err;
2839 g_assert (debugger_thread_id == GetCurrentThreadId ());
2841 mono_loader_lock ();
2843 mono_mutex_lock (&suspend_mutex);
2845 g_assert (suspend_count > 0);
2846 suspend_count --;
2848 DEBUG(1, fprintf (log_file, "[%p] Resuming vm, suspend count=%d...\n", (gpointer)GetCurrentThreadId (), suspend_count));
2850 if (suspend_count == 0) {
2851 // FIXME: Is it safe to call this inside the lock ?
2852 stop_single_stepping ();
2853 mono_g_hash_table_foreach (thread_to_tls, reset_native_thread_suspend_state, NULL);
2856 /* Signal this even when suspend_count > 0, since some threads might have resume_count > 0 */
2857 err = mono_cond_broadcast (&suspend_cond);
2858 g_assert (err == 0);
2860 mono_mutex_unlock (&suspend_mutex);
2861 //g_assert (err == 0);
2863 if (suspend_count == 0)
2864 mono_thread_pool_resume ();
2866 mono_loader_unlock ();
2870 * resume_thread:
2872 * Resume just one thread.
2874 static void
2875 resume_thread (MonoInternalThread *thread)
2877 int err;
2878 DebuggerTlsData *tls;
2880 g_assert (debugger_thread_id == GetCurrentThreadId ());
2882 mono_loader_lock ();
2884 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
2885 g_assert (tls);
2887 mono_mutex_lock (&suspend_mutex);
2889 g_assert (suspend_count > 0);
2891 DEBUG(1, fprintf (log_file, "[sdb] Resuming thread %p...\n", (gpointer)(gssize)thread->tid));
2893 tls->resume_count += suspend_count;
2896 * Signal suspend_count without decreasing suspend_count, the threads will wake up
2897 * but only the one whose resume_count field is > 0 will be resumed.
2899 err = mono_cond_broadcast (&suspend_cond);
2900 g_assert (err == 0);
2902 mono_mutex_unlock (&suspend_mutex);
2903 //g_assert (err == 0);
2905 mono_loader_unlock ();
2908 static void
2909 invalidate_frames (DebuggerTlsData *tls)
2911 int i;
2913 if (!tls)
2914 tls = mono_native_tls_get_value (debugger_tls_id);
2915 g_assert (tls);
2917 for (i = 0; i < tls->frame_count; ++i) {
2918 if (tls->frames [i]->jit)
2919 mono_debug_free_method_jit_info (tls->frames [i]->jit);
2920 g_free (tls->frames [i]);
2922 g_free (tls->frames);
2923 tls->frame_count = 0;
2924 tls->frames = NULL;
2928 * suspend_current:
2930 * Suspend the current thread until the runtime is resumed. If the thread has a
2931 * pending invoke, then the invoke is executed before this function returns.
2933 static void
2934 suspend_current (void)
2936 int err;
2937 DebuggerTlsData *tls;
2939 g_assert (debugger_thread_id != GetCurrentThreadId ());
2941 if (mono_loader_lock_is_owned_by_self ()) {
2943 * If we own the loader mutex, can't suspend until we release it, since the
2944 * whole runtime can deadlock otherwise.
2946 return;
2949 tls = mono_native_tls_get_value (debugger_tls_id);
2950 g_assert (tls);
2952 mono_mutex_lock (&suspend_mutex);
2954 tls->suspending = FALSE;
2955 tls->really_suspended = TRUE;
2957 if (!tls->suspended) {
2958 tls->suspended = TRUE;
2959 MONO_SEM_POST (&suspend_sem);
2962 DEBUG(1, fprintf (log_file, "[%p] Suspended.\n", (gpointer)GetCurrentThreadId ()));
2964 while (suspend_count - tls->resume_count > 0) {
2965 #ifdef HOST_WIN32
2966 if (WAIT_TIMEOUT == WaitForSingleObject(suspend_cond, 0))
2968 mono_mutex_unlock (&suspend_mutex);
2969 Sleep(1);
2970 mono_mutex_lock (&suspend_mutex);
2972 else
2975 #else
2976 err = mono_cond_wait (&suspend_cond, &suspend_mutex);
2977 g_assert (err == 0);
2978 #endif
2981 tls->suspended = FALSE;
2982 tls->really_suspended = FALSE;
2984 threads_suspend_count --;
2986 mono_mutex_unlock (&suspend_mutex);
2988 DEBUG(1, fprintf (log_file, "[%p] Resumed.\n", (gpointer)GetCurrentThreadId ()));
2990 if (tls->pending_invoke) {
2991 /* Save the original context */
2992 tls->pending_invoke->has_ctx = TRUE;
2993 tls->pending_invoke->ctx = tls->context.ctx;
2995 invoke_method ();
2998 /* The frame info becomes invalid after a resume */
2999 tls->context.valid = FALSE;
3000 tls->async_state.valid = FALSE;
3001 invalidate_frames (tls);
3004 static void
3005 count_thread (gpointer key, gpointer value, gpointer user_data)
3007 DebuggerTlsData *tls = value;
3009 if (!tls->suspended && !tls->terminated)
3010 *(int*)user_data = *(int*)user_data + 1;
3013 static int
3014 count_threads_to_wait_for (void)
3016 int count = 0;
3018 mono_loader_lock ();
3019 mono_g_hash_table_foreach (thread_to_tls, count_thread, &count);
3020 mono_loader_unlock ();
3022 return count;
3026 * wait_for_suspend:
3028 * Wait until the runtime is completely suspended.
3030 static void
3031 wait_for_suspend (void)
3033 int nthreads, nwait, err;
3034 gboolean waited = FALSE;
3036 // FIXME: Threads starting/stopping ?
3037 mono_loader_lock ();
3038 nthreads = mono_g_hash_table_size (thread_to_tls);
3039 mono_loader_unlock ();
3041 while (TRUE) {
3042 nwait = count_threads_to_wait_for ();
3043 if (nwait) {
3044 DEBUG(1, fprintf (log_file, "Waiting for %d(%d) threads to suspend...\n", nwait, nthreads));
3045 err = MONO_SEM_WAIT (&suspend_sem);
3046 g_assert (err == 0);
3047 waited = TRUE;
3048 } else {
3049 break;
3053 if (waited)
3054 DEBUG(1, fprintf (log_file, "%d threads suspended.\n", nthreads));
3058 * is_suspended:
3060 * Return whenever the runtime is suspended.
3062 static gboolean
3063 is_suspended (void)
3065 return count_threads_to_wait_for () == 0;
3068 static MonoSeqPointInfo*
3069 get_seq_points (MonoDomain *domain, MonoMethod *method)
3071 MonoSeqPointInfo *seq_points;
3073 mono_domain_lock (domain);
3074 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
3075 if (!seq_points && method->is_inflated) {
3076 /* generic sharing + aot */
3077 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (method));
3078 if (!seq_points)
3079 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mini_get_shared_method (method));
3081 mono_domain_unlock (domain);
3083 return seq_points;
3086 static void
3087 no_seq_points_found (MonoMethod *method)
3090 * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
3092 printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE));
3096 * find_next_seq_point_for_native_offset:
3098 * Find the first sequence point after NATIVE_OFFSET.
3100 static SeqPoint*
3101 find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info)
3103 MonoSeqPointInfo *seq_points;
3104 int i;
3106 seq_points = get_seq_points (domain, method);
3107 if (!seq_points) {
3108 if (info)
3109 *info = NULL;
3110 return NULL;
3112 g_assert (seq_points);
3113 if (info)
3114 *info = seq_points;
3116 for (i = 0; i < seq_points->len; ++i) {
3117 if (seq_points->seq_points [i].native_offset >= native_offset)
3118 return &seq_points->seq_points [i];
3121 return NULL;
3125 * find_prev_seq_point_for_native_offset:
3127 * Find the first sequence point before NATIVE_OFFSET.
3129 static SeqPoint*
3130 find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info)
3132 MonoSeqPointInfo *seq_points;
3133 int i;
3135 seq_points = get_seq_points (domain, method);
3136 if (info)
3137 *info = seq_points;
3138 if (!seq_points)
3139 return NULL;
3141 for (i = seq_points->len - 1; i >= 0; --i) {
3142 if (seq_points->seq_points [i].native_offset <= native_offset)
3143 return &seq_points->seq_points [i];
3146 return NULL;
3150 * find_seq_point:
3152 * Find the sequence point corresponding to the IL offset IL_OFFSET, which
3153 * should be the location of a sequence point.
3155 static G_GNUC_UNUSED SeqPoint*
3156 find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info)
3158 MonoSeqPointInfo *seq_points;
3159 int i;
3161 *info = NULL;
3163 seq_points = get_seq_points (domain, method);
3164 if (!seq_points)
3165 return NULL;
3166 *info = seq_points;
3168 for (i = 0; i < seq_points->len; ++i) {
3169 if (seq_points->seq_points [i].il_offset == il_offset)
3170 return &seq_points->seq_points [i];
3173 return NULL;
3176 typedef struct {
3177 DebuggerTlsData *tls;
3178 GSList *frames;
3179 } ComputeFramesUserData;
3181 static gboolean
3182 process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
3184 ComputeFramesUserData *ud = user_data;
3185 StackFrame *frame;
3186 MonoMethod *method, *actual_method, *api_method;
3187 SeqPoint *sp;
3188 int flags = 0;
3190 if (info->type != FRAME_TYPE_MANAGED) {
3191 if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) {
3192 /* Mark the last frame as an invoke frame */
3193 if (ud->frames)
3194 ((StackFrame*)g_slist_last (ud->frames)->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE;
3196 return FALSE;
3199 if (info->ji)
3200 method = jinfo_get_method (info->ji);
3201 else
3202 method = info->method;
3203 actual_method = info->actual_method;
3204 api_method = method;
3206 if (!method)
3207 return FALSE;
3209 if (!method || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && method->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE))
3210 return FALSE;
3212 if (info->il_offset == -1) {
3213 /* mono_debug_il_offset_from_address () doesn't seem to be precise enough (#2092) */
3214 if (ud->frames == NULL) {
3215 sp = find_prev_seq_point_for_native_offset (info->domain, method, info->native_offset, NULL);
3216 if (sp)
3217 info->il_offset = sp->il_offset;
3219 if (info->il_offset == -1)
3220 info->il_offset = mono_debug_il_offset_from_address (method, info->domain, info->native_offset);
3223 DEBUG (1, fprintf (log_file, "\tFrame: %s:%x(%x) %d\n", mono_method_full_name (method, TRUE), info->il_offset, info->native_offset, info->managed));
3225 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
3226 if (!CHECK_PROTOCOL_VERSION (2, 17))
3227 /* Older clients can't handle this flag */
3228 return FALSE;
3229 api_method = mono_marshal_method_from_wrapper (method);
3230 if (!api_method)
3231 return FALSE;
3232 actual_method = api_method;
3233 flags |= FRAME_FLAG_NATIVE_TRANSITION;
3236 frame = g_new0 (StackFrame, 1);
3237 frame->method = method;
3238 frame->actual_method = actual_method;
3239 frame->api_method = api_method;
3240 frame->il_offset = info->il_offset;
3241 frame->native_offset = info->native_offset;
3242 frame->flags = flags;
3243 frame->ji = info->ji;
3244 if (info->reg_locations)
3245 memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*));
3246 if (ctx) {
3247 frame->ctx = *ctx;
3248 frame->has_ctx = TRUE;
3250 frame->domain = info->domain;
3252 ud->frames = g_slist_append (ud->frames, frame);
3254 return FALSE;
3257 static gboolean
3258 process_filter_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
3260 ComputeFramesUserData *ud = user_data;
3263 * 'tls->filter_ctx' is the location of the throw site.
3265 * mono_walk_stack() will never actually hit the throw site, but unwind
3266 * directly from the filter to the call site; we abort stack unwinding here
3267 * once this happens and resume from the throw site.
3270 if (MONO_CONTEXT_GET_SP (ctx) >= MONO_CONTEXT_GET_SP (&ud->tls->filter_state.ctx))
3271 return TRUE;
3273 return process_frame (info, ctx, user_data);
3276 static void
3277 compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
3279 ComputeFramesUserData user_data;
3280 GSList *tmp;
3281 int i, findex, new_frame_count;
3282 StackFrame **new_frames, *f;
3283 MonoUnwindOptions opts = MONO_UNWIND_DEFAULT|MONO_UNWIND_REG_LOCATIONS;
3285 // FIXME: Locking on tls
3286 if (tls->frames && tls->frames_up_to_date)
3287 return;
3289 DEBUG(1, fprintf (log_file, "Frames for %p(tid=%lx):\n", thread, (glong)thread->tid));
3291 user_data.tls = tls;
3292 user_data.frames = NULL;
3293 if (tls->terminated) {
3294 tls->frame_count = 0;
3295 return;
3296 } if (!tls->really_suspended && tls->async_state.valid) {
3297 /* Have to use the state saved by the signal handler */
3298 process_frame (&tls->async_last_frame, NULL, &user_data);
3299 mono_walk_stack_with_state (process_frame, &tls->async_state, opts, &user_data);
3300 } else if (tls->filter_state.valid) {
3302 * We are inside an exception filter.
3304 * First we add all the frames from inside the filter; 'tls->ctx' has the current context.
3306 if (tls->context.valid)
3307 mono_walk_stack_with_state (process_filter_frame, &tls->context, opts, &user_data);
3309 * After that, we resume unwinding from the location where the exception has been thrown.
3311 mono_walk_stack_with_state (process_frame, &tls->filter_state, opts, &user_data);
3312 } else if (tls->context.valid) {
3313 mono_walk_stack_with_state (process_frame, &tls->context, opts, &user_data);
3314 } else {
3315 // FIXME:
3316 tls->frame_count = 0;
3317 return;
3320 new_frame_count = g_slist_length (user_data.frames);
3321 new_frames = g_new0 (StackFrame*, new_frame_count);
3322 findex = 0;
3323 for (tmp = user_data.frames; tmp; tmp = tmp->next) {
3324 f = tmp->data;
3327 * Reuse the id for already existing stack frames, so invokes don't invalidate
3328 * the still valid stack frames.
3330 for (i = 0; i < tls->frame_count; ++i) {
3331 if (MONO_CONTEXT_GET_SP (&tls->frames [i]->ctx) == MONO_CONTEXT_GET_SP (&f->ctx)) {
3332 f->id = tls->frames [i]->id;
3333 break;
3337 if (i >= tls->frame_count)
3338 f->id = InterlockedIncrement (&frame_id);
3340 new_frames [findex ++] = f;
3343 g_slist_free (user_data.frames);
3345 invalidate_frames (tls);
3347 tls->frames = new_frames;
3348 tls->frame_count = new_frame_count;
3349 tls->frames_up_to_date = TRUE;
3353 * GHFunc to emit an appdomain creation event
3354 * @param key Don't care
3355 * @param value A loaded appdomain
3356 * @param user_data Don't care
3358 static void
3359 emit_appdomain_load (gpointer key, gpointer value, gpointer user_data)
3361 process_profiler_event (EVENT_KIND_APPDOMAIN_CREATE, value);
3362 g_hash_table_foreach (get_agent_domain_info (value)->loaded_classes, emit_type_load, NULL);
3366 * GHFunc to emit a thread start event
3367 * @param key A thread id
3368 * @param value A thread object
3369 * @param user_data Don't care
3371 static void
3372 emit_thread_start (gpointer key, gpointer value, gpointer user_data)
3374 if (GPOINTER_TO_INT (key) != debugger_thread_id)
3375 process_profiler_event (EVENT_KIND_THREAD_START, value);
3379 * GFunc to emit an assembly load event
3380 * @param value A loaded assembly
3381 * @param user_data Don't care
3383 static void
3384 emit_assembly_load (gpointer value, gpointer user_data)
3386 process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, value);
3390 * GFunc to emit a type load event
3391 * @param value A loaded type
3392 * @param user_data Don't care
3394 static void
3395 emit_type_load (gpointer key, gpointer value, gpointer user_data)
3397 process_profiler_event (EVENT_KIND_TYPE_LOAD, value);
3400 static char*
3401 strdup_tolower (char *s)
3403 char *s2, *p;
3405 s2 = g_strdup (s);
3406 for (p = s2; *p; ++p)
3407 *p = tolower (*p);
3408 return s2;
3412 * EVENT HANDLING
3416 * create_event_list:
3418 * Return a list of event request ids matching EVENT, starting from REQS, which
3419 * can be NULL to include all event requests. Set SUSPEND_POLICY to the suspend
3420 * policy.
3421 * We return request ids, instead of requests, to simplify threading, since
3422 * requests could be deleted anytime when the loader lock is not held.
3423 * LOCKING: Assumes the loader lock is held.
3425 static GSList*
3426 create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo *ei, int *suspend_policy)
3428 int i, j;
3429 GSList *events = NULL;
3431 *suspend_policy = SUSPEND_POLICY_NONE;
3433 if (!reqs)
3434 reqs = event_requests;
3436 if (!reqs)
3437 return NULL;
3439 for (i = 0; i < reqs->len; ++i) {
3440 EventRequest *req = g_ptr_array_index (reqs, i);
3441 if (req->event_kind == event) {
3442 gboolean filtered = FALSE;
3444 /* Apply filters */
3445 for (j = 0; j < req->nmodifiers; ++j) {
3446 Modifier *mod = &req->modifiers [j];
3448 if (mod->kind == MOD_KIND_COUNT) {
3449 filtered = TRUE;
3450 if (mod->data.count > 0) {
3451 if (mod->data.count > 0) {
3452 mod->data.count --;
3453 if (mod->data.count == 0)
3454 filtered = FALSE;
3457 } else if (mod->kind == MOD_KIND_THREAD_ONLY) {
3458 if (mod->data.thread != mono_thread_internal_current ())
3459 filtered = TRUE;
3460 } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && ei) {
3461 if (mod->data.exc_class && mod->subclasses && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass))
3462 filtered = TRUE;
3463 if (mod->data.exc_class && !mod->subclasses && mod->data.exc_class != ei->exc->vtable->klass)
3464 filtered = TRUE;
3465 if (ei->caught && !mod->caught)
3466 filtered = TRUE;
3467 if (!ei->caught && !mod->uncaught)
3468 filtered = TRUE;
3469 } else if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && ji) {
3470 int k;
3471 gboolean found = FALSE;
3472 MonoAssembly **assemblies = mod->data.assemblies;
3474 if (assemblies) {
3475 for (k = 0; assemblies [k]; ++k)
3476 if (assemblies [k] == jinfo_get_method (ji)->klass->image->assembly)
3477 found = TRUE;
3479 if (!found)
3480 filtered = TRUE;
3481 } else if (mod->kind == MOD_KIND_SOURCE_FILE_ONLY && ei && ei->klass) {
3482 gpointer iter = NULL;
3483 MonoMethod *method;
3484 MonoDebugSourceInfo *sinfo;
3485 char *source_file, *s;
3486 gboolean found = FALSE;
3487 int i;
3488 GPtrArray *source_file_list;
3490 while ((method = mono_class_get_methods (ei->klass, &iter))) {
3491 MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
3493 if (minfo) {
3494 mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
3495 for (i = 0; i < source_file_list->len; ++i) {
3496 sinfo = g_ptr_array_index (source_file_list, i);
3498 * Do a case-insesitive match by converting the file name to
3499 * lowercase.
3501 s = strdup_tolower (sinfo->source_file);
3502 if (g_hash_table_lookup (mod->data.source_files, s))
3503 found = TRUE;
3504 else {
3505 char *s2 = g_path_get_basename (sinfo->source_file);
3506 char *s3 = strdup_tolower (s2);
3508 if (g_hash_table_lookup (mod->data.source_files, s3))
3509 found = TRUE;
3510 g_free (s2);
3511 g_free (s3);
3513 g_free (s);
3515 g_ptr_array_free (source_file_list, TRUE);
3518 if (!found)
3519 filtered = TRUE;
3520 } else if (mod->kind == MOD_KIND_TYPE_NAME_ONLY && ei && ei->klass) {
3521 char *s;
3523 s = mono_type_full_name (&ei->klass->byval_arg);
3524 if (!g_hash_table_lookup (mod->data.type_names, s))
3525 filtered = TRUE;
3526 g_free (s);
3527 } else if (mod->kind == MOD_KIND_STEP) {
3528 if ((mod->data.filter & STEP_FILTER_STATIC_CTOR) && ji &&
3529 (jinfo_get_method (ji)->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
3530 !strcmp (jinfo_get_method (ji)->name, ".cctor"))
3531 filtered = TRUE;
3532 if ((mod->data.filter & STEP_FILTER_DEBUGGER_HIDDEN) && ji) {
3533 MonoCustomAttrInfo *ainfo;
3534 static MonoClass *klass;
3536 if (!klass) {
3537 klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerHiddenAttribute");
3538 g_assert (klass);
3540 if (!ji->dbg_hidden_inited) {
3541 ainfo = mono_custom_attrs_from_method (jinfo_get_method (ji));
3542 if (ainfo) {
3543 if (mono_custom_attrs_has_attr (ainfo, klass))
3544 ji->dbg_hidden = TRUE;
3545 mono_custom_attrs_free (ainfo);
3547 ji->dbg_hidden_inited = TRUE;
3549 if (ji->dbg_hidden)
3550 filtered = TRUE;
3552 if ((mod->data.filter & STEP_FILTER_DEBUGGER_STEP_THROUGH) && ji) {
3553 MonoCustomAttrInfo *ainfo;
3554 static MonoClass *klass;
3556 if (!klass) {
3557 klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerStepThroughAttribute");
3558 g_assert (klass);
3560 if (!ji->dbg_step_through_inited) {
3561 ainfo = mono_custom_attrs_from_method (jinfo_get_method (ji));
3562 if (ainfo) {
3563 if (mono_custom_attrs_has_attr (ainfo, klass))
3564 ji->dbg_step_through = TRUE;
3565 mono_custom_attrs_free (ainfo);
3567 ainfo = mono_custom_attrs_from_class (jinfo_get_method (ji)->klass);
3568 if (ainfo) {
3569 if (mono_custom_attrs_has_attr (ainfo, klass))
3570 ji->dbg_step_through = TRUE;
3571 mono_custom_attrs_free (ainfo);
3573 ji->dbg_step_through_inited = TRUE;
3575 if (ji->dbg_step_through)
3576 filtered = TRUE;
3578 if ((mod->data.filter & STEP_FILTER_DEBUGGER_NON_USER_CODE) && ji) {
3579 MonoCustomAttrInfo *ainfo;
3580 static MonoClass *klass;
3582 if (!klass) {
3583 klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerNonUserCodeAttribute");
3584 g_assert (klass);
3586 if (!ji->dbg_non_user_code_inited) {
3587 ainfo = mono_custom_attrs_from_method (jinfo_get_method (ji));
3588 if (ainfo) {
3589 if (mono_custom_attrs_has_attr (ainfo, klass))
3590 ji->dbg_non_user_code = TRUE;
3591 mono_custom_attrs_free (ainfo);
3593 ainfo = mono_custom_attrs_from_class (jinfo_get_method (ji)->klass);
3594 if (ainfo) {
3595 if (mono_custom_attrs_has_attr (ainfo, klass))
3596 ji->dbg_non_user_code = TRUE;
3597 mono_custom_attrs_free (ainfo);
3599 ji->dbg_non_user_code_inited = TRUE;
3601 if (ji->dbg_non_user_code)
3602 filtered = TRUE;
3607 if (!filtered) {
3608 *suspend_policy = MAX (*suspend_policy, req->suspend_policy);
3609 events = g_slist_append (events, GINT_TO_POINTER (req->id));
3614 /* Send a VM START/DEATH event by default */
3615 if (event == EVENT_KIND_VM_START)
3616 events = g_slist_append (events, GINT_TO_POINTER (0));
3617 if (event == EVENT_KIND_VM_DEATH)
3618 events = g_slist_append (events, GINT_TO_POINTER (0));
3620 return events;
3623 static G_GNUC_UNUSED const char*
3624 event_to_string (EventKind event)
3626 switch (event) {
3627 case EVENT_KIND_VM_START: return "VM_START";
3628 case EVENT_KIND_VM_DEATH: return "VM_DEATH";
3629 case EVENT_KIND_THREAD_START: return "THREAD_START";
3630 case EVENT_KIND_THREAD_DEATH: return "THREAD_DEATH";
3631 case EVENT_KIND_APPDOMAIN_CREATE: return "APPDOMAIN_CREATE";
3632 case EVENT_KIND_APPDOMAIN_UNLOAD: return "APPDOMAIN_UNLOAD";
3633 case EVENT_KIND_METHOD_ENTRY: return "METHOD_ENTRY";
3634 case EVENT_KIND_METHOD_EXIT: return "METHOD_EXIT";
3635 case EVENT_KIND_ASSEMBLY_LOAD: return "ASSEMBLY_LOAD";
3636 case EVENT_KIND_ASSEMBLY_UNLOAD: return "ASSEMBLY_UNLOAD";
3637 case EVENT_KIND_BREAKPOINT: return "BREAKPOINT";
3638 case EVENT_KIND_STEP: return "STEP";
3639 case EVENT_KIND_TYPE_LOAD: return "TYPE_LOAD";
3640 case EVENT_KIND_EXCEPTION: return "EXCEPTION";
3641 case EVENT_KIND_KEEPALIVE: return "KEEPALIVE";
3642 case EVENT_KIND_USER_BREAK: return "USER_BREAK";
3643 case EVENT_KIND_USER_LOG: return "USER_LOG";
3644 default:
3645 g_assert_not_reached ();
3646 return "";
3651 * process_event:
3653 * Send an event to the client, suspending the vm if needed.
3654 * LOCKING: Since this can suspend the calling thread, no locks should be held
3655 * by the caller.
3656 * The EVENTS list is freed by this function.
3658 static void
3659 process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx, GSList *events, int suspend_policy)
3661 Buffer buf;
3662 GSList *l;
3663 MonoDomain *domain = mono_domain_get ();
3664 MonoThread *thread = NULL;
3665 MonoObject *keepalive_obj = NULL;
3666 gboolean send_success = FALSE;
3667 static int ecount;
3668 int nevents;
3670 if (!inited) {
3671 DEBUG (2, fprintf (log_file, "Debugger agent not initialized yet: dropping %s\n", event_to_string (event)));
3672 return;
3675 if (!vm_start_event_sent && event != EVENT_KIND_VM_START) {
3676 // FIXME: We miss those events
3677 DEBUG (2, fprintf (log_file, "VM start event not sent yet: dropping %s\n", event_to_string (event)));
3678 return;
3681 if (vm_death_event_sent) {
3682 DEBUG (2, fprintf (log_file, "VM death event has been sent: dropping %s\n", event_to_string (event)));
3683 return;
3686 if (mono_runtime_is_shutting_down () && event != EVENT_KIND_VM_DEATH) {
3687 DEBUG (2, fprintf (log_file, "Mono runtime is shutting down: dropping %s\n", event_to_string (event)));
3688 return;
3691 if (disconnected) {
3692 DEBUG (2, fprintf (log_file, "Debugger client is not connected: dropping %s\n", event_to_string (event)));
3693 return;
3696 if (event == EVENT_KIND_KEEPALIVE)
3697 suspend_policy = SUSPEND_POLICY_NONE;
3698 else {
3699 if (events == NULL)
3700 return;
3702 if (agent_config.defer) {
3703 /* Make sure the thread id is always set when doing deferred debugging */
3704 if (debugger_thread_id == GetCurrentThreadId ()) {
3705 /* Don't suspend on events from the debugger thread */
3706 suspend_policy = SUSPEND_POLICY_NONE;
3707 thread = mono_thread_get_main ();
3709 else thread = mono_thread_current ();
3710 } else {
3711 if (debugger_thread_id == GetCurrentThreadId () && event != EVENT_KIND_VM_DEATH)
3712 // FIXME: Send these with a NULL thread, don't suspend the current thread
3713 return;
3717 nevents = g_slist_length (events);
3718 buffer_init (&buf, 128);
3719 buffer_add_byte (&buf, suspend_policy);
3720 buffer_add_int (&buf, nevents);
3722 for (l = events; l; l = l->next) {
3723 buffer_add_byte (&buf, event); // event kind
3724 buffer_add_int (&buf, GPOINTER_TO_INT (l->data)); // request id
3726 ecount ++;
3728 if (!thread)
3729 thread = mono_thread_current ();
3731 if (event == EVENT_KIND_VM_START && arg != NULL)
3732 thread = arg;
3734 buffer_add_objid (&buf, (MonoObject*)thread); // thread
3736 switch (event) {
3737 case EVENT_KIND_THREAD_START:
3738 case EVENT_KIND_THREAD_DEATH:
3739 break;
3740 case EVENT_KIND_APPDOMAIN_CREATE:
3741 case EVENT_KIND_APPDOMAIN_UNLOAD:
3742 buffer_add_domainid (&buf, arg);
3743 break;
3744 case EVENT_KIND_METHOD_ENTRY:
3745 case EVENT_KIND_METHOD_EXIT:
3746 buffer_add_methodid (&buf, domain, arg);
3747 break;
3748 case EVENT_KIND_ASSEMBLY_LOAD:
3749 case EVENT_KIND_ASSEMBLY_UNLOAD:
3750 buffer_add_assemblyid (&buf, domain, arg);
3751 break;
3752 case EVENT_KIND_TYPE_LOAD:
3753 buffer_add_typeid (&buf, domain, arg);
3754 break;
3755 case EVENT_KIND_BREAKPOINT:
3756 case EVENT_KIND_STEP:
3757 buffer_add_methodid (&buf, domain, arg);
3758 buffer_add_long (&buf, il_offset);
3759 break;
3760 case EVENT_KIND_VM_START:
3761 buffer_add_domainid (&buf, mono_get_root_domain ());
3762 break;
3763 case EVENT_KIND_VM_DEATH:
3764 if (CHECK_PROTOCOL_VERSION (2, 27))
3765 buffer_add_int (&buf, mono_environment_exitcode_get ());
3766 break;
3767 case EVENT_KIND_EXCEPTION: {
3768 EventInfo *ei = arg;
3769 buffer_add_objid (&buf, ei->exc);
3771 * We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it
3772 * later after the suspension. (#12494).
3774 keepalive_obj = ei->exc;
3775 break;
3777 case EVENT_KIND_USER_BREAK:
3778 break;
3779 case EVENT_KIND_USER_LOG: {
3780 EventInfo *ei = arg;
3781 buffer_add_int (&buf, ei->level);
3782 buffer_add_string (&buf, ei->category ? ei->category : "");
3783 buffer_add_string (&buf, ei->message ? ei->message : "");
3784 break;
3786 case EVENT_KIND_KEEPALIVE:
3787 suspend_policy = SUSPEND_POLICY_NONE;
3788 break;
3789 default:
3790 g_assert_not_reached ();
3794 if (event == EVENT_KIND_VM_START) {
3795 suspend_policy = agent_config.suspend ? SUSPEND_POLICY_ALL : SUSPEND_POLICY_NONE;
3796 if (!agent_config.defer)
3797 start_debugger_thread ();
3800 if (event == EVENT_KIND_VM_DEATH) {
3801 vm_death_event_sent = TRUE;
3802 suspend_policy = SUSPEND_POLICY_NONE;
3805 if (mono_runtime_is_shutting_down ())
3806 suspend_policy = SUSPEND_POLICY_NONE;
3808 if (suspend_policy != SUSPEND_POLICY_NONE) {
3810 * Save the thread context and start suspending before sending the packet,
3811 * since we could be receiving the resume request before send_packet ()
3812 * returns.
3814 save_thread_context (ctx);
3815 suspend_vm ();
3817 if (keepalive_obj)
3818 /* This will keep this object alive */
3819 get_objref (keepalive_obj);
3822 send_success = send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
3824 buffer_free (&buf);
3826 g_slist_free (events);
3827 events = NULL;
3829 if (!send_success) {
3830 DEBUG (2, fprintf (log_file, "Sending command %s failed.\n", event_to_string (event)));
3831 return;
3834 if (event == EVENT_KIND_VM_START) {
3835 vm_start_event_sent = TRUE;
3838 DEBUG (1, fprintf (log_file, "[%p] Sent %d events %s(%d), suspend=%d.\n", (gpointer)GetCurrentThreadId (), nevents, event_to_string (event), ecount, suspend_policy));
3840 switch (suspend_policy) {
3841 case SUSPEND_POLICY_NONE:
3842 break;
3843 case SUSPEND_POLICY_ALL:
3844 suspend_current ();
3845 break;
3846 case SUSPEND_POLICY_EVENT_THREAD:
3847 NOT_IMPLEMENTED;
3848 break;
3849 default:
3850 g_assert_not_reached ();
3854 static void
3855 process_profiler_event (EventKind event, gpointer arg)
3857 int suspend_policy;
3858 GSList *events;
3859 EventInfo ei, *ei_arg = NULL;
3861 if (event == EVENT_KIND_TYPE_LOAD) {
3862 ei.klass = arg;
3863 ei_arg = &ei;
3866 mono_loader_lock ();
3867 events = create_event_list (event, NULL, NULL, ei_arg, &suspend_policy);
3868 mono_loader_unlock ();
3870 process_event (event, arg, 0, NULL, events, suspend_policy);
3873 static void
3874 runtime_initialized (MonoProfiler *prof)
3876 process_profiler_event (EVENT_KIND_VM_START, mono_thread_current ());
3877 if (agent_config.defer)
3878 start_debugger_thread ();
3881 static void
3882 runtime_shutdown (MonoProfiler *prof)
3884 process_profiler_event (EVENT_KIND_VM_DEATH, mono_thread_current ());
3886 mono_debugger_agent_cleanup ();
3889 static void
3890 thread_startup (MonoProfiler *prof, uintptr_t tid)
3892 MonoInternalThread *thread = mono_thread_internal_current ();
3893 MonoInternalThread *old_thread;
3894 DebuggerTlsData *tls;
3896 if (tid == debugger_thread_id)
3897 return;
3899 g_assert (thread->tid == tid);
3901 mono_loader_lock ();
3902 old_thread = mono_g_hash_table_lookup (tid_to_thread, (gpointer)tid);
3903 mono_loader_unlock ();
3904 if (old_thread) {
3905 if (thread == old_thread) {
3907 * For some reason, thread_startup () might be called for the same thread
3908 * multiple times (attach ?).
3910 DEBUG (1, fprintf (log_file, "[%p] thread_start () called multiple times for %p, ignored.\n", (gpointer)tid, (gpointer)tid));
3911 return;
3912 } else {
3914 * thread_end () might not be called for some threads, and the tid could
3915 * get reused.
3917 DEBUG (1, fprintf (log_file, "[%p] Removing stale data for tid %p.\n", (gpointer)tid, (gpointer)tid));
3918 mono_loader_lock ();
3919 mono_g_hash_table_remove (thread_to_tls, old_thread);
3920 mono_g_hash_table_remove (tid_to_thread, (gpointer)tid);
3921 mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
3922 mono_loader_unlock ();
3926 tls = mono_native_tls_get_value (debugger_tls_id);
3927 g_assert (!tls);
3928 // FIXME: Free this somewhere
3929 tls = g_new0 (DebuggerTlsData, 1);
3930 MONO_GC_REGISTER_ROOT_SINGLE (tls->thread);
3931 tls->thread = thread;
3932 mono_native_tls_set_value (debugger_tls_id, tls);
3934 DEBUG (1, fprintf (log_file, "[%p] Thread started, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls));
3936 mono_loader_lock ();
3937 mono_g_hash_table_insert (thread_to_tls, thread, tls);
3938 mono_g_hash_table_insert (tid_to_thread, (gpointer)tid, thread);
3939 mono_g_hash_table_insert (tid_to_thread_obj, (gpointer)tid, mono_thread_current ());
3940 mono_loader_unlock ();
3942 process_profiler_event (EVENT_KIND_THREAD_START, thread);
3945 * suspend_vm () could have missed this thread, so wait for a resume.
3947 suspend_current ();
3950 static void
3951 thread_end (MonoProfiler *prof, uintptr_t tid)
3953 MonoInternalThread *thread;
3954 DebuggerTlsData *tls = NULL;
3956 mono_loader_lock ();
3957 thread = mono_g_hash_table_lookup (tid_to_thread, (gpointer)tid);
3958 if (thread) {
3959 mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
3960 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
3961 if (tls) {
3962 /* FIXME: Maybe we need to free this instead, but some code can't handle that */
3963 tls->terminated = TRUE;
3964 /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */
3965 MONO_GC_UNREGISTER_ROOT (tls->thread);
3966 tls->thread = NULL;
3969 mono_loader_unlock ();
3971 /* We might be called for threads started before we registered the start callback */
3972 if (thread) {
3973 DEBUG (1, fprintf (log_file, "[%p] Thread terminated, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls));
3974 process_profiler_event (EVENT_KIND_THREAD_DEATH, thread);
3978 static void
3979 appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result)
3981 mono_loader_lock ();
3982 g_hash_table_insert (domains, domain, domain);
3983 mono_loader_unlock ();
3985 process_profiler_event (EVENT_KIND_APPDOMAIN_CREATE, domain);
3988 static void
3989 appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
3991 clear_breakpoints_for_domain (domain);
3993 mono_loader_lock ();
3994 /* Invalidate each thread's frame stack */
3995 mono_g_hash_table_foreach (thread_to_tls, invalidate_each_thread, NULL);
3996 mono_loader_unlock ();
3998 process_profiler_event (EVENT_KIND_APPDOMAIN_UNLOAD, domain);
4002 * invalidate_each_thread:
4004 * A GHFunc to invalidate frames.
4005 * value must be a DebuggerTlsData*
4007 static void
4008 invalidate_each_thread (gpointer key, gpointer value, gpointer user_data)
4010 invalidate_frames (value);
4013 static void
4014 assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result)
4016 /* Sent later in jit_end () */
4017 mono_loader_lock ();
4018 g_ptr_array_add (pending_assembly_loads, assembly);
4019 mono_loader_unlock ();
4022 static void
4023 assembly_unload (MonoProfiler *prof, MonoAssembly *assembly)
4025 process_profiler_event (EVENT_KIND_ASSEMBLY_UNLOAD, assembly);
4027 clear_event_requests_for_assembly (assembly);
4028 clear_types_for_assembly (assembly);
4031 static void
4032 start_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
4034 #if defined(HOST_WIN32) && !defined(__GNUC__)
4035 gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
4036 #else
4037 gpointer stackptr = __builtin_frame_address (1);
4038 #endif
4039 MonoInternalThread *thread = mono_thread_internal_current ();
4040 DebuggerTlsData *tls;
4042 mono_loader_lock ();
4044 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
4045 /* Could be the debugger thread with assembly/type load hooks */
4046 if (tls)
4047 tls->invoke_addr = stackptr;
4049 mono_loader_unlock ();
4052 static void
4053 end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
4055 int i;
4056 #if defined(HOST_WIN32) && !defined(__GNUC__)
4057 gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
4058 #else
4059 gpointer stackptr = __builtin_frame_address (1);
4060 #endif
4062 if (!embedding || ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
4063 return;
4066 * We need to stop single stepping when exiting a runtime invoke, since if it is
4067 * a step out, it may return to native code, and thus never end.
4069 mono_loader_lock ();
4070 ss_invoke_addr = NULL;
4072 for (i = 0; i < event_requests->len; ++i) {
4073 EventRequest *req = g_ptr_array_index (event_requests, i);
4075 if (req->event_kind == EVENT_KIND_STEP) {
4076 ss_destroy (req->info);
4077 g_ptr_array_remove_index_fast (event_requests, i);
4078 g_free (req);
4079 break;
4082 mono_loader_unlock ();
4085 static void
4086 send_type_load (MonoClass *klass)
4088 gboolean type_load = FALSE;
4089 MonoDomain *domain = mono_domain_get ();
4090 AgentDomainInfo *info = NULL;
4092 mono_loader_lock ();
4093 mono_domain_lock (domain);
4095 info = get_agent_domain_info (domain);
4097 if (!g_hash_table_lookup (info->loaded_classes, klass)) {
4098 type_load = TRUE;
4099 g_hash_table_insert (info->loaded_classes, klass, klass);
4102 mono_domain_unlock (domain);
4103 mono_loader_unlock ();
4104 if (type_load)
4105 emit_type_load (klass, klass, NULL);
4109 * Emit load events for all types currently loaded in the domain.
4110 * Takes the loader and domain locks.
4111 * user_data is unused.
4113 static void
4114 send_types_for_domain (MonoDomain *domain, void *user_data)
4116 AgentDomainInfo *info = NULL;
4118 mono_loader_lock ();
4119 mono_domain_lock (domain);
4120 info = get_agent_domain_info (domain);
4121 g_assert (info);
4122 g_hash_table_foreach (info->loaded_classes, emit_type_load, NULL);
4123 mono_domain_unlock (domain);
4124 mono_loader_unlock ();
4127 static void
4128 jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
4131 * We emit type load events when the first method of the type is JITted,
4132 * since the class load profiler callbacks might be called with the
4133 * loader lock held. They could also occur in the debugger thread.
4134 * Same for assembly load events.
4136 while (TRUE) {
4137 MonoAssembly *assembly = NULL;
4139 // FIXME: Maybe store this in TLS so the thread of the event is correct ?
4140 mono_loader_lock ();
4141 if (pending_assembly_loads->len > 0) {
4142 assembly = g_ptr_array_index (pending_assembly_loads, 0);
4143 g_ptr_array_remove_index (pending_assembly_loads, 0);
4145 mono_loader_unlock ();
4147 if (assembly) {
4148 process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, assembly);
4149 } else {
4150 break;
4154 send_type_load (method->klass);
4156 if (!result)
4157 add_pending_breakpoints (method, jinfo);
4161 * BREAKPOINTS/SINGLE STEPPING
4165 * Contains information about an inserted breakpoint.
4167 typedef struct {
4168 long il_offset, native_offset;
4169 guint8 *ip;
4170 MonoJitInfo *ji;
4171 MonoDomain *domain;
4172 SeqPoint *sp;
4173 } BreakpointInstance;
4176 * Contains generic information about a breakpoint.
4178 typedef struct {
4180 * The method where the breakpoint is placed. Can be NULL in which case it
4181 * is inserted into every method. This is used to implement method entry/
4182 * exit events. Can be a generic method definition, in which case the
4183 * breakpoint is inserted into every instance.
4185 MonoMethod *method;
4186 long il_offset;
4187 EventRequest *req;
4189 * A list of BreakpointInstance structures describing where the breakpoint
4190 * was inserted. There could be more than one because of
4191 * generics/appdomains/method entry/exit.
4193 GPtrArray *children;
4194 } MonoBreakpoint;
4196 /* List of breakpoints */
4197 static GPtrArray *breakpoints;
4198 /* Maps breakpoint locations to the number of breakpoints at that location */
4199 static GHashTable *bp_locs;
4201 static void
4202 breakpoints_init (void)
4204 breakpoints = g_ptr_array_new ();
4205 bp_locs = g_hash_table_new (NULL, NULL);
4209 * insert_breakpoint:
4211 * Insert the breakpoint described by BP into the method described by
4212 * JI.
4214 static void
4215 insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp, MonoError *error)
4217 int i, count;
4218 BreakpointInstance *inst;
4219 SeqPoint *sp = NULL;
4221 if (error)
4222 mono_error_init (error);
4224 for (i = 0; i < seq_points->len; ++i) {
4225 sp = &seq_points->seq_points [i];
4227 if (sp->il_offset == bp->il_offset)
4228 break;
4231 if (i == seq_points->len) {
4233 * The set of IL offsets with seq points doesn't completely match the
4234 * info returned by CMD_METHOD_GET_DEBUG_INFO (#407).
4236 for (i = 0; i < seq_points->len; ++i) {
4237 sp = &seq_points->seq_points [i];
4239 if (sp->il_offset != METHOD_ENTRY_IL_OFFSET && sp->il_offset != METHOD_EXIT_IL_OFFSET && sp->il_offset + 1 == bp->il_offset)
4240 break;
4244 if (i == seq_points->len) {
4245 char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (jinfo_get_method (ji), TRUE), bp->il_offset, seq_points->len);
4247 for (i = 0; i < seq_points->len; ++i)
4248 DEBUG (1, fprintf (log_file, "%d\n", seq_points->seq_points [i].il_offset));
4250 if (error) {
4251 mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s);
4252 g_warning ("%s", s);
4253 g_free (s);
4254 return;
4255 } else {
4256 g_warning ("%s", s);
4257 g_free (s);
4258 return;
4262 inst = g_new0 (BreakpointInstance, 1);
4263 inst->sp = sp;
4264 inst->native_offset = sp->native_offset;
4265 inst->ip = (guint8*)ji->code_start + sp->native_offset;
4266 inst->ji = ji;
4267 inst->domain = domain;
4269 mono_loader_lock ();
4271 g_ptr_array_add (bp->children, inst);
4273 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, inst->ip));
4274 g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
4275 mono_loader_unlock ();
4277 if (sp->native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
4278 DEBUG (1, fprintf (log_file, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset));
4279 } else if (count == 0) {
4280 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
4281 mono_arch_set_breakpoint (ji, inst->ip);
4282 #else
4283 NOT_IMPLEMENTED;
4284 #endif
4287 DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)sp->il_offset));
4290 static void
4291 remove_breakpoint (BreakpointInstance *inst)
4293 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
4294 int count;
4295 MonoJitInfo *ji = inst->ji;
4296 guint8 *ip = inst->ip;
4298 mono_loader_lock ();
4299 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, ip));
4300 g_hash_table_insert (bp_locs, ip, GINT_TO_POINTER (count - 1));
4301 mono_loader_unlock ();
4303 g_assert (count > 0);
4305 if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
4306 mono_arch_clear_breakpoint (ji, ip);
4308 #else
4309 NOT_IMPLEMENTED;
4310 #endif
4313 static inline gboolean
4314 bp_matches_method (MonoBreakpoint *bp, MonoMethod *method)
4316 int i;
4318 if (!bp->method)
4319 return TRUE;
4320 if (method == bp->method)
4321 return TRUE;
4322 if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method)
4323 return TRUE;
4325 if (bp->method->is_inflated && method->is_inflated) {
4326 MonoMethodInflated *bpimethod = (MonoMethodInflated*)bp->method;
4327 MonoMethodInflated *imethod = (MonoMethodInflated*)method;
4329 /* Open generic methods should match closed generic methods of the same class */
4330 if (bpimethod->declaring == imethod->declaring && bpimethod->context.class_inst == imethod->context.class_inst && bpimethod->context.method_inst && bpimethod->context.method_inst->is_open) {
4331 for (i = 0; i < bpimethod->context.method_inst->type_argc; ++i) {
4332 MonoType *t1 = bpimethod->context.method_inst->type_argv [i];
4334 /* FIXME: Handle !mvar */
4335 if (t1->type != MONO_TYPE_MVAR)
4336 return FALSE;
4338 return TRUE;
4342 return FALSE;
4346 * add_pending_breakpoints:
4348 * Insert pending breakpoints into the newly JITted method METHOD.
4350 static void
4351 add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
4353 int i, j;
4354 MonoSeqPointInfo *seq_points;
4355 MonoDomain *domain;
4356 MonoMethod *jmethod;
4358 if (!breakpoints)
4359 return;
4361 domain = mono_domain_get ();
4363 mono_loader_lock ();
4365 for (i = 0; i < breakpoints->len; ++i) {
4366 MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
4367 gboolean found = FALSE;
4369 if (!bp_matches_method (bp, method))
4370 continue;
4372 for (j = 0; j < bp->children->len; ++j) {
4373 BreakpointInstance *inst = g_ptr_array_index (bp->children, j);
4375 if (inst->ji == ji)
4376 found = TRUE;
4379 if (!found) {
4380 jmethod = jinfo_get_method (ji);
4381 mono_domain_lock (domain);
4382 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, jmethod);
4383 if (!seq_points && jmethod->is_inflated)
4384 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (jmethod));
4385 mono_domain_unlock (domain);
4386 if (!seq_points)
4387 /* Could be AOT code */
4388 continue;
4389 g_assert (seq_points);
4391 insert_breakpoint (seq_points, domain, ji, bp, NULL);
4395 mono_loader_unlock ();
4398 static void
4399 set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp, MonoError *error)
4401 gpointer code;
4402 MonoJitInfo *ji;
4404 if (error)
4405 mono_error_init (error);
4407 code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
4408 if (!code) {
4409 /* Might be AOTed code */
4410 code = mono_aot_get_method (domain, method);
4411 g_assert (code);
4412 ji = mono_jit_info_table_find (domain, code);
4413 g_assert (ji);
4415 g_assert (code);
4417 insert_breakpoint (seq_points, domain, ji, bp, error);
4420 static void
4421 clear_breakpoint (MonoBreakpoint *bp);
4424 * set_breakpoint:
4426 * Set a breakpoint at IL_OFFSET in METHOD.
4427 * METHOD can be NULL, in which case a breakpoint is placed in all methods.
4428 * METHOD can also be a generic method definition, in which case a breakpoint
4429 * is placed in all instances of the method.
4430 * If ERROR is non-NULL, then it is set and NULL is returnd if some breakpoints couldn't be
4431 * inserted.
4433 static MonoBreakpoint*
4434 set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError *error)
4436 MonoBreakpoint *bp;
4437 GHashTableIter iter, iter2;
4438 MonoDomain *domain;
4439 MonoMethod *m;
4440 MonoSeqPointInfo *seq_points;
4442 if (error)
4443 mono_error_init (error);
4445 // FIXME:
4446 // - suspend/resume the vm to prevent code patching problems
4447 // - multiple breakpoints on the same location
4448 // - dynamic methods
4449 // - races
4451 bp = g_new0 (MonoBreakpoint, 1);
4452 bp->method = method;
4453 bp->il_offset = il_offset;
4454 bp->req = req;
4455 bp->children = g_ptr_array_new ();
4457 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));
4459 mono_loader_lock ();
4461 g_hash_table_iter_init (&iter, domains);
4462 while (g_hash_table_iter_next (&iter, (void**)&domain, NULL)) {
4463 mono_domain_lock (domain);
4465 g_hash_table_iter_init (&iter2, domain_jit_info (domain)->seq_points);
4466 while (g_hash_table_iter_next (&iter2, (void**)&m, (void**)&seq_points)) {
4467 if (bp_matches_method (bp, m))
4468 set_bp_in_method (domain, m, seq_points, bp, error);
4471 mono_domain_unlock (domain);
4474 mono_loader_unlock ();
4476 mono_loader_lock ();
4477 g_ptr_array_add (breakpoints, bp);
4478 mono_loader_unlock ();
4480 if (error && !mono_error_ok (error)) {
4481 clear_breakpoint (bp);
4482 return NULL;
4485 return bp;
4488 static void
4489 clear_breakpoint (MonoBreakpoint *bp)
4491 int i;
4493 // FIXME: locking, races
4494 for (i = 0; i < bp->children->len; ++i) {
4495 BreakpointInstance *inst = g_ptr_array_index (bp->children, i);
4497 remove_breakpoint (inst);
4499 g_free (inst);
4502 mono_loader_lock ();
4503 g_ptr_array_remove (breakpoints, bp);
4504 mono_loader_unlock ();
4506 g_ptr_array_free (bp->children, TRUE);
4507 g_free (bp);
4510 static void
4511 breakpoints_cleanup (void)
4513 int i;
4515 mono_loader_lock ();
4516 i = 0;
4517 while (i < event_requests->len) {
4518 EventRequest *req = g_ptr_array_index (event_requests, i);
4520 if (req->event_kind == EVENT_KIND_BREAKPOINT) {
4521 clear_breakpoint (req->info);
4522 g_ptr_array_remove_index_fast (event_requests, i);
4523 g_free (req);
4524 } else {
4525 i ++;
4529 for (i = 0; i < breakpoints->len; ++i)
4530 g_free (g_ptr_array_index (breakpoints, i));
4532 g_ptr_array_free (breakpoints, TRUE);
4533 g_hash_table_destroy (bp_locs);
4535 breakpoints = NULL;
4536 bp_locs = NULL;
4538 mono_loader_unlock ();
4542 * clear_breakpoints_for_domain:
4544 * Clear breakpoint instances which reference DOMAIN.
4546 static void
4547 clear_breakpoints_for_domain (MonoDomain *domain)
4549 int i, j;
4551 /* This could be called after shutdown */
4552 if (!breakpoints)
4553 return;
4555 mono_loader_lock ();
4556 for (i = 0; i < breakpoints->len; ++i) {
4557 MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
4559 j = 0;
4560 while (j < bp->children->len) {
4561 BreakpointInstance *inst = g_ptr_array_index (bp->children, j);
4563 if (inst->domain == domain) {
4564 remove_breakpoint (inst);
4566 g_free (inst);
4568 g_ptr_array_remove_index_fast (bp->children, j);
4569 } else {
4570 j ++;
4574 mono_loader_unlock ();
4578 * ss_update:
4580 * Return FALSE if single stepping needs to continue.
4582 static gboolean
4583 ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx)
4585 MonoDebugMethodInfo *minfo;
4586 MonoDebugSourceLocation *loc = NULL;
4587 gboolean hit = TRUE;
4588 MonoMethod *method;
4590 if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) {
4592 * These seq points are inserted by the JIT after calls, step over needs to skip them.
4594 DEBUG (1, fprintf (log_file, "[%p] Seq point at nonempty stack %x while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset));
4595 return FALSE;
4598 if (req->depth == STEP_DEPTH_OVER && hit) {
4599 if (!tls->context.valid)
4600 mono_thread_state_init_from_monoctx (&tls->context, ctx);
4601 compute_frame_info (tls->thread, tls);
4602 if (req->nframes && tls->frame_count && tls->frame_count > req->nframes) {
4603 /* Hit the breakpoint in a recursive call */
4604 DEBUG (1, fprintf (log_file, "[%p] Breakpoint at lower frame while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId ()));
4605 return FALSE;
4609 if (req->size != STEP_SIZE_LINE)
4610 return TRUE;
4612 /* Have to check whenever a different source line was reached */
4613 method = jinfo_get_method (ji);
4614 minfo = mono_debug_lookup_method (method);
4616 if (minfo)
4617 loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
4619 if (!loc || (loc && method == ss_req->last_method && loc->row == ss_req->last_line)) {
4620 /* Have to continue single stepping */
4621 if (!loc)
4622 DEBUG(1, fprintf (log_file, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset));
4623 else
4624 DEBUG(1, fprintf (log_file, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer)GetCurrentThreadId (), loc->row));
4625 hit = FALSE;
4628 if (loc) {
4629 ss_req->last_method = method;
4630 ss_req->last_line = loc->row;
4631 mono_debug_free_source_location (loc);
4634 return hit;
4637 static gboolean
4638 breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
4640 return bp->method && bp->method->klass->image->assembly == assembly;
4643 static void
4644 process_breakpoint_inner (DebuggerTlsData *tls)
4646 MonoJitInfo *ji;
4647 guint8 *ip;
4648 int i, j, suspend_policy;
4649 guint32 native_offset;
4650 MonoBreakpoint *bp;
4651 BreakpointInstance *inst;
4652 GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs;
4653 GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL;
4654 EventKind kind = EVENT_KIND_BREAKPOINT;
4655 MonoContext *ctx = &tls->restore_ctx;
4656 MonoMethod *method;
4657 MonoSeqPointInfo *info;
4658 SeqPoint *sp;
4660 // FIXME: Speed this up
4662 ip = MONO_CONTEXT_GET_IP (ctx);
4663 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
4664 g_assert (ji);
4665 method = jinfo_get_method (ji);
4667 /* Compute the native offset of the breakpoint from the ip */
4668 native_offset = ip - (guint8*)ji->code_start;
4671 * Skip the instruction causing the breakpoint signal.
4673 mono_arch_skip_breakpoint (ctx, ji);
4675 if (method->wrapper_type || tls->disable_breakpoints)
4676 return;
4678 bp_reqs = g_ptr_array_new ();
4679 ss_reqs = g_ptr_array_new ();
4680 ss_reqs_orig = g_ptr_array_new ();
4682 mono_loader_lock ();
4685 * The ip points to the instruction causing the breakpoint event, which is after
4686 * the offset recorded in the seq point map, so find the prev seq point before ip.
4688 sp = find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info);
4689 if (!sp)
4690 no_seq_points_found (method);
4691 g_assert (sp);
4693 DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x, sp il offset=0x%x.\n", (gpointer)GetCurrentThreadId (), method->name, ip, native_offset, sp ? sp->il_offset : -1));
4695 bp = NULL;
4696 for (i = 0; i < breakpoints->len; ++i) {
4697 bp = g_ptr_array_index (breakpoints, i);
4699 if (!bp->method)
4700 continue;
4702 for (j = 0; j < bp->children->len; ++j) {
4703 inst = g_ptr_array_index (bp->children, j);
4704 if (inst->ji == ji && inst->sp == sp) {
4705 if (bp->req->event_kind == EVENT_KIND_STEP) {
4706 g_ptr_array_add (ss_reqs_orig, bp->req);
4707 } else {
4708 g_ptr_array_add (bp_reqs, bp->req);
4713 if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) {
4714 /* Maybe a method entry/exit event */
4715 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET)
4716 kind = EVENT_KIND_METHOD_ENTRY;
4717 else if (sp->il_offset == METHOD_EXIT_IL_OFFSET)
4718 kind = EVENT_KIND_METHOD_EXIT;
4721 /* Process single step requests */
4722 for (i = 0; i < ss_reqs_orig->len; ++i) {
4723 EventRequest *req = g_ptr_array_index (ss_reqs_orig, i);
4724 SingleStepReq *ss_req = req->info;
4725 gboolean hit;
4727 if (mono_thread_internal_current () != ss_req->thread)
4728 continue;
4730 hit = ss_update (ss_req, ji, sp, tls, ctx);
4731 if (hit)
4732 g_ptr_array_add (ss_reqs, req);
4734 /* Start single stepping again from the current sequence point */
4735 ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
4738 if (ss_reqs->len > 0)
4739 ss_events = create_event_list (EVENT_KIND_STEP, ss_reqs, ji, NULL, &suspend_policy);
4740 if (bp_reqs->len > 0)
4741 bp_events = create_event_list (EVENT_KIND_BREAKPOINT, bp_reqs, ji, NULL, &suspend_policy);
4742 if (kind != EVENT_KIND_BREAKPOINT)
4743 enter_leave_events = create_event_list (kind, NULL, ji, NULL, &suspend_policy);
4745 mono_loader_unlock ();
4747 g_ptr_array_free (bp_reqs, TRUE);
4748 g_ptr_array_free (ss_reqs, TRUE);
4751 * FIXME: The first event will suspend, so the second will only be sent after the
4752 * resume.
4754 if (ss_events)
4755 process_event (EVENT_KIND_STEP, method, 0, ctx, ss_events, suspend_policy);
4756 if (bp_events)
4757 process_event (kind, method, 0, ctx, bp_events, suspend_policy);
4758 if (enter_leave_events)
4759 process_event (kind, method, 0, ctx, enter_leave_events, suspend_policy);
4762 /* Process a breakpoint/single step event after resuming from a signal handler */
4763 static void
4764 process_signal_event (void (*func) (DebuggerTlsData*))
4766 DebuggerTlsData *tls;
4767 MonoContext orig_restore_ctx, ctx;
4769 tls = mono_native_tls_get_value (debugger_tls_id);
4770 /* Have to save/restore the restore_ctx as we can be called recursively during invokes etc. */
4771 memcpy (&orig_restore_ctx, &tls->restore_ctx, sizeof (MonoContext));
4772 memcpy (&tls->restore_ctx, &tls->handler_ctx, sizeof (MonoContext));
4774 func (tls);
4776 /* This is called when resuming from a signal handler, so it shouldn't return */
4777 memcpy (&ctx, &tls->restore_ctx, sizeof (MonoContext));
4778 memcpy (&tls->restore_ctx, &orig_restore_ctx, sizeof (MonoContext));
4779 mono_restore_context (&ctx);
4780 g_assert_not_reached ();
4783 static void
4784 process_breakpoint (void)
4786 process_signal_event (process_breakpoint_inner);
4789 static void
4790 resume_from_signal_handler (void *sigctx, void *func)
4792 DebuggerTlsData *tls;
4793 MonoContext ctx;
4795 /* Save the original context in TLS */
4796 // FIXME: This might not work on an altstack ?
4797 tls = mono_native_tls_get_value (debugger_tls_id);
4798 if (!tls)
4799 fprintf (stderr, "Thread %p is not attached to the JIT.\n", (gpointer)GetCurrentThreadId ());
4800 g_assert (tls);
4802 // FIXME: MonoContext usually doesn't include the fp registers, so these are
4803 // clobbered by a single step/breakpoint event. If this turns out to be a problem,
4804 // clob:c could be added to op_seq_point.
4806 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
4807 memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
4808 #ifdef MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX
4809 mono_arch_setup_resume_sighandler_ctx (&ctx, func);
4810 #else
4811 MONO_CONTEXT_SET_IP (&ctx, func);
4812 #endif
4813 mono_arch_monoctx_to_sigctx (&ctx, sigctx);
4815 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
4816 mono_ppc_set_func_into_sigctx (sigctx, func);
4817 #endif
4820 void
4821 mono_debugger_agent_breakpoint_hit (void *sigctx)
4824 * We are called from a signal handler, and running code there causes all kinds of
4825 * problems, like the original signal is disabled, libgc can't handle altstack, etc.
4826 * So set up the signal context to return to the real breakpoint handler function.
4828 resume_from_signal_handler (sigctx, process_breakpoint);
4831 static gboolean
4832 user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
4834 if (frame->managed) {
4835 *(MonoContext*)data = *ctx;
4837 return TRUE;
4838 } else {
4839 return FALSE;
4844 * Called by System.Diagnostics.Debugger:Break ().
4846 void
4847 mono_debugger_agent_user_break (void)
4849 if (agent_config.enabled) {
4850 MonoContext ctx;
4851 int suspend_policy;
4852 GSList *events;
4854 /* Obtain a context */
4855 MONO_CONTEXT_SET_IP (&ctx, NULL);
4856 mono_walk_stack_with_ctx (user_break_cb, NULL, 0, &ctx);
4857 g_assert (MONO_CONTEXT_GET_IP (&ctx) != NULL);
4859 mono_loader_lock ();
4860 events = create_event_list (EVENT_KIND_USER_BREAK, NULL, NULL, NULL, &suspend_policy);
4861 mono_loader_unlock ();
4863 process_event (EVENT_KIND_USER_BREAK, NULL, 0, &ctx, events, suspend_policy);
4864 } else {
4865 G_BREAKPOINT ();
4869 static const char*
4870 ss_depth_to_string (StepDepth depth)
4872 switch (depth) {
4873 case STEP_DEPTH_OVER:
4874 return "over";
4875 case STEP_DEPTH_OUT:
4876 return "out";
4877 case STEP_DEPTH_INTO:
4878 return "into";
4879 default:
4880 g_assert_not_reached ();
4881 return NULL;
4885 static void
4886 process_single_step_inner (DebuggerTlsData *tls)
4888 MonoJitInfo *ji;
4889 guint8 *ip;
4890 GPtrArray *reqs;
4891 int il_offset, suspend_policy;
4892 MonoDomain *domain;
4893 GSList *events;
4894 MonoContext *ctx = &tls->restore_ctx;
4895 MonoMethod *method;
4896 SeqPoint *sp;
4897 MonoSeqPointInfo *info;
4899 ip = MONO_CONTEXT_GET_IP (ctx);
4901 /* Skip the instruction causing the single step */
4902 mono_arch_skip_single_step (ctx);
4904 if (suspend_count > 0) {
4905 process_suspend (tls, ctx);
4906 return;
4909 if (!ss_req)
4910 // FIXME: A suspend race
4911 return;
4913 if (mono_thread_internal_current () != ss_req->thread)
4914 return;
4916 if (log_level > 0) {
4917 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
4919 DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_depth_to_string (ss_req->depth), mono_method_full_name (jinfo_get_method (ji), TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
4922 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
4923 g_assert (ji);
4924 method = jinfo_get_method (ji);
4925 g_assert (method);
4927 if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
4928 return;
4931 * FIXME:
4932 * Stopping in memset makes half-initialized vtypes visible.
4933 * Stopping in memcpy makes half-copied vtypes visible.
4935 if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
4936 return;
4939 * The ip points to the instruction causing the single step event, which is before
4940 * the offset recorded in the seq point map, so find the next seq point after ip.
4942 sp = find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info);
4943 if (!sp)
4944 return;
4945 il_offset = sp->il_offset;
4947 if (!ss_update (ss_req, ji, sp, tls, ctx))
4948 return;
4950 /* Start single stepping again from the current sequence point */
4951 ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
4953 if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) &&
4954 (method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
4955 !strcmp (method->name, ".cctor"))
4956 return;
4958 // FIXME: Has to lock earlier
4960 reqs = g_ptr_array_new ();
4962 mono_loader_lock ();
4964 g_ptr_array_add (reqs, ss_req->req);
4966 events = create_event_list (EVENT_KIND_STEP, reqs, ji, NULL, &suspend_policy);
4968 g_ptr_array_free (reqs, TRUE);
4970 mono_loader_unlock ();
4972 process_event (EVENT_KIND_STEP, jinfo_get_method (ji), il_offset, ctx, events, suspend_policy);
4975 static void
4976 process_single_step (void)
4978 process_signal_event (process_single_step_inner);
4982 * mono_debugger_agent_single_step_event:
4984 * Called from a signal handler to handle a single step event.
4986 void
4987 mono_debugger_agent_single_step_event (void *sigctx)
4989 /* Resume to process_single_step through the signal context */
4991 // FIXME: Since step out/over is implemented using step in, the step in case should
4992 // be as fast as possible. Move the relevant code from process_single_step_inner ()
4993 // here
4995 if (GetCurrentThreadId () == debugger_thread_id) {
4997 * This could happen despite our best effors when the runtime calls
4998 * assembly/type resolve hooks.
4999 * FIXME: Breakpoints too.
5001 MonoContext ctx;
5003 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
5004 mono_arch_skip_single_step (&ctx);
5005 mono_arch_monoctx_to_sigctx (&ctx, sigctx);
5006 return;
5009 resume_from_signal_handler (sigctx, process_single_step);
5012 void
5013 debugger_agent_single_step_from_context (MonoContext *ctx)
5015 DebuggerTlsData *tls;
5016 MonoContext orig_restore_ctx;
5018 tls = mono_native_tls_get_value (debugger_tls_id);
5019 g_assert (tls);
5021 /* Have to save/restore the restore_ctx as we can be called recursively during invokes etc. */
5022 memcpy (&orig_restore_ctx, &tls->restore_ctx, sizeof (MonoContext));
5023 memcpy (&tls->restore_ctx, ctx, sizeof (MonoContext));
5025 process_single_step_inner (tls);
5027 memcpy (ctx, &tls->restore_ctx, sizeof (MonoContext));
5028 memcpy (&tls->restore_ctx, &orig_restore_ctx, sizeof (MonoContext));
5031 void
5032 debugger_agent_breakpoint_from_context (MonoContext *ctx)
5034 DebuggerTlsData *tls;
5035 MonoContext orig_restore_ctx;
5037 tls = mono_native_tls_get_value (debugger_tls_id);
5038 g_assert (tls);
5039 memcpy (&orig_restore_ctx, &tls->restore_ctx, sizeof (MonoContext));
5040 memcpy (&tls->restore_ctx, ctx, sizeof (MonoContext));
5042 process_breakpoint_inner (tls);
5044 memcpy (ctx, &tls->restore_ctx, sizeof (MonoContext));
5045 memcpy (&tls->restore_ctx, &orig_restore_ctx, sizeof (MonoContext));
5049 * start_single_stepping:
5051 * Turn on single stepping. Can be called multiple times, for example,
5052 * by a single step event request + a suspend.
5054 static void
5055 start_single_stepping (void)
5057 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
5058 int val = InterlockedIncrement (&ss_count);
5060 if (val == 1)
5061 mono_arch_start_single_stepping ();
5063 if (ss_req != NULL && ss_invoke_addr == NULL) {
5064 DebuggerTlsData *tls;
5066 mono_loader_lock ();
5068 tls = mono_g_hash_table_lookup (thread_to_tls, ss_req->thread);
5069 ss_invoke_addr = tls->invoke_addr;
5071 mono_loader_unlock ();
5073 #else
5074 g_assert_not_reached ();
5075 #endif
5078 static void
5079 stop_single_stepping (void)
5081 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
5082 int val = InterlockedDecrement (&ss_count);
5084 if (val == 0)
5085 mono_arch_stop_single_stepping ();
5086 if (ss_req != NULL)
5087 ss_invoke_addr = NULL;
5088 #else
5089 g_assert_not_reached ();
5090 #endif
5094 * ss_stop:
5096 * Stop the single stepping operation given by SS_REQ.
5098 static void
5099 ss_stop (SingleStepReq *ss_req)
5101 if (ss_req->bps) {
5102 GSList *l;
5104 for (l = ss_req->bps; l; l = l->next) {
5105 clear_breakpoint (l->data);
5107 g_slist_free (ss_req->bps);
5108 ss_req->bps = NULL;
5111 if (ss_req->global) {
5112 stop_single_stepping ();
5113 ss_req->global = FALSE;
5118 * ss_start:
5120 * Start the single stepping operation given by SS_REQ from the sequence point SP.
5121 * If CTX is not set, then this can target any thread. If CTX is set, then TLS should
5122 * belong to the same thread as CTX.
5124 static void
5125 ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch)
5127 int i, j, frame_index;
5128 SeqPoint *next_sp;
5129 MonoBreakpoint *bp;
5130 gboolean enable_global = FALSE;
5132 /* Stop the previous operation */
5133 ss_stop (ss_req);
5136 * Implement single stepping using breakpoints if possible.
5138 if (step_to_catch) {
5139 bp = set_breakpoint (method, sp->il_offset, ss_req->req, NULL);
5140 ss_req->bps = g_slist_append (ss_req->bps, bp);
5141 } else {
5142 frame_index = 1;
5144 if ((!sp || sp->next_len == 0 || ss_req->depth == STEP_DEPTH_OUT || ss_req->depth == STEP_DEPTH_OVER) && ctx) {
5145 /* Need parent frames */
5146 if (!tls->context.valid)
5147 mono_thread_state_init_from_monoctx (&tls->context, ctx);
5148 compute_frame_info (tls->thread, tls);
5152 * Find the first sequence point in the current or in a previous frame which
5153 * is not the last in its method.
5155 if (ss_req->depth == STEP_DEPTH_OUT) {
5156 /* Ignore seq points in current method */
5157 while (frame_index < tls->frame_count) {
5158 StackFrame *frame = tls->frames [frame_index];
5160 method = frame->method;
5161 sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
5162 frame_index ++;
5163 if (sp && sp->next_len != 0)
5164 break;
5166 // There could be method calls before the next seq point in the caller when using nested calls
5167 //enable_global = TRUE;
5168 } else {
5169 if (sp && sp->next_len == 0) {
5170 sp = NULL;
5171 while (frame_index < tls->frame_count) {
5172 StackFrame *frame = tls->frames [frame_index];
5174 method = frame->method;
5175 sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
5176 if (sp && sp->next_len != 0)
5177 break;
5178 sp = NULL;
5179 frame_index ++;
5184 if (sp && sp->next_len > 0) {
5185 for (i = 0; i < sp->next_len; ++i) {
5186 next_sp = &info->seq_points [sp->next [i]];
5188 bp = set_breakpoint (method, next_sp->il_offset, ss_req->req, NULL);
5189 ss_req->bps = g_slist_append (ss_req->bps, bp);
5193 if (ss_req->depth == STEP_DEPTH_OVER) {
5194 if (ss_req->nframes == 0)
5195 ss_req->nframes = tls->frame_count;
5196 /* Need to stop in catch clauses as well */
5197 for (i = 0; i < tls->frame_count; ++i) {
5198 StackFrame *frame = tls->frames [i];
5200 if (frame->ji) {
5201 MonoJitInfo *jinfo = frame->ji;
5202 for (j = 0; j < jinfo->num_clauses; ++j) {
5203 MonoJitExceptionInfo *ei = &jinfo->clauses [j];
5205 sp = find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL);
5206 if (sp) {
5207 bp = set_breakpoint (frame->method, sp->il_offset, ss_req->req, NULL);
5208 ss_req->bps = g_slist_append (ss_req->bps, bp);
5216 if (ss_req->depth == STEP_DEPTH_INTO) {
5217 /* Enable global stepping so we stop at method entry too */
5218 enable_global = TRUE;
5222 * The ctx/frame info computed above will become invalid when we continue.
5224 tls->context.valid = FALSE;
5225 tls->async_state.valid = FALSE;
5226 invalidate_frames (tls);
5229 if (enable_global) {
5230 DEBUG (1, fprintf (log_file, "[dbg] Turning on global single stepping.\n"));
5231 ss_req->global = TRUE;
5232 start_single_stepping ();
5233 } else if (!ss_req->bps) {
5234 DEBUG (1, fprintf (log_file, "[dbg] Turning on global single stepping.\n"));
5235 ss_req->global = TRUE;
5236 start_single_stepping ();
5237 } else {
5238 ss_req->global = FALSE;
5243 * Start single stepping of thread THREAD
5245 static ErrorCode
5246 ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
5248 DebuggerTlsData *tls;
5249 MonoSeqPointInfo *info = NULL;
5250 SeqPoint *sp = NULL;
5251 MonoMethod *method = NULL;
5252 MonoDebugMethodInfo *minfo;
5253 gboolean step_to_catch = FALSE;
5255 if (suspend_count == 0)
5256 return ERR_NOT_SUSPENDED;
5258 wait_for_suspend ();
5260 // FIXME: Multiple requests
5261 if (ss_req) {
5262 DEBUG (0, fprintf (log_file, "Received a single step request while the previous one was still active.\n"));
5263 return ERR_NOT_IMPLEMENTED;
5266 DEBUG (1, fprintf (log_file, "[dbg] Starting single step of thread %p (depth=%s).\n", thread, ss_depth_to_string (depth)));
5268 ss_req = g_new0 (SingleStepReq, 1);
5269 ss_req->req = req;
5270 ss_req->thread = thread;
5271 ss_req->size = size;
5272 ss_req->depth = depth;
5273 req->info = ss_req;
5275 mono_loader_lock ();
5276 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
5277 mono_loader_unlock ();
5278 g_assert (tls);
5279 g_assert (tls->context.valid);
5280 ss_req->start_sp = ss_req->last_sp = MONO_CONTEXT_GET_SP (&tls->context.ctx);
5282 if (tls->catch_state.valid) {
5283 gboolean res;
5284 StackFrameInfo frame;
5285 MonoContext new_ctx;
5286 MonoLMF *lmf = NULL;
5289 * We are stopped at a throw site. Stepping should go to the catch site.
5292 /* Find the the jit info for the catch context */
5293 res = mono_find_jit_info_ext (tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], thread->jit_data, NULL, &tls->catch_state.ctx, &new_ctx, NULL, &lmf, NULL, &frame);
5294 g_assert (res);
5295 g_assert (frame.type == FRAME_TYPE_MANAGED);
5298 * Find the seq point corresponding to the landing site ip, which is the first seq
5299 * point after ip.
5301 sp = find_next_seq_point_for_native_offset (frame.domain, frame.method, frame.native_offset, &info);
5302 if (!sp)
5303 no_seq_points_found (frame.method);
5304 g_assert (sp);
5306 method = frame.method;
5308 step_to_catch = TRUE;
5309 /* This make sure the seq point is not skipped by process_single_step () */
5310 ss_req->last_sp = NULL;
5313 if (!step_to_catch && ss_req->size == STEP_SIZE_LINE) {
5314 StackFrame *frame;
5316 /* Compute the initial line info */
5317 compute_frame_info (thread, tls);
5319 if (tls->frame_count) {
5320 frame = tls->frames [0];
5322 ss_req->last_method = frame->method;
5323 ss_req->last_line = -1;
5325 minfo = mono_debug_lookup_method (frame->method);
5326 if (minfo && frame->il_offset != -1) {
5327 MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, frame->il_offset);
5329 if (loc) {
5330 ss_req->last_line = loc->row;
5331 g_free (loc);
5337 if (!step_to_catch) {
5338 StackFrame *frame;
5340 compute_frame_info (thread, tls);
5342 if (tls->frame_count) {
5343 frame = tls->frames [0];
5345 if (!method && frame->il_offset != -1) {
5346 /* FIXME: Sort the table and use a binary search */
5347 sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
5348 if (!sp)
5349 no_seq_points_found (frame->method);
5350 g_assert (sp);
5351 method = frame->method;
5356 ss_start (ss_req, method, sp, info, &tls->context.ctx, tls, step_to_catch);
5358 return 0;
5361 static void
5362 ss_destroy (SingleStepReq *req)
5364 // FIXME: Locking
5365 g_assert (ss_req == req);
5367 ss_stop (ss_req);
5369 g_free (ss_req);
5370 ss_req = NULL;
5374 * Called from metadata by the icall for System.Diagnostics.Debugger:Log ().
5376 void
5377 mono_debugger_agent_debug_log (int level, MonoString *category, MonoString *message)
5379 int suspend_policy;
5380 GSList *events;
5381 EventInfo ei;
5383 if (!agent_config.enabled)
5384 return;
5386 mono_loader_lock ();
5387 events = create_event_list (EVENT_KIND_USER_LOG, NULL, NULL, NULL, &suspend_policy);
5388 mono_loader_unlock ();
5390 ei.level = level;
5391 ei.category = category ? mono_string_to_utf8 (category) : NULL;
5392 ei.message = message ? mono_string_to_utf8 (message) : NULL;
5394 process_event (EVENT_KIND_USER_LOG, &ei, 0, NULL, events, suspend_policy);
5396 g_free (ei.category);
5397 g_free (ei.message);
5400 gboolean
5401 mono_debugger_agent_debug_log_is_enabled (void)
5403 /* Treat this as true even if there is no event request for EVENT_KIND_USER_LOG */
5404 return agent_config.enabled;
5407 #if defined(PLATFORM_ANDROID) || defined(TARGET_ANDROID)
5408 void
5409 mono_debugger_agent_unhandled_exception (MonoException *exc)
5411 int suspend_policy;
5412 GSList *events;
5413 EventInfo ei;
5415 if (!inited)
5416 return;
5418 memset (&ei, 0, sizeof (EventInfo));
5419 ei.exc = (MonoObject*)exc;
5421 mono_loader_lock ();
5422 events = create_event_list (EVENT_KIND_EXCEPTION, NULL, NULL, &ei, &suspend_policy);
5423 mono_loader_unlock ();
5425 process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy);
5427 #endif
5429 void
5430 mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx,
5431 MonoContext *catch_ctx)
5433 int i, j, suspend_policy;
5434 GSList *events;
5435 MonoJitInfo *ji, *catch_ji;
5436 EventInfo ei;
5437 DebuggerTlsData *tls = NULL;
5439 if (thread_to_tls != NULL) {
5440 MonoInternalThread *thread = mono_thread_internal_current ();
5442 mono_loader_lock ();
5443 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
5444 mono_loader_unlock ();
5446 if (tls && tls->abort_requested)
5447 return;
5448 if (tls && tls->disable_breakpoints)
5449 return;
5452 memset (&ei, 0, sizeof (EventInfo));
5454 /* Just-In-Time debugging */
5455 if (!catch_ctx) {
5456 if (agent_config.onuncaught && !inited) {
5457 finish_agent_init (FALSE);
5460 * Send an unsolicited EXCEPTION event with a dummy request id.
5462 events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
5463 ei.exc = (MonoObject*)exc;
5464 process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
5465 return;
5467 } else if (agent_config.onthrow && !inited) {
5468 GSList *l;
5469 gboolean found = FALSE;
5471 for (l = agent_config.onthrow; l; l = l->next) {
5472 char *ex_type = l->data;
5473 char *f = mono_type_full_name (&exc->object.vtable->klass->byval_arg);
5475 if (!strcmp (ex_type, "") || !strcmp (ex_type, f))
5476 found = TRUE;
5478 g_free (f);
5481 if (found) {
5482 finish_agent_init (FALSE);
5485 * Send an unsolicited EXCEPTION event with a dummy request id.
5487 events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
5488 ei.exc = (MonoObject*)exc;
5489 process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
5490 return;
5494 if (!inited)
5495 return;
5497 ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL);
5498 if (catch_ctx)
5499 catch_ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (catch_ctx), NULL);
5500 else
5501 catch_ji = NULL;
5503 ei.exc = (MonoObject*)exc;
5504 ei.caught = catch_ctx != NULL;
5506 mono_loader_lock ();
5508 /* Treat exceptions which are caught in non-user code as unhandled */
5509 for (i = 0; i < event_requests->len; ++i) {
5510 EventRequest *req = g_ptr_array_index (event_requests, i);
5511 if (req->event_kind != EVENT_KIND_EXCEPTION)
5512 continue;
5514 for (j = 0; j < req->nmodifiers; ++j) {
5515 Modifier *mod = &req->modifiers [j];
5517 if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && catch_ji) {
5518 int k;
5519 gboolean found = FALSE;
5520 MonoAssembly **assemblies = mod->data.assemblies;
5522 if (assemblies) {
5523 for (k = 0; assemblies [k]; ++k)
5524 if (assemblies [k] == jinfo_get_method (catch_ji)->klass->image->assembly)
5525 found = TRUE;
5527 if (!found)
5528 ei.caught = FALSE;
5533 events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy);
5534 mono_loader_unlock ();
5536 if (tls && ei.caught && catch_ctx) {
5537 memset (&tls->catch_state, 0, sizeof (tls->catch_state));
5538 tls->catch_state.ctx = *catch_ctx;
5539 tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
5540 tls->catch_state.valid = TRUE;
5543 process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy);
5545 if (tls)
5546 tls->catch_state.valid = FALSE;
5549 void
5550 mono_debugger_agent_begin_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
5552 DebuggerTlsData *tls;
5554 if (!inited)
5555 return;
5557 tls = mono_native_tls_get_value (debugger_tls_id);
5558 if (!tls)
5559 return;
5562 * We're about to invoke an exception filter during the first pass of exception handling.
5564 * 'ctx' is the context that'll get passed to the filter ('call_filter (ctx, ei->data.filter)'),
5565 * 'orig_ctx' is the context where the exception has been thrown.
5568 * See mcs/class/Mono.Debugger.Soft/Tests/dtest-excfilter.il for an example.
5570 * If we're stopped in Filter(), normal stack unwinding would first unwind to
5571 * the call site (line 37) and then continue to Main(), but it would never
5572 * include the throw site (line 32).
5574 * Since exception filters are invoked during the first pass of exception handling,
5575 * the stack frames of the throw site are still intact, so we should include them
5576 * in a stack trace.
5578 * We do this here by saving the context of the throw site in 'tls->filter_state'.
5580 * Exception filters are used by MonoDroid, where we want to stop inside a call filter,
5581 * but report the location of the 'throw' to the user.
5585 g_assert (mono_thread_state_init_from_monoctx (&tls->filter_state, orig_ctx));
5588 void
5589 mono_debugger_agent_end_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
5591 DebuggerTlsData *tls;
5593 if (!inited)
5594 return;
5596 tls = mono_native_tls_get_value (debugger_tls_id);
5597 if (!tls)
5598 return;
5600 tls->filter_state.valid = FALSE;
5604 * buffer_add_value_full:
5606 * Add the encoding of the value at ADDR described by T to the buffer.
5607 * AS_VTYPE determines whenever to treat primitive types as primitive types or
5608 * vtypes.
5610 static void
5611 buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
5612 gboolean as_vtype, GHashTable *parent_vtypes)
5614 MonoObject *obj;
5615 gboolean boxed_vtype = FALSE;
5617 if (t->byref) {
5618 if (!(*(void**)addr)) {
5619 /* This can happen with compiler generated locals */
5620 //printf ("%s\n", mono_type_full_name (t));
5621 buffer_add_byte (buf, VALUE_TYPE_ID_NULL);
5622 return;
5624 g_assert (*(void**)addr);
5625 addr = *(void**)addr;
5628 if (as_vtype) {
5629 switch (t->type) {
5630 case MONO_TYPE_BOOLEAN:
5631 case MONO_TYPE_I1:
5632 case MONO_TYPE_U1:
5633 case MONO_TYPE_CHAR:
5634 case MONO_TYPE_I2:
5635 case MONO_TYPE_U2:
5636 case MONO_TYPE_I4:
5637 case MONO_TYPE_U4:
5638 case MONO_TYPE_R4:
5639 case MONO_TYPE_I8:
5640 case MONO_TYPE_U8:
5641 case MONO_TYPE_R8:
5642 case MONO_TYPE_I:
5643 case MONO_TYPE_U:
5644 case MONO_TYPE_PTR:
5645 goto handle_vtype;
5646 break;
5647 default:
5648 break;
5652 switch (t->type) {
5653 case MONO_TYPE_VOID:
5654 buffer_add_byte (buf, t->type);
5655 break;
5656 case MONO_TYPE_BOOLEAN:
5657 case MONO_TYPE_I1:
5658 case MONO_TYPE_U1:
5659 buffer_add_byte (buf, t->type);
5660 buffer_add_int (buf, *(gint8*)addr);
5661 break;
5662 case MONO_TYPE_CHAR:
5663 case MONO_TYPE_I2:
5664 case MONO_TYPE_U2:
5665 buffer_add_byte (buf, t->type);
5666 buffer_add_int (buf, *(gint16*)addr);
5667 break;
5668 case MONO_TYPE_I4:
5669 case MONO_TYPE_U4:
5670 case MONO_TYPE_R4:
5671 buffer_add_byte (buf, t->type);
5672 buffer_add_int (buf, *(gint32*)addr);
5673 break;
5674 case MONO_TYPE_I8:
5675 case MONO_TYPE_U8:
5676 case MONO_TYPE_R8:
5677 buffer_add_byte (buf, t->type);
5678 buffer_add_long (buf, *(gint64*)addr);
5679 break;
5680 case MONO_TYPE_I:
5681 case MONO_TYPE_U:
5682 /* Treat it as a vtype */
5683 goto handle_vtype;
5684 case MONO_TYPE_PTR: {
5685 gssize val = *(gssize*)addr;
5687 buffer_add_byte (buf, t->type);
5688 buffer_add_long (buf, val);
5689 break;
5691 handle_ref:
5692 case MONO_TYPE_STRING:
5693 case MONO_TYPE_SZARRAY:
5694 case MONO_TYPE_OBJECT:
5695 case MONO_TYPE_CLASS:
5696 case MONO_TYPE_ARRAY:
5697 obj = *(MonoObject**)addr;
5699 if (!obj) {
5700 buffer_add_byte (buf, VALUE_TYPE_ID_NULL);
5701 } else {
5702 if (obj->vtable->klass->valuetype) {
5703 t = &obj->vtable->klass->byval_arg;
5704 addr = mono_object_unbox (obj);
5705 boxed_vtype = TRUE;
5706 goto handle_vtype;
5707 } else if (obj->vtable->klass->rank) {
5708 buffer_add_byte (buf, obj->vtable->klass->byval_arg.type);
5709 } else if (obj->vtable->klass->byval_arg.type == MONO_TYPE_GENERICINST) {
5710 buffer_add_byte (buf, MONO_TYPE_CLASS);
5711 } else {
5712 buffer_add_byte (buf, obj->vtable->klass->byval_arg.type);
5714 buffer_add_objid (buf, obj);
5716 break;
5717 handle_vtype:
5718 case MONO_TYPE_VALUETYPE: {
5719 int nfields;
5720 gpointer iter;
5721 MonoClassField *f;
5722 MonoClass *klass = mono_class_from_mono_type (t);
5723 int vtype_index;
5725 if (boxed_vtype) {
5727 * Handle boxed vtypes recursively referencing themselves using fields.
5729 if (!parent_vtypes)
5730 parent_vtypes = g_hash_table_new (NULL, NULL);
5731 vtype_index = GPOINTER_TO_INT (g_hash_table_lookup (parent_vtypes, addr));
5732 if (vtype_index) {
5733 if (CHECK_PROTOCOL_VERSION (2, 33)) {
5734 buffer_add_byte (buf, VALUE_TYPE_ID_PARENT_VTYPE);
5735 buffer_add_int (buf, vtype_index - 1);
5736 } else {
5737 /* The client can't handle PARENT_VTYPE */
5738 buffer_add_byte (buf, VALUE_TYPE_ID_NULL);
5740 break;
5741 } else {
5742 g_hash_table_insert (parent_vtypes, addr, GINT_TO_POINTER (g_hash_table_size (parent_vtypes) + 1));
5746 buffer_add_byte (buf, MONO_TYPE_VALUETYPE);
5747 buffer_add_byte (buf, klass->enumtype);
5748 buffer_add_typeid (buf, domain, klass);
5750 nfields = 0;
5751 iter = NULL;
5752 while ((f = mono_class_get_fields (klass, &iter))) {
5753 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
5754 continue;
5755 if (mono_field_is_deleted (f))
5756 continue;
5757 nfields ++;
5759 buffer_add_int (buf, nfields);
5761 iter = NULL;
5762 while ((f = mono_class_get_fields (klass, &iter))) {
5763 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
5764 continue;
5765 if (mono_field_is_deleted (f))
5766 continue;
5767 buffer_add_value_full (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain, FALSE, parent_vtypes);
5770 if (boxed_vtype) {
5771 g_hash_table_remove (parent_vtypes, addr);
5772 if (g_hash_table_size (parent_vtypes) == 0) {
5773 g_hash_table_destroy (parent_vtypes);
5774 parent_vtypes = NULL;
5777 break;
5779 case MONO_TYPE_GENERICINST:
5780 if (mono_type_generic_inst_is_valuetype (t)) {
5781 goto handle_vtype;
5782 } else {
5783 goto handle_ref;
5785 break;
5786 default:
5787 NOT_IMPLEMENTED;
5791 static void
5792 buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
5794 buffer_add_value_full (buf, t, addr, domain, FALSE, NULL);
5797 static gboolean
5798 obj_is_of_type (MonoObject *obj, MonoType *t)
5800 MonoClass *klass = obj->vtable->klass;
5801 if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
5802 if (mono_class_is_transparent_proxy (klass)) {
5803 klass = ((MonoTransparentProxy *)obj)->remote_class->proxy_class;
5804 if (mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
5805 return TRUE;
5808 return FALSE;
5810 return TRUE;
5813 static ErrorCode
5814 decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit);
5816 static ErrorCode
5817 decode_vtype (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
5819 gboolean is_enum;
5820 MonoClass *klass;
5821 MonoClassField *f;
5822 int nfields;
5823 gpointer iter = NULL;
5824 MonoDomain *d;
5825 int err;
5827 is_enum = decode_byte (buf, &buf, limit);
5828 /* Enums are sent as a normal vtype */
5829 if (is_enum)
5830 return ERR_NOT_IMPLEMENTED;
5831 klass = decode_typeid (buf, &buf, limit, &d, &err);
5832 if (err)
5833 return err;
5835 if (t && klass != mono_class_from_mono_type (t)) {
5836 char *name = mono_type_full_name (t);
5837 char *name2 = mono_type_full_name (&klass->byval_arg);
5838 DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got %s.\n", (gpointer)GetCurrentThreadId (), name, name2));
5839 g_free (name);
5840 g_free (name2);
5841 return ERR_INVALID_ARGUMENT;
5844 nfields = decode_int (buf, &buf, limit);
5845 while ((f = mono_class_get_fields (klass, &iter))) {
5846 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
5847 continue;
5848 if (mono_field_is_deleted (f))
5849 continue;
5850 err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit);
5851 if (err)
5852 return err;
5853 nfields --;
5855 g_assert (nfields == 0);
5857 *endbuf = buf;
5859 return 0;
5862 static ErrorCode
5863 decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
5865 int err;
5867 if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
5868 !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
5869 !(t->type == MONO_TYPE_U && type == MONO_TYPE_VALUETYPE) &&
5870 !(t->type == MONO_TYPE_PTR && type == MONO_TYPE_I8) &&
5871 !(t->type == MONO_TYPE_GENERICINST && type == MONO_TYPE_VALUETYPE)) {
5872 char *name = mono_type_full_name (t);
5873 DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type));
5874 g_free (name);
5875 return ERR_INVALID_ARGUMENT;
5878 switch (t->type) {
5879 case MONO_TYPE_BOOLEAN:
5880 *(guint8*)addr = decode_int (buf, &buf, limit);
5881 break;
5882 case MONO_TYPE_CHAR:
5883 *(gunichar2*)addr = decode_int (buf, &buf, limit);
5884 break;
5885 case MONO_TYPE_I1:
5886 *(gint8*)addr = decode_int (buf, &buf, limit);
5887 break;
5888 case MONO_TYPE_U1:
5889 *(guint8*)addr = decode_int (buf, &buf, limit);
5890 break;
5891 case MONO_TYPE_I2:
5892 *(gint16*)addr = decode_int (buf, &buf, limit);
5893 break;
5894 case MONO_TYPE_U2:
5895 *(guint16*)addr = decode_int (buf, &buf, limit);
5896 break;
5897 case MONO_TYPE_I4:
5898 *(gint32*)addr = decode_int (buf, &buf, limit);
5899 break;
5900 case MONO_TYPE_U4:
5901 *(guint32*)addr = decode_int (buf, &buf, limit);
5902 break;
5903 case MONO_TYPE_I8:
5904 *(gint64*)addr = decode_long (buf, &buf, limit);
5905 break;
5906 case MONO_TYPE_U8:
5907 *(guint64*)addr = decode_long (buf, &buf, limit);
5908 break;
5909 case MONO_TYPE_R4:
5910 *(guint32*)addr = decode_int (buf, &buf, limit);
5911 break;
5912 case MONO_TYPE_R8:
5913 *(guint64*)addr = decode_long (buf, &buf, limit);
5914 break;
5915 case MONO_TYPE_PTR:
5916 /* We send these as I8, so we get them back as such */
5917 g_assert (type == MONO_TYPE_I8);
5918 *(gssize*)addr = decode_long (buf, &buf, limit);
5919 break;
5920 case MONO_TYPE_GENERICINST:
5921 if (MONO_TYPE_ISSTRUCT (t)) {
5922 /* The client sends these as a valuetype */
5923 goto handle_vtype;
5924 } else {
5925 goto handle_ref;
5927 break;
5928 case MONO_TYPE_I:
5929 case MONO_TYPE_U:
5930 /* We send these as vtypes, so we get them back as such */
5931 g_assert (type == MONO_TYPE_VALUETYPE);
5932 /* Fall through */
5933 handle_vtype:
5934 case MONO_TYPE_VALUETYPE:
5935 err = decode_vtype (t, domain, addr,buf, &buf, limit);
5936 if (err)
5937 return err;
5938 break;
5939 handle_ref:
5940 default:
5941 if (MONO_TYPE_IS_REFERENCE (t)) {
5942 if (type == MONO_TYPE_OBJECT) {
5943 int objid = decode_objid (buf, &buf, limit);
5944 int err;
5945 MonoObject *obj;
5947 err = get_object (objid, (MonoObject**)&obj);
5948 if (err)
5949 return err;
5951 if (obj) {
5952 if (!obj_is_of_type (obj, t)) {
5953 DEBUG (1, fprintf (log_file, "Expected type '%s', got '%s'\n", mono_type_full_name (t), obj->vtable->klass->name));
5954 return ERR_INVALID_ARGUMENT;
5957 if (obj && obj->vtable->domain != domain)
5958 return ERR_INVALID_ARGUMENT;
5960 mono_gc_wbarrier_generic_store (addr, obj);
5961 } else if (type == VALUE_TYPE_ID_NULL) {
5962 *(MonoObject**)addr = NULL;
5963 } else if (type == MONO_TYPE_VALUETYPE) {
5964 guint8 *buf2;
5965 gboolean is_enum;
5966 MonoClass *klass;
5967 MonoDomain *d;
5968 guint8 *vtype_buf;
5969 int vtype_buf_size;
5971 /* This can happen when round-tripping boxed vtypes */
5973 * Obtain vtype class.
5974 * Same as the beginning of the handle_vtype case above.
5976 buf2 = buf;
5977 is_enum = decode_byte (buf, &buf, limit);
5978 if (is_enum)
5979 return ERR_NOT_IMPLEMENTED;
5980 klass = decode_typeid (buf, &buf, limit, &d, &err);
5981 if (err)
5982 return err;
5984 /* Decode the vtype into a temporary buffer, then box it. */
5985 vtype_buf_size = mono_class_value_size (klass, NULL);
5986 vtype_buf = g_malloc0 (vtype_buf_size);
5987 g_assert (vtype_buf);
5989 buf = buf2;
5990 err = decode_vtype (NULL, domain, vtype_buf, buf, &buf, limit);
5991 if (err) {
5992 g_free (vtype_buf);
5993 return err;
5995 *(MonoObject**)addr = mono_value_box (d, klass, vtype_buf);
5996 g_free (vtype_buf);
5997 } else {
5998 char *name = mono_type_full_name (t);
5999 DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type));
6000 g_free (name);
6001 return ERR_INVALID_ARGUMENT;
6003 } else {
6004 NOT_IMPLEMENTED;
6006 break;
6009 *endbuf = buf;
6011 return 0;
6014 static ErrorCode
6015 decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
6017 int err;
6018 int type = decode_byte (buf, &buf, limit);
6020 if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) {
6021 MonoType *targ = t->data.generic_class->context.class_inst->type_argv [0];
6022 guint8 *nullable_buf;
6025 * First try decoding it as a Nullable`1
6027 err = decode_value_internal (t, type, domain, addr, buf, endbuf, limit);
6028 if (!err)
6029 return err;
6032 * Then try decoding as a primitive value or null.
6034 if (targ->type == type) {
6035 nullable_buf = g_malloc (mono_class_instance_size (mono_class_from_mono_type (targ)));
6036 err = decode_value_internal (targ, type, domain, nullable_buf, buf, endbuf, limit);
6037 if (err) {
6038 g_free (nullable_buf);
6039 return err;
6041 mono_nullable_init (addr, mono_value_box (domain, mono_class_from_mono_type (targ), nullable_buf), mono_class_from_mono_type (t));
6042 g_free (nullable_buf);
6043 *endbuf = buf;
6044 return ERR_NONE;
6045 } else if (type == VALUE_TYPE_ID_NULL) {
6046 mono_nullable_init (addr, NULL, mono_class_from_mono_type (t));
6047 *endbuf = buf;
6048 return ERR_NONE;
6052 return decode_value_internal (t, type, domain, addr, buf, endbuf, limit);
6055 static void
6056 add_var (Buffer *buf, MonoDebugMethodJitInfo *jit, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
6058 guint32 flags;
6059 int reg;
6060 guint8 *addr, *gaddr;
6061 mgreg_t reg_val;
6063 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6064 reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6066 switch (flags) {
6067 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
6068 reg_val = mono_arch_context_get_int_reg (ctx, reg);
6070 buffer_add_value_full (buf, t, &reg_val, domain, as_vtype, NULL);
6071 break;
6072 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
6073 addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6074 addr += (gint32)var->offset;
6076 //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
6078 buffer_add_value_full (buf, t, addr, domain, as_vtype, NULL);
6079 break;
6080 case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
6081 NOT_IMPLEMENTED;
6082 break;
6083 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR:
6084 case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR:
6085 /* Same as regoffset, but with an indirection */
6086 addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6087 addr += (gint32)var->offset;
6089 gaddr = *(gpointer*)addr;
6090 g_assert (gaddr);
6091 buffer_add_value_full (buf, t, gaddr, domain, as_vtype, NULL);
6092 break;
6093 case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: {
6094 MonoDebugVarInfo *info_var = jit->gsharedvt_info_var;
6095 MonoDebugVarInfo *locals_var = jit->gsharedvt_locals_var;
6096 MonoGSharedVtMethodRuntimeInfo *info;
6097 guint8 *locals;
6098 int idx;
6100 idx = reg;
6102 g_assert (info_var);
6103 g_assert (locals_var);
6105 flags = info_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6106 reg = info_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6107 if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) {
6108 addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6109 addr += (gint32)info_var->offset;
6110 info = *(gpointer*)addr;
6111 } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) {
6112 info = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6113 } else {
6114 g_assert_not_reached ();
6116 g_assert (info);
6118 flags = locals_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6119 reg = locals_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6120 if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) {
6121 addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6122 addr += (gint32)locals_var->offset;
6123 locals = *(gpointer*)addr;
6124 } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) {
6125 locals = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6126 } else {
6127 g_assert_not_reached ();
6129 g_assert (locals);
6131 addr = locals + GPOINTER_TO_INT (info->entries [idx]);
6133 buffer_add_value_full (buf, t, addr, domain, as_vtype, NULL);
6134 break;
6137 default:
6138 g_assert_not_reached ();
6142 static void
6143 set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, guint8 *val, mgreg_t **reg_locations, MonoContext *restore_ctx)
6145 guint32 flags;
6146 int reg, size;
6147 guint8 *addr, *gaddr;
6149 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6150 reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
6152 if (MONO_TYPE_IS_REFERENCE (t))
6153 size = sizeof (gpointer);
6154 else
6155 size = mono_class_value_size (mono_class_from_mono_type (t), NULL);
6157 switch (flags) {
6158 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: {
6159 #ifdef MONO_ARCH_HAVE_CONTEXT_SET_INT_REG
6160 mgreg_t v;
6161 gboolean is_signed = FALSE;
6163 if (t->byref) {
6164 addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6166 if (addr) {
6167 // FIXME: Write barriers
6168 mono_gc_memmove_atomic (addr, val, size);
6170 break;
6173 if (!t->byref && (t->type == MONO_TYPE_I1 || t->type == MONO_TYPE_I2 || t->type == MONO_TYPE_I4 || t->type == MONO_TYPE_I8))
6174 is_signed = TRUE;
6176 switch (size) {
6177 case 1:
6178 v = is_signed ? *(gint8*)val : *(guint8*)val;
6179 break;
6180 case 2:
6181 v = is_signed ? *(gint16*)val : *(guint16*)val;
6182 break;
6183 case 4:
6184 v = is_signed ? *(gint32*)val : *(guint32*)val;
6185 break;
6186 case 8:
6187 v = is_signed ? *(gint64*)val : *(guint64*)val;
6188 break;
6189 default:
6190 g_assert_not_reached ();
6193 /* Set value on the stack or in the return ctx */
6194 if (reg_locations [reg]) {
6195 /* Saved on the stack */
6196 DEBUG (1, fprintf (log_file, "[dbg] Setting stack location %p for reg %x to %p.\n", reg_locations [reg], reg, (gpointer)v));
6197 *(reg_locations [reg]) = v;
6198 } else {
6199 /* Not saved yet */
6200 DEBUG (1, fprintf (log_file, "[dbg] Setting context location for reg %x to %p.\n", reg, (gpointer)v));
6201 mono_arch_context_set_int_reg (restore_ctx, reg, v);
6204 // FIXME: Move these to mono-context.h/c.
6205 mono_arch_context_set_int_reg (ctx, reg, v);
6206 #else
6207 // FIXME: Can't set registers, so we disable linears
6208 NOT_IMPLEMENTED;
6209 #endif
6210 break;
6212 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
6213 addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6214 addr += (gint32)var->offset;
6216 //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
6218 if (t->byref) {
6219 addr = *(guint8**)addr;
6221 if (!addr)
6222 break;
6225 // FIXME: Write barriers
6226 mono_gc_memmove_atomic (addr, val, size);
6227 break;
6228 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR:
6229 /* Same as regoffset, but with an indirection */
6230 addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
6231 addr += (gint32)var->offset;
6233 gaddr = *(gpointer*)addr;
6234 g_assert (gaddr);
6235 // FIXME: Write barriers
6236 mono_gc_memmove_atomic (gaddr, val, size);
6237 break;
6238 case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
6239 NOT_IMPLEMENTED;
6240 break;
6241 default:
6242 g_assert_not_reached ();
6246 static void
6247 clear_event_request (int req_id, int etype)
6249 int i;
6251 mono_loader_lock ();
6252 for (i = 0; i < event_requests->len; ++i) {
6253 EventRequest *req = g_ptr_array_index (event_requests, i);
6255 if (req->id == req_id && req->event_kind == etype) {
6256 if (req->event_kind == EVENT_KIND_BREAKPOINT)
6257 clear_breakpoint (req->info);
6258 if (req->event_kind == EVENT_KIND_STEP)
6259 ss_destroy (req->info);
6260 if (req->event_kind == EVENT_KIND_METHOD_ENTRY)
6261 clear_breakpoint (req->info);
6262 if (req->event_kind == EVENT_KIND_METHOD_EXIT)
6263 clear_breakpoint (req->info);
6264 g_ptr_array_remove_index_fast (event_requests, i);
6265 g_free (req);
6266 break;
6269 mono_loader_unlock ();
6272 static gboolean
6273 event_req_matches_assembly (EventRequest *req, MonoAssembly *assembly)
6275 if (req->event_kind == EVENT_KIND_BREAKPOINT)
6276 return breakpoint_matches_assembly (req->info, assembly);
6277 else {
6278 int i, j;
6280 for (i = 0; i < req->nmodifiers; ++i) {
6281 Modifier *m = &req->modifiers [i];
6283 if (m->kind == MOD_KIND_EXCEPTION_ONLY && m->data.exc_class && m->data.exc_class->image->assembly == assembly)
6284 return TRUE;
6285 if (m->kind == MOD_KIND_ASSEMBLY_ONLY && m->data.assemblies) {
6286 for (j = 0; m->data.assemblies [j]; ++j)
6287 if (m->data.assemblies [j] == assembly)
6288 return TRUE;
6293 return FALSE;
6297 * clear_event_requests_for_assembly:
6299 * Clear all events requests which reference ASSEMBLY.
6301 static void
6302 clear_event_requests_for_assembly (MonoAssembly *assembly)
6304 int i;
6305 gboolean found;
6307 mono_loader_lock ();
6308 found = TRUE;
6309 while (found) {
6310 found = FALSE;
6311 for (i = 0; i < event_requests->len; ++i) {
6312 EventRequest *req = g_ptr_array_index (event_requests, i);
6314 if (event_req_matches_assembly (req, assembly)) {
6315 clear_event_request (req->id, req->event_kind);
6316 found = TRUE;
6317 break;
6321 mono_loader_unlock ();
6325 * type_comes_from_assembly:
6327 * GHRFunc that returns TRUE if klass comes from assembly
6329 static gboolean
6330 type_comes_from_assembly (gpointer klass, gpointer also_klass, gpointer assembly)
6332 return (mono_class_get_image ((MonoClass*)klass) == mono_assembly_get_image ((MonoAssembly*)assembly));
6336 * clear_types_for_assembly:
6338 * Clears types from loaded_classes for a given assembly
6340 static void
6341 clear_types_for_assembly (MonoAssembly *assembly)
6343 MonoDomain *domain = mono_domain_get ();
6344 AgentDomainInfo *info = NULL;
6346 mono_loader_lock ();
6347 info = get_agent_domain_info (domain);
6348 g_hash_table_foreach_remove (info->loaded_classes, type_comes_from_assembly, assembly);
6349 mono_loader_unlock ();
6352 static void
6353 add_thread (gpointer key, gpointer value, gpointer user_data)
6355 MonoInternalThread *thread = value;
6356 Buffer *buf = user_data;
6358 buffer_add_objid (buf, (MonoObject*)thread);
6361 static ErrorCode
6362 do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp)
6364 guint8 *end = invoke->endp;
6365 MonoMethod *m;
6366 int i, err, nargs;
6367 MonoMethodSignature *sig;
6368 guint8 **arg_buf;
6369 void **args;
6370 MonoObject *this, *res, *exc;
6371 MonoDomain *domain;
6372 guint8 *this_buf;
6373 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
6374 MonoLMFExt ext;
6375 #endif
6376 MonoStopwatch watch;
6378 if (invoke->method) {
6380 * Invoke this method directly, currently only Environment.Exit () is supported.
6382 this = NULL;
6383 DEBUG (1, fprintf (log_file, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (invoke->method, TRUE), this ? this->vtable->klass->name : "<null>"));
6384 mono_runtime_invoke (invoke->method, NULL, invoke->args, &exc);
6385 g_assert_not_reached ();
6388 m = decode_methodid (p, &p, end, &domain, &err);
6389 if (err)
6390 return err;
6391 sig = mono_method_signature (m);
6393 if (m->klass->valuetype)
6394 this_buf = g_alloca (mono_class_instance_size (m->klass));
6395 else
6396 this_buf = g_alloca (sizeof (MonoObject*));
6397 if (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) {
6398 /* Should be null */
6399 int type = decode_byte (p, &p, end);
6400 if (type != VALUE_TYPE_ID_NULL) {
6401 DEBUG (1, fprintf (log_file, "[%p] Error: Static vtype method invoked with this argument.\n", (gpointer)GetCurrentThreadId ()));
6402 return ERR_INVALID_ARGUMENT;
6404 memset (this_buf, 0, mono_class_instance_size (m->klass));
6405 } else {
6406 err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
6407 if (err)
6408 return err;
6411 if (!m->klass->valuetype)
6412 this = *(MonoObject**)this_buf;
6413 else
6414 this = NULL;
6416 if (MONO_CLASS_IS_INTERFACE (m->klass)) {
6417 if (!this) {
6418 DEBUG (1, fprintf (log_file, "[%p] Error: Interface method invoked without this argument.\n", (gpointer)GetCurrentThreadId ()));
6419 return ERR_INVALID_ARGUMENT;
6421 m = mono_object_get_virtual_method (this, m);
6424 DEBUG (1, fprintf (log_file, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "<null>"));
6426 if (this && this->vtable->domain != domain)
6427 NOT_IMPLEMENTED;
6429 if (!m->klass->valuetype && !(m->flags & METHOD_ATTRIBUTE_STATIC) && !this) {
6430 if (!strcmp (m->name, ".ctor")) {
6431 if (m->klass->flags & TYPE_ATTRIBUTE_ABSTRACT)
6432 return ERR_INVALID_ARGUMENT;
6433 else
6434 this = mono_object_new (domain, m->klass);
6435 } else {
6436 return ERR_INVALID_ARGUMENT;
6440 if (this && !obj_is_of_type (this, &m->klass->byval_arg))
6441 return ERR_INVALID_ARGUMENT;
6443 nargs = decode_int (p, &p, end);
6444 if (nargs != sig->param_count)
6445 return ERR_INVALID_ARGUMENT;
6446 /* Use alloca to get gc tracking */
6447 arg_buf = g_alloca (nargs * sizeof (gpointer));
6448 memset (arg_buf, 0, nargs * sizeof (gpointer));
6449 args = g_alloca (nargs * sizeof (gpointer));
6450 for (i = 0; i < nargs; ++i) {
6451 if (MONO_TYPE_IS_REFERENCE (sig->params [i])) {
6452 err = decode_value (sig->params [i], domain, (guint8*)&args [i], p, &p, end);
6453 if (err)
6454 break;
6456 if (args [i] && ((MonoObject*)args [i])->vtable->domain != domain)
6457 NOT_IMPLEMENTED;
6458 } else {
6459 arg_buf [i] = g_alloca (mono_class_instance_size (mono_class_from_mono_type (sig->params [i])));
6460 err = decode_value (sig->params [i], domain, arg_buf [i], p, &p, end);
6461 if (err)
6462 break;
6463 args [i] = arg_buf [i];
6467 if (i < nargs)
6468 return err;
6470 if (invoke->flags & INVOKE_FLAG_DISABLE_BREAKPOINTS)
6471 tls->disable_breakpoints = TRUE;
6472 else
6473 tls->disable_breakpoints = FALSE;
6476 * Add an LMF frame to link the stack frames on the invoke method with our caller.
6478 /* FIXME: Move this to arch specific code */
6479 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
6480 if (invoke->has_ctx) {
6481 MonoLMF **lmf_addr;
6483 lmf_addr = mono_get_lmf_addr ();
6485 /* Setup our lmf */
6486 memset (&ext, 0, sizeof (ext));
6487 mono_arch_init_lmf_ext (&ext, *lmf_addr);
6489 ext.debugger_invoke = TRUE;
6490 memcpy (&ext.ctx, &invoke->ctx, sizeof (MonoContext));
6492 mono_set_lmf ((MonoLMF*)&ext);
6494 #endif
6496 mono_stopwatch_start (&watch);
6497 if (m->klass->valuetype)
6498 res = mono_runtime_invoke (m, this_buf, args, &exc);
6499 else
6500 res = mono_runtime_invoke (m, this, args, &exc);
6501 mono_stopwatch_stop (&watch);
6502 DEBUG (1, fprintf (log_file, "[%p] Invoke result: %p, exc: %s, time: %ld ms.\n", (gpointer)GetCurrentThreadId (), res, exc ? exc->vtable->klass->name : NULL, (long)mono_stopwatch_elapsed_ms (&watch)));
6503 if (exc) {
6504 buffer_add_byte (buf, 0);
6505 buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &exc, domain);
6506 } else {
6507 buffer_add_byte (buf, 1);
6508 if (sig->ret->type == MONO_TYPE_VOID) {
6509 if (!strcmp (m->name, ".ctor") && !m->klass->valuetype) {
6510 buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &this, domain);
6512 else
6513 buffer_add_value (buf, &mono_defaults.void_class->byval_arg, NULL, domain);
6514 } else if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
6515 buffer_add_value (buf, sig->ret, &res, domain);
6516 } else if (mono_class_from_mono_type (sig->ret)->valuetype || sig->ret->type == MONO_TYPE_PTR || sig->ret->type == MONO_TYPE_FNPTR) {
6517 if (mono_class_is_nullable (mono_class_from_mono_type (sig->ret))) {
6518 MonoClass *k = mono_class_from_mono_type (sig->ret);
6519 guint8 *nullable_buf = g_alloca (mono_class_value_size (k, NULL));
6521 g_assert (nullable_buf);
6522 mono_nullable_init (nullable_buf, res, k);
6523 buffer_add_value (buf, sig->ret, nullable_buf, domain);
6524 } else {
6525 g_assert (res);
6526 buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
6528 } else {
6529 NOT_IMPLEMENTED;
6533 tls->disable_breakpoints = FALSE;
6535 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
6536 if (invoke->has_ctx)
6537 mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
6538 #endif
6540 *endp = p;
6541 // FIXME: byref arguments
6542 // FIXME: varargs
6543 return ERR_NONE;
6547 * invoke_method:
6549 * Invoke the method given by tls->pending_invoke in the current thread.
6551 static void
6552 invoke_method (void)
6554 DebuggerTlsData *tls;
6555 InvokeData *invoke;
6556 int id;
6557 int i, err, mindex;
6558 Buffer buf;
6559 MonoContext restore_ctx;
6560 guint8 *p;
6562 tls = mono_native_tls_get_value (debugger_tls_id);
6563 g_assert (tls);
6566 * Store the `InvokeData *' in `tls->invoke' until we're done with
6567 * the invocation, so CMD_VM_ABORT_INVOKE can check it.
6570 mono_loader_lock ();
6572 invoke = tls->pending_invoke;
6573 g_assert (invoke);
6574 tls->pending_invoke = NULL;
6576 invoke->last_invoke = tls->invoke;
6577 tls->invoke = invoke;
6579 mono_loader_unlock ();
6581 tls->frames_up_to_date = FALSE;
6583 id = invoke->id;
6585 p = invoke->p;
6586 err = 0;
6587 for (mindex = 0; mindex < invoke->nmethods; ++mindex) {
6588 buffer_init (&buf, 128);
6590 if (err) {
6591 /* Fail the other invokes as well */
6592 } else {
6593 err = do_invoke_method (tls, &buf, invoke, p, &p);
6596 /* Start suspending before sending the reply */
6597 if (mindex == invoke->nmethods - 1) {
6598 if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) {
6599 for (i = 0; i < invoke->suspend_count; ++i)
6600 suspend_vm ();
6604 send_reply_packet (id, err, &buf);
6606 buffer_free (&buf);
6609 memcpy (&restore_ctx, &invoke->ctx, sizeof (MonoContext));
6611 if (invoke->has_ctx)
6612 save_thread_context (&restore_ctx);
6614 if (invoke->flags & INVOKE_FLAG_SINGLE_THREADED) {
6615 g_assert (tls->resume_count);
6616 tls->resume_count -= invoke->suspend_count;
6619 DEBUG (1, fprintf (log_file, "[%p] Invoke finished (%d), resume_count = %d.\n", (gpointer)GetCurrentThreadId (), err, tls->resume_count));
6622 * Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE:
6624 * It is possible that ves_icall_System_Threading_Thread_Abort () was called
6625 * after the mono_runtime_invoke() already returned, but it doesn't matter
6626 * because we reset the abort here.
6629 mono_loader_lock ();
6631 if (tls->abort_requested)
6632 mono_thread_internal_reset_abort (tls->thread);
6634 tls->invoke = tls->invoke->last_invoke;
6635 tls->abort_requested = FALSE;
6637 mono_loader_unlock ();
6639 g_free (invoke->p);
6640 g_free (invoke);
6642 suspend_current ();
6645 static gboolean
6646 is_really_suspended (gpointer key, gpointer value, gpointer user_data)
6648 MonoThread *thread = value;
6649 DebuggerTlsData *tls;
6650 gboolean res;
6652 mono_loader_lock ();
6653 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
6654 g_assert (tls);
6655 res = tls->really_suspended;
6656 mono_loader_unlock ();
6658 return res;
6661 static GPtrArray*
6662 get_source_files_for_type (MonoClass *klass)
6664 gpointer iter = NULL;
6665 MonoMethod *method;
6666 MonoDebugSourceInfo *sinfo;
6667 GPtrArray *files;
6668 int i, j;
6670 files = g_ptr_array_new ();
6672 while ((method = mono_class_get_methods (klass, &iter))) {
6673 MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
6674 GPtrArray *source_file_list;
6676 if (minfo) {
6677 mono_debug_symfile_get_line_numbers_full (minfo, NULL, &source_file_list, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
6678 for (j = 0; j < source_file_list->len; ++j) {
6679 sinfo = g_ptr_array_index (source_file_list, j);
6680 for (i = 0; i < files->len; ++i)
6681 if (!strcmp (g_ptr_array_index (files, i), sinfo->source_file))
6682 break;
6683 if (i == files->len)
6684 g_ptr_array_add (files, g_strdup (sinfo->source_file));
6686 g_ptr_array_free (source_file_list, TRUE);
6690 return files;
6693 static ErrorCode
6694 vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
6696 switch (command) {
6697 case CMD_VM_VERSION: {
6698 char *build_info, *version;
6700 build_info = mono_get_runtime_build_info ();
6701 version = g_strdup_printf ("mono %s", build_info);
6703 buffer_add_string (buf, version); /* vm version */
6704 buffer_add_int (buf, MAJOR_VERSION);
6705 buffer_add_int (buf, MINOR_VERSION);
6706 g_free (build_info);
6707 g_free (version);
6708 break;
6710 case CMD_VM_SET_PROTOCOL_VERSION: {
6711 major_version = decode_int (p, &p, end);
6712 minor_version = decode_int (p, &p, end);
6713 protocol_version_set = TRUE;
6714 DEBUG(1, fprintf (log_file, "[dbg] Protocol version %d.%d, client protocol version %d.%d.\n", MAJOR_VERSION, MINOR_VERSION, major_version, minor_version));
6715 break;
6717 case CMD_VM_ALL_THREADS: {
6718 // FIXME: Domains
6719 mono_loader_lock ();
6720 buffer_add_int (buf, mono_g_hash_table_size (tid_to_thread_obj));
6721 mono_g_hash_table_foreach (tid_to_thread_obj, add_thread, buf);
6722 mono_loader_unlock ();
6723 break;
6725 case CMD_VM_SUSPEND:
6726 suspend_vm ();
6727 wait_for_suspend ();
6728 break;
6729 case CMD_VM_RESUME:
6730 if (suspend_count == 0)
6731 return ERR_NOT_SUSPENDED;
6732 resume_vm ();
6733 clear_suspended_objs ();
6734 break;
6735 case CMD_VM_DISPOSE:
6736 /* Clear all event requests */
6737 mono_loader_lock ();
6738 while (event_requests->len > 0) {
6739 EventRequest *req = g_ptr_array_index (event_requests, 0);
6741 clear_event_request (req->id, req->event_kind);
6743 mono_loader_unlock ();
6745 while (suspend_count > 0)
6746 resume_vm ();
6747 disconnected = TRUE;
6748 vm_start_event_sent = FALSE;
6749 break;
6750 case CMD_VM_EXIT: {
6751 MonoInternalThread *thread;
6752 DebuggerTlsData *tls;
6753 #ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
6754 MonoClass *env_class;
6755 #endif
6756 MonoMethod *exit_method = NULL;
6757 gpointer *args;
6758 int exit_code;
6760 exit_code = decode_int (p, &p, end);
6762 // FIXME: What if there is a VM_DEATH event request with SUSPEND_ALL ?
6764 /* Have to send a reply before exiting */
6765 send_reply_packet (id, 0, buf);
6767 /* Clear all event requests */
6768 mono_loader_lock ();
6769 while (event_requests->len > 0) {
6770 EventRequest *req = g_ptr_array_index (event_requests, 0);
6772 clear_event_request (req->id, req->event_kind);
6774 mono_loader_unlock ();
6777 * The JDWP documentation says that the shutdown is not orderly. It doesn't
6778 * specify whenever a VM_DEATH event is sent. We currently do an orderly
6779 * shutdown by hijacking a thread to execute Environment.Exit (). This is
6780 * better than doing the shutdown ourselves, since it avoids various races.
6783 suspend_vm ();
6784 wait_for_suspend ();
6786 #ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
6787 env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
6788 if (env_class)
6789 exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
6790 #endif
6792 mono_loader_lock ();
6793 thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL);
6794 mono_loader_unlock ();
6796 if (thread && exit_method) {
6797 mono_loader_lock ();
6798 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
6799 mono_loader_unlock ();
6801 args = g_new0 (gpointer, 1);
6802 args [0] = g_malloc (sizeof (int));
6803 *(int*)(args [0]) = exit_code;
6805 tls->pending_invoke = g_new0 (InvokeData, 1);
6806 tls->pending_invoke->method = exit_method;
6807 tls->pending_invoke->args = args;
6808 tls->pending_invoke->nmethods = 1;
6810 while (suspend_count > 0)
6811 resume_vm ();
6812 } else {
6814 * No thread found, do it ourselves.
6815 * FIXME: This can race with normal shutdown etc.
6817 while (suspend_count > 0)
6818 resume_vm ();
6820 if (!mono_runtime_try_shutdown ())
6821 break;
6823 mono_environment_exitcode_set (exit_code);
6825 /* Suspend all managed threads since the runtime is going away */
6826 DEBUG(1, fprintf (log_file, "Suspending all threads...\n"));
6827 mono_thread_suspend_all_other_threads ();
6828 DEBUG(1, fprintf (log_file, "Shutting down the runtime...\n"));
6829 mono_runtime_quit ();
6830 transport_close2 ();
6831 DEBUG(1, fprintf (log_file, "Exiting...\n"));
6833 exit (exit_code);
6835 break;
6837 case CMD_VM_INVOKE_METHOD:
6838 case CMD_VM_INVOKE_METHODS: {
6839 int objid = decode_objid (p, &p, end);
6840 MonoThread *thread;
6841 DebuggerTlsData *tls;
6842 int i, count, err, flags, nmethods;
6844 err = get_object (objid, (MonoObject**)&thread);
6845 if (err)
6846 return err;
6848 flags = decode_int (p, &p, end);
6850 if (command == CMD_VM_INVOKE_METHODS)
6851 nmethods = decode_int (p, &p, end);
6852 else
6853 nmethods = 1;
6855 // Wait for suspending if it already started
6856 if (suspend_count)
6857 wait_for_suspend ();
6858 if (!is_suspended ())
6859 return ERR_NOT_SUSPENDED;
6861 mono_loader_lock ();
6862 tls = mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL (thread));
6863 mono_loader_unlock ();
6864 g_assert (tls);
6866 if (!tls->really_suspended)
6867 /* The thread is still running native code, can't do invokes */
6868 return ERR_NOT_SUSPENDED;
6871 * Store the invoke data into tls, the thread will execute it after it is
6872 * resumed.
6874 if (tls->pending_invoke)
6875 return ERR_NOT_SUSPENDED;
6876 tls->pending_invoke = g_new0 (InvokeData, 1);
6877 tls->pending_invoke->id = id;
6878 tls->pending_invoke->flags = flags;
6879 tls->pending_invoke->p = g_malloc (end - p);
6880 memcpy (tls->pending_invoke->p, p, end - p);
6881 tls->pending_invoke->endp = tls->pending_invoke->p + (end - p);
6882 tls->pending_invoke->suspend_count = suspend_count;
6883 tls->pending_invoke->nmethods = nmethods;
6885 if (flags & INVOKE_FLAG_SINGLE_THREADED) {
6886 resume_thread (THREAD_TO_INTERNAL (thread));
6888 else {
6889 count = suspend_count;
6890 for (i = 0; i < count; ++i)
6891 resume_vm ();
6893 break;
6895 case CMD_VM_ABORT_INVOKE: {
6896 int objid = decode_objid (p, &p, end);
6897 MonoThread *thread;
6898 DebuggerTlsData *tls;
6899 int invoke_id, err;
6901 err = get_object (objid, (MonoObject**)&thread);
6902 if (err)
6903 return err;
6905 invoke_id = decode_int (p, &p, end);
6907 mono_loader_lock ();
6908 tls = mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL (thread));
6909 g_assert (tls);
6911 if (tls->abort_requested) {
6912 mono_loader_unlock ();
6913 break;
6917 * Check whether we're still inside the mono_runtime_invoke() and that it's
6918 * actually the correct invocation.
6920 * Careful, we do not stop the thread that's doing the invocation, so we can't
6921 * inspect its stack. However, invoke_method() also acquires the loader lock
6922 * when it's done, so we're safe here.
6926 if (!tls->invoke || (tls->invoke->id != invoke_id)) {
6927 mono_loader_unlock ();
6928 return ERR_NO_INVOCATION;
6931 tls->abort_requested = TRUE;
6933 ves_icall_System_Threading_Thread_Abort (THREAD_TO_INTERNAL (thread), NULL);
6934 mono_loader_unlock ();
6935 break;
6938 case CMD_VM_SET_KEEPALIVE: {
6939 int timeout = decode_int (p, &p, end);
6940 agent_config.keepalive = timeout;
6941 // FIXME:
6942 #ifndef DISABLE_SOCKET_TRANSPORT
6943 set_keepalive ();
6944 #else
6945 NOT_IMPLEMENTED;
6946 #endif
6947 break;
6949 case CMD_VM_GET_TYPES_FOR_SOURCE_FILE: {
6950 GHashTableIter iter, kiter;
6951 MonoDomain *domain;
6952 MonoClass *klass;
6953 GPtrArray *files;
6954 int i;
6955 char *fname, *basename;
6956 gboolean ignore_case;
6957 GSList *class_list, *l;
6958 GPtrArray *res_classes, *res_domains;
6960 fname = decode_string (p, &p, end);
6961 ignore_case = decode_byte (p, &p, end);
6963 basename = g_path_get_basename (fname);
6965 res_classes = g_ptr_array_new ();
6966 res_domains = g_ptr_array_new ();
6968 mono_loader_lock ();
6969 g_hash_table_iter_init (&iter, domains);
6970 while (g_hash_table_iter_next (&iter, NULL, (void**)&domain)) {
6971 AgentDomainInfo *info = domain_jit_info (domain)->agent_info;
6973 /* Update 'source_file_to_class' cache */
6974 g_hash_table_iter_init (&kiter, info->loaded_classes);
6975 while (g_hash_table_iter_next (&kiter, NULL, (void**)&klass)) {
6976 if (!g_hash_table_lookup (info->source_files, klass)) {
6977 files = get_source_files_for_type (klass);
6978 g_hash_table_insert (info->source_files, klass, files);
6980 for (i = 0; i < files->len; ++i) {
6981 char *s = g_ptr_array_index (files, i);
6982 char *s2 = g_path_get_basename (s);
6983 char *s3;
6985 class_list = g_hash_table_lookup (info->source_file_to_class, s2);
6986 if (!class_list) {
6987 class_list = g_slist_prepend (class_list, klass);
6988 g_hash_table_insert (info->source_file_to_class, g_strdup (s2), class_list);
6989 } else {
6990 class_list = g_slist_prepend (class_list, klass);
6991 g_hash_table_insert (info->source_file_to_class, s2, class_list);
6994 /* The _ignorecase hash contains the lowercase path */
6995 s3 = strdup_tolower (s2);
6996 class_list = g_hash_table_lookup (info->source_file_to_class_ignorecase, s3);
6997 if (!class_list) {
6998 class_list = g_slist_prepend (class_list, klass);
6999 g_hash_table_insert (info->source_file_to_class_ignorecase, g_strdup (s3), class_list);
7000 } else {
7001 class_list = g_slist_prepend (class_list, klass);
7002 g_hash_table_insert (info->source_file_to_class_ignorecase, s3, class_list);
7005 g_free (s2);
7006 g_free (s3);
7011 if (ignore_case) {
7012 char *s;
7014 s = strdup_tolower (basename);
7015 class_list = g_hash_table_lookup (info->source_file_to_class_ignorecase, s);
7016 g_free (s);
7017 } else {
7018 class_list = g_hash_table_lookup (info->source_file_to_class, basename);
7021 for (l = class_list; l; l = l->next) {
7022 klass = l->data;
7024 g_ptr_array_add (res_classes, klass);
7025 g_ptr_array_add (res_domains, domain);
7028 mono_loader_unlock ();
7030 g_free (fname);
7031 g_free (basename);
7033 buffer_add_int (buf, res_classes->len);
7034 for (i = 0; i < res_classes->len; ++i)
7035 buffer_add_typeid (buf, g_ptr_array_index (res_domains, i), g_ptr_array_index (res_classes, i));
7036 g_ptr_array_free (res_classes, TRUE);
7037 g_ptr_array_free (res_domains, TRUE);
7038 break;
7040 case CMD_VM_GET_TYPES: {
7041 GHashTableIter iter;
7042 MonoDomain *domain;
7043 int i;
7044 char *name;
7045 gboolean ignore_case;
7046 GPtrArray *res_classes, *res_domains;
7047 MonoTypeNameParse info;
7049 name = decode_string (p, &p, end);
7050 ignore_case = decode_byte (p, &p, end);
7052 if (!mono_reflection_parse_type (name, &info)) {
7053 g_free (name);
7054 mono_reflection_free_type_info (&info);
7055 return ERR_INVALID_ARGUMENT;
7058 res_classes = g_ptr_array_new ();
7059 res_domains = g_ptr_array_new ();
7061 mono_loader_lock ();
7062 g_hash_table_iter_init (&iter, domains);
7063 while (g_hash_table_iter_next (&iter, NULL, (void**)&domain)) {
7064 MonoAssembly *ass;
7065 gboolean type_resolve;
7066 MonoType *t;
7067 GSList *tmp;
7069 mono_domain_assemblies_lock (domain);
7070 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
7071 ass = tmp->data;
7073 if (ass->image) {
7074 type_resolve = TRUE;
7075 t = mono_reflection_get_type (ass->image, &info, ignore_case, &type_resolve);
7076 if (t) {
7077 g_ptr_array_add (res_classes, mono_type_get_class (t));
7078 g_ptr_array_add (res_domains, domain);
7082 mono_domain_assemblies_unlock (domain);
7084 mono_loader_unlock ();
7086 g_free (name);
7087 mono_reflection_free_type_info (&info);
7089 buffer_add_int (buf, res_classes->len);
7090 for (i = 0; i < res_classes->len; ++i)
7091 buffer_add_typeid (buf, g_ptr_array_index (res_domains, i), g_ptr_array_index (res_classes, i));
7092 g_ptr_array_free (res_classes, TRUE);
7093 g_ptr_array_free (res_domains, TRUE);
7094 break;
7096 case CMD_VM_START_BUFFERING:
7097 case CMD_VM_STOP_BUFFERING:
7098 /* Handled in the main loop */
7099 break;
7100 default:
7101 return ERR_NOT_IMPLEMENTED;
7104 return ERR_NONE;
7107 static ErrorCode
7108 event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
7110 int err;
7111 MonoError error;
7113 switch (command) {
7114 case CMD_EVENT_REQUEST_SET: {
7115 EventRequest *req;
7116 int i, event_kind, suspend_policy, nmodifiers, mod;
7117 MonoMethod *method;
7118 long location = 0;
7119 MonoThread *step_thread;
7120 int size = 0, depth = 0, filter = 0, step_thread_id = 0;
7121 MonoDomain *domain;
7122 Modifier *modifier;
7124 event_kind = decode_byte (p, &p, end);
7125 suspend_policy = decode_byte (p, &p, end);
7126 nmodifiers = decode_byte (p, &p, end);
7128 req = g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier)));
7129 req->id = InterlockedIncrement (&event_request_id);
7130 req->event_kind = event_kind;
7131 req->suspend_policy = suspend_policy;
7132 req->nmodifiers = nmodifiers;
7134 method = NULL;
7135 for (i = 0; i < nmodifiers; ++i) {
7136 mod = decode_byte (p, &p, end);
7138 req->modifiers [i].kind = mod;
7139 if (mod == MOD_KIND_COUNT) {
7140 req->modifiers [i].data.count = decode_int (p, &p, end);
7141 } else if (mod == MOD_KIND_LOCATION_ONLY) {
7142 method = decode_methodid (p, &p, end, &domain, &err);
7143 if (err)
7144 return err;
7145 location = decode_long (p, &p, end);
7146 } else if (mod == MOD_KIND_STEP) {
7147 step_thread_id = decode_id (p, &p, end);
7148 size = decode_int (p, &p, end);
7149 depth = decode_int (p, &p, end);
7150 if (CHECK_PROTOCOL_VERSION (2, 16))
7151 filter = decode_int (p, &p, end);
7152 req->modifiers [i].data.filter = filter;
7153 if (!CHECK_PROTOCOL_VERSION (2, 26) && (req->modifiers [i].data.filter & STEP_FILTER_DEBUGGER_HIDDEN))
7154 /* Treat STEP_THOUGH the same as HIDDEN */
7155 req->modifiers [i].data.filter |= STEP_FILTER_DEBUGGER_STEP_THROUGH;
7156 } else if (mod == MOD_KIND_THREAD_ONLY) {
7157 int id = decode_id (p, &p, end);
7159 err = get_object (id, (MonoObject**)&req->modifiers [i].data.thread);
7160 if (err) {
7161 g_free (req);
7162 return err;
7164 } else if (mod == MOD_KIND_EXCEPTION_ONLY) {
7165 MonoClass *exc_class = decode_typeid (p, &p, end, &domain, &err);
7167 if (err)
7168 return err;
7169 req->modifiers [i].caught = decode_byte (p, &p, end);
7170 req->modifiers [i].uncaught = decode_byte (p, &p, end);
7171 if (CHECK_PROTOCOL_VERSION (2, 25))
7172 req->modifiers [i].subclasses = decode_byte (p, &p, end);
7173 else
7174 req->modifiers [i].subclasses = TRUE;
7175 DEBUG(1, fprintf (log_file, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : "", req->modifiers [i].subclasses ? ", include-subclasses" : ""));
7176 if (exc_class) {
7177 req->modifiers [i].data.exc_class = exc_class;
7179 if (!mono_class_is_assignable_from (mono_defaults.exception_class, exc_class)) {
7180 g_free (req);
7181 return ERR_INVALID_ARGUMENT;
7184 } else if (mod == MOD_KIND_ASSEMBLY_ONLY) {
7185 int n = decode_int (p, &p, end);
7186 int j;
7188 req->modifiers [i].data.assemblies = g_new0 (MonoAssembly*, n);
7189 for (j = 0; j < n; ++j) {
7190 req->modifiers [i].data.assemblies [j] = decode_assemblyid (p, &p, end, &domain, &err);
7191 if (err) {
7192 g_free (req->modifiers [i].data.assemblies);
7193 return err;
7196 } else if (mod == MOD_KIND_SOURCE_FILE_ONLY) {
7197 int n = decode_int (p, &p, end);
7198 int j;
7200 modifier = &req->modifiers [i];
7201 modifier->data.source_files = g_hash_table_new (g_str_hash, g_str_equal);
7202 for (j = 0; j < n; ++j) {
7203 char *s = decode_string (p, &p, end);
7204 char *s2;
7206 if (s) {
7207 s2 = strdup_tolower (s);
7208 g_hash_table_insert (modifier->data.source_files, s2, s2);
7209 g_free (s);
7212 } else if (mod == MOD_KIND_TYPE_NAME_ONLY) {
7213 int n = decode_int (p, &p, end);
7214 int j;
7216 modifier = &req->modifiers [i];
7217 modifier->data.type_names = g_hash_table_new (g_str_hash, g_str_equal);
7218 for (j = 0; j < n; ++j) {
7219 char *s = decode_string (p, &p, end);
7221 if (s)
7222 g_hash_table_insert (modifier->data.type_names, s, s);
7224 } else {
7225 g_free (req);
7226 return ERR_NOT_IMPLEMENTED;
7230 if (req->event_kind == EVENT_KIND_BREAKPOINT) {
7231 g_assert (method);
7233 req->info = set_breakpoint (method, location, req, &error);
7234 if (!mono_error_ok (&error)) {
7235 g_free (req);
7236 DEBUG(1, fprintf (log_file, "[dbg] Failed to set breakpoint: %s\n", mono_error_get_message (&error)));
7237 mono_error_cleanup (&error);
7238 return ERR_NO_SEQ_POINT_AT_IL_OFFSET;
7240 } else if (req->event_kind == EVENT_KIND_STEP) {
7241 g_assert (step_thread_id);
7243 err = get_object (step_thread_id, (MonoObject**)&step_thread);
7244 if (err) {
7245 g_free (req);
7246 return err;
7249 err = ss_create (THREAD_TO_INTERNAL (step_thread), size, depth, req);
7250 if (err) {
7251 g_free (req);
7252 return err;
7254 } else if (req->event_kind == EVENT_KIND_METHOD_ENTRY) {
7255 req->info = set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req, NULL);
7256 } else if (req->event_kind == EVENT_KIND_METHOD_EXIT) {
7257 req->info = set_breakpoint (NULL, METHOD_EXIT_IL_OFFSET, req, NULL);
7258 } else if (req->event_kind == EVENT_KIND_EXCEPTION) {
7259 } else if (req->event_kind == EVENT_KIND_TYPE_LOAD) {
7260 } else {
7261 if (req->nmodifiers) {
7262 g_free (req);
7263 return ERR_NOT_IMPLEMENTED;
7267 mono_loader_lock ();
7268 g_ptr_array_add (event_requests, req);
7270 if (agent_config.defer) {
7271 /* Transmit cached data to the client on receipt of the event request */
7272 switch (req->event_kind) {
7273 case EVENT_KIND_APPDOMAIN_CREATE:
7274 /* Emit load events for currently loaded domains */
7275 g_hash_table_foreach (domains, emit_appdomain_load, NULL);
7276 break;
7277 case EVENT_KIND_ASSEMBLY_LOAD:
7278 /* Emit load events for currently loaded assemblies */
7279 mono_assembly_foreach (emit_assembly_load, NULL);
7280 break;
7281 case EVENT_KIND_THREAD_START:
7282 /* Emit start events for currently started threads */
7283 mono_g_hash_table_foreach (tid_to_thread, emit_thread_start, NULL);
7284 break;
7285 case EVENT_KIND_TYPE_LOAD:
7286 /* Emit type load events for currently loaded types */
7287 mono_domain_foreach (send_types_for_domain, NULL);
7288 break;
7289 default:
7290 break;
7293 mono_loader_unlock ();
7295 buffer_add_int (buf, req->id);
7296 break;
7298 case CMD_EVENT_REQUEST_CLEAR: {
7299 int etype = decode_byte (p, &p, end);
7300 int req_id = decode_int (p, &p, end);
7302 // FIXME: Make a faster mapping from req_id to request
7303 mono_loader_lock ();
7304 clear_event_request (req_id, etype);
7305 mono_loader_unlock ();
7306 break;
7308 case CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS: {
7309 int i;
7311 mono_loader_lock ();
7312 i = 0;
7313 while (i < event_requests->len) {
7314 EventRequest *req = g_ptr_array_index (event_requests, i);
7316 if (req->event_kind == EVENT_KIND_BREAKPOINT) {
7317 clear_breakpoint (req->info);
7319 g_ptr_array_remove_index_fast (event_requests, i);
7320 g_free (req);
7321 } else {
7322 i ++;
7325 mono_loader_unlock ();
7326 break;
7328 default:
7329 return ERR_NOT_IMPLEMENTED;
7332 return ERR_NONE;
7335 static ErrorCode
7336 domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
7338 int err;
7339 MonoDomain *domain;
7341 switch (command) {
7342 case CMD_APPDOMAIN_GET_ROOT_DOMAIN: {
7343 buffer_add_domainid (buf, mono_get_root_domain ());
7344 break;
7346 case CMD_APPDOMAIN_GET_FRIENDLY_NAME: {
7347 domain = decode_domainid (p, &p, end, NULL, &err);
7348 if (err)
7349 return err;
7350 buffer_add_string (buf, domain->friendly_name);
7351 break;
7353 case CMD_APPDOMAIN_GET_ASSEMBLIES: {
7354 GSList *tmp;
7355 MonoAssembly *ass;
7356 int count;
7358 domain = decode_domainid (p, &p, end, NULL, &err);
7359 if (err)
7360 return err;
7361 mono_loader_lock ();
7362 count = 0;
7363 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
7364 count ++;
7366 buffer_add_int (buf, count);
7367 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
7368 ass = tmp->data;
7369 buffer_add_assemblyid (buf, domain, ass);
7371 mono_loader_unlock ();
7372 break;
7374 case CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY: {
7375 domain = decode_domainid (p, &p, end, NULL, &err);
7376 if (err)
7377 return err;
7379 buffer_add_assemblyid (buf, domain, domain->entry_assembly);
7380 break;
7382 case CMD_APPDOMAIN_GET_CORLIB: {
7383 domain = decode_domainid (p, &p, end, NULL, &err);
7384 if (err)
7385 return err;
7387 buffer_add_assemblyid (buf, domain, domain->domain->mbr.obj.vtable->klass->image->assembly);
7388 break;
7390 case CMD_APPDOMAIN_CREATE_STRING: {
7391 char *s;
7392 MonoString *o;
7394 domain = decode_domainid (p, &p, end, NULL, &err);
7395 if (err)
7396 return err;
7397 s = decode_string (p, &p, end);
7399 o = mono_string_new (domain, s);
7400 buffer_add_objid (buf, (MonoObject*)o);
7401 break;
7403 case CMD_APPDOMAIN_CREATE_BOXED_VALUE: {
7404 MonoClass *klass;
7405 MonoDomain *domain2;
7406 MonoObject *o;
7408 domain = decode_domainid (p, &p, end, NULL, &err);
7409 if (err)
7410 return err;
7411 klass = decode_typeid (p, &p, end, &domain2, &err);
7412 if (err)
7413 return err;
7415 // FIXME:
7416 g_assert (domain == domain2);
7418 o = mono_object_new (domain, klass);
7420 err = decode_value (&klass->byval_arg, domain, mono_object_unbox (o), p, &p, end);
7421 if (err)
7422 return err;
7424 buffer_add_objid (buf, o);
7425 break;
7427 default:
7428 return ERR_NOT_IMPLEMENTED;
7431 return ERR_NONE;
7434 static ErrorCode
7435 assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
7437 int err;
7438 MonoAssembly *ass;
7439 MonoDomain *domain;
7441 ass = decode_assemblyid (p, &p, end, &domain, &err);
7442 if (err)
7443 return err;
7445 switch (command) {
7446 case CMD_ASSEMBLY_GET_LOCATION: {
7447 buffer_add_string (buf, mono_image_get_filename (ass->image));
7448 break;
7450 case CMD_ASSEMBLY_GET_ENTRY_POINT: {
7451 guint32 token;
7452 MonoMethod *m;
7454 if (ass->image->dynamic) {
7455 buffer_add_id (buf, 0);
7456 } else {
7457 token = mono_image_get_entry_point (ass->image);
7458 if (token == 0) {
7459 buffer_add_id (buf, 0);
7460 } else {
7461 m = mono_get_method (ass->image, token, NULL);
7462 buffer_add_methodid (buf, domain, m);
7465 break;
7467 case CMD_ASSEMBLY_GET_MANIFEST_MODULE: {
7468 buffer_add_moduleid (buf, domain, ass->image);
7469 break;
7471 case CMD_ASSEMBLY_GET_OBJECT: {
7472 MonoObject *o = (MonoObject*)mono_assembly_get_object (domain, ass);
7473 buffer_add_objid (buf, o);
7474 break;
7476 case CMD_ASSEMBLY_GET_TYPE: {
7477 char *s = decode_string (p, &p, end);
7478 gboolean ignorecase = decode_byte (p, &p, end);
7479 MonoTypeNameParse info;
7480 MonoType *t;
7481 gboolean type_resolve, res;
7482 MonoDomain *d = mono_domain_get ();
7484 /* This is needed to be able to find referenced assemblies */
7485 res = mono_domain_set (domain, FALSE);
7486 g_assert (res);
7488 if (!mono_reflection_parse_type (s, &info)) {
7489 t = NULL;
7490 } else {
7491 if (info.assembly.name)
7492 NOT_IMPLEMENTED;
7493 t = mono_reflection_get_type (ass->image, &info, ignorecase, &type_resolve);
7495 buffer_add_typeid (buf, domain, t ? mono_class_from_mono_type (t) : NULL);
7496 mono_reflection_free_type_info (&info);
7497 g_free (s);
7499 mono_domain_set (d, TRUE);
7501 break;
7503 case CMD_ASSEMBLY_GET_NAME: {
7504 gchar *name;
7505 MonoAssembly *mass = ass;
7507 name = g_strdup_printf (
7508 "%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
7509 mass->aname.name,
7510 mass->aname.major, mass->aname.minor, mass->aname.build, mass->aname.revision,
7511 mass->aname.culture && *mass->aname.culture? mass->aname.culture: "neutral",
7512 mass->aname.public_key_token [0] ? (char *)mass->aname.public_key_token : "null",
7513 (mass->aname.flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
7515 buffer_add_string (buf, name);
7516 g_free (name);
7517 break;
7519 default:
7520 return ERR_NOT_IMPLEMENTED;
7523 return ERR_NONE;
7526 static ErrorCode
7527 module_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
7529 int err;
7530 MonoDomain *domain;
7532 switch (command) {
7533 case CMD_MODULE_GET_INFO: {
7534 MonoImage *image = decode_moduleid (p, &p, end, &domain, &err);
7535 char *basename;
7537 basename = g_path_get_basename (image->name);
7538 buffer_add_string (buf, basename); // name
7539 buffer_add_string (buf, image->module_name); // scopename
7540 buffer_add_string (buf, image->name); // fqname
7541 buffer_add_string (buf, mono_image_get_guid (image)); // guid
7542 buffer_add_assemblyid (buf, domain, image->assembly); // assembly
7543 g_free (basename);
7544 break;
7546 default:
7547 return ERR_NOT_IMPLEMENTED;
7550 return ERR_NONE;
7553 static ErrorCode
7554 field_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
7556 int err;
7557 MonoDomain *domain;
7559 switch (command) {
7560 case CMD_FIELD_GET_INFO: {
7561 MonoClassField *f = decode_fieldid (p, &p, end, &domain, &err);
7563 buffer_add_string (buf, f->name);
7564 buffer_add_typeid (buf, domain, f->parent);
7565 buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type));
7566 buffer_add_int (buf, f->type->attrs);
7567 break;
7569 default:
7570 return ERR_NOT_IMPLEMENTED;
7573 return ERR_NONE;
7576 static void
7577 buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val)
7579 if (val && val->vtable->klass == mono_defaults.monotype_class) {
7580 /* Special case these so the client doesn't have to handle Type objects */
7582 buffer_add_byte (buf, VALUE_TYPE_ID_TYPE);
7583 buffer_add_typeid (buf, domain, mono_class_from_mono_type (((MonoReflectionType*)val)->type));
7584 } else if (MONO_TYPE_IS_REFERENCE (t))
7585 buffer_add_value (buf, t, &val, domain);
7586 else
7587 buffer_add_value (buf, t, mono_object_unbox (val), domain);
7590 static void
7591 buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass *attr_klass, MonoCustomAttrInfo *cinfo)
7593 int i, j;
7594 int nattrs = 0;
7596 if (!cinfo) {
7597 buffer_add_int (buf, 0);
7598 return;
7601 for (i = 0; i < cinfo->num_attrs; ++i) {
7602 if (!attr_klass || mono_class_has_parent (cinfo->attrs [i].ctor->klass, attr_klass))
7603 nattrs ++;
7605 buffer_add_int (buf, nattrs);
7607 for (i = 0; i < cinfo->num_attrs; ++i) {
7608 MonoCustomAttrEntry *attr = &cinfo->attrs [i];
7609 if (!attr_klass || mono_class_has_parent (attr->ctor->klass, attr_klass)) {
7610 MonoArray *typed_args, *named_args;
7611 MonoType *t;
7612 CattrNamedArg *arginfo = NULL;
7613 MonoError error;
7615 mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error);
7616 g_assert (mono_error_ok (&error));
7618 buffer_add_methodid (buf, domain, attr->ctor);
7620 /* Ctor args */
7621 if (typed_args) {
7622 buffer_add_int (buf, mono_array_length (typed_args));
7623 for (j = 0; j < mono_array_length (typed_args); ++j) {
7624 MonoObject *val = mono_array_get (typed_args, MonoObject*, j);
7626 t = mono_method_signature (attr->ctor)->params [j];
7628 buffer_add_cattr_arg (buf, t, domain, val);
7630 } else {
7631 buffer_add_int (buf, 0);
7634 /* Named args */
7635 if (named_args) {
7636 buffer_add_int (buf, mono_array_length (named_args));
7638 for (j = 0; j < mono_array_length (named_args); ++j) {
7639 MonoObject *val = mono_array_get (named_args, MonoObject*, j);
7641 if (arginfo [j].prop) {
7642 buffer_add_byte (buf, 0x54);
7643 buffer_add_propertyid (buf, domain, arginfo [j].prop);
7644 } else if (arginfo [j].field) {
7645 buffer_add_byte (buf, 0x53);
7646 buffer_add_fieldid (buf, domain, arginfo [j].field);
7647 } else {
7648 g_assert_not_reached ();
7651 buffer_add_cattr_arg (buf, arginfo [j].type, domain, val);
7653 } else {
7654 buffer_add_int (buf, 0);
7656 g_free (arginfo);
7661 /* FIXME: Code duplication with icall.c */
7662 static void
7663 collect_interfaces (MonoClass *klass, GHashTable *ifaces, MonoError *error)
7665 int i;
7666 MonoClass *ic;
7668 mono_class_setup_interfaces (klass, error);
7669 if (!mono_error_ok (error))
7670 return;
7672 for (i = 0; i < klass->interface_count; i++) {
7673 ic = klass->interfaces [i];
7674 g_hash_table_insert (ifaces, ic, ic);
7676 collect_interfaces (ic, ifaces, error);
7677 if (!mono_error_ok (error))
7678 return;
7682 static ErrorCode
7683 type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf)
7685 MonoClass *nested;
7686 MonoType *type;
7687 gpointer iter;
7688 guint8 b;
7689 int err, nnested;
7690 char *name;
7692 switch (command) {
7693 case CMD_TYPE_GET_INFO: {
7694 buffer_add_string (buf, klass->name_space);
7695 buffer_add_string (buf, klass->name);
7696 // FIXME: byref
7697 name = mono_type_get_name_full (&klass->byval_arg, MONO_TYPE_NAME_FORMAT_FULL_NAME);
7698 buffer_add_string (buf, name);
7699 g_free (name);
7700 buffer_add_assemblyid (buf, domain, klass->image->assembly);
7701 buffer_add_moduleid (buf, domain, klass->image);
7702 buffer_add_typeid (buf, domain, klass->parent);
7703 if (klass->rank || klass->byval_arg.type == MONO_TYPE_PTR)
7704 buffer_add_typeid (buf, domain, klass->element_class);
7705 else
7706 buffer_add_id (buf, 0);
7707 buffer_add_int (buf, klass->type_token);
7708 buffer_add_byte (buf, klass->rank);
7709 buffer_add_int (buf, klass->flags);
7710 b = 0;
7711 type = &klass->byval_arg;
7712 // FIXME: Can't decide whenever a class represents a byref type
7713 if (FALSE)
7714 b |= (1 << 0);
7715 if (type->type == MONO_TYPE_PTR)
7716 b |= (1 << 1);
7717 if (!type->byref && (((type->type >= MONO_TYPE_BOOLEAN) && (type->type <= MONO_TYPE_R8)) || (type->type == MONO_TYPE_I) || (type->type == MONO_TYPE_U)))
7718 b |= (1 << 2);
7719 if (type->type == MONO_TYPE_VALUETYPE)
7720 b |= (1 << 3);
7721 if (klass->enumtype)
7722 b |= (1 << 4);
7723 if (klass->generic_container)
7724 b |= (1 << 5);
7725 if (klass->generic_container || klass->generic_class)
7726 b |= (1 << 6);
7727 buffer_add_byte (buf, b);
7728 nnested = 0;
7729 iter = NULL;
7730 while ((nested = mono_class_get_nested_types (klass, &iter)))
7731 nnested ++;
7732 buffer_add_int (buf, nnested);
7733 iter = NULL;
7734 while ((nested = mono_class_get_nested_types (klass, &iter)))
7735 buffer_add_typeid (buf, domain, nested);
7736 if (CHECK_PROTOCOL_VERSION (2, 12)) {
7737 if (klass->generic_container)
7738 buffer_add_typeid (buf, domain, klass);
7739 else if (klass->generic_class)
7740 buffer_add_typeid (buf, domain, klass->generic_class->container_class);
7741 else
7742 buffer_add_id (buf, 0);
7744 if (CHECK_PROTOCOL_VERSION (2, 15)) {
7745 int count, i;
7747 if (klass->generic_class) {
7748 MonoGenericInst *inst = klass->generic_class->context.class_inst;
7750 count = inst->type_argc;
7751 buffer_add_int (buf, count);
7752 for (i = 0; i < count; i++)
7753 buffer_add_typeid (buf, domain, mono_class_from_mono_type (inst->type_argv [i]));
7754 } else if (klass->generic_container) {
7755 MonoGenericContainer *container = klass->generic_container;
7756 MonoClass *pklass;
7758 count = container->type_argc;
7759 buffer_add_int (buf, count);
7760 for (i = 0; i < count; i++) {
7761 pklass = mono_class_from_generic_parameter (mono_generic_container_get_param (container, i), klass->image, FALSE);
7762 buffer_add_typeid (buf, domain, pklass);
7764 } else {
7765 buffer_add_int (buf, 0);
7768 break;
7770 case CMD_TYPE_GET_METHODS: {
7771 int nmethods;
7772 int i = 0;
7773 gpointer iter = NULL;
7774 MonoMethod *m;
7776 mono_class_setup_methods (klass);
7778 nmethods = mono_class_num_methods (klass);
7780 buffer_add_int (buf, nmethods);
7782 while ((m = mono_class_get_methods (klass, &iter))) {
7783 buffer_add_methodid (buf, domain, m);
7784 i ++;
7786 g_assert (i == nmethods);
7787 break;
7789 case CMD_TYPE_GET_FIELDS: {
7790 int nfields;
7791 int i = 0;
7792 gpointer iter = NULL;
7793 MonoClassField *f;
7795 nfields = mono_class_num_fields (klass);
7797 buffer_add_int (buf, nfields);
7799 while ((f = mono_class_get_fields (klass, &iter))) {
7800 buffer_add_fieldid (buf, domain, f);
7801 buffer_add_string (buf, f->name);
7802 buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type));
7803 buffer_add_int (buf, f->type->attrs);
7804 i ++;
7806 g_assert (i == nfields);
7807 break;
7809 case CMD_TYPE_GET_PROPERTIES: {
7810 int nprops;
7811 int i = 0;
7812 gpointer iter = NULL;
7813 MonoProperty *p;
7815 nprops = mono_class_num_properties (klass);
7817 buffer_add_int (buf, nprops);
7819 while ((p = mono_class_get_properties (klass, &iter))) {
7820 buffer_add_propertyid (buf, domain, p);
7821 buffer_add_string (buf, p->name);
7822 buffer_add_methodid (buf, domain, p->get);
7823 buffer_add_methodid (buf, domain, p->set);
7824 buffer_add_int (buf, p->attrs);
7825 i ++;
7827 g_assert (i == nprops);
7828 break;
7830 case CMD_TYPE_GET_CATTRS: {
7831 MonoClass *attr_klass;
7832 MonoCustomAttrInfo *cinfo;
7834 attr_klass = decode_typeid (p, &p, end, NULL, &err);
7835 /* attr_klass can be NULL */
7836 if (err)
7837 return err;
7839 cinfo = mono_custom_attrs_from_class (klass);
7841 buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
7842 break;
7844 case CMD_TYPE_GET_FIELD_CATTRS: {
7845 MonoClass *attr_klass;
7846 MonoCustomAttrInfo *cinfo;
7847 MonoClassField *field;
7849 field = decode_fieldid (p, &p, end, NULL, &err);
7850 if (err)
7851 return err;
7852 attr_klass = decode_typeid (p, &p, end, NULL, &err);
7853 if (err)
7854 return err;
7856 cinfo = mono_custom_attrs_from_field (klass, field);
7858 buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
7859 break;
7861 case CMD_TYPE_GET_PROPERTY_CATTRS: {
7862 MonoClass *attr_klass;
7863 MonoCustomAttrInfo *cinfo;
7864 MonoProperty *prop;
7866 prop = decode_propertyid (p, &p, end, NULL, &err);
7867 if (err)
7868 return err;
7869 attr_klass = decode_typeid (p, &p, end, NULL, &err);
7870 if (err)
7871 return err;
7873 cinfo = mono_custom_attrs_from_property (klass, prop);
7875 buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
7876 break;
7878 case CMD_TYPE_GET_VALUES:
7879 case CMD_TYPE_GET_VALUES_2: {
7880 guint8 *val;
7881 MonoClassField *f;
7882 MonoVTable *vtable;
7883 MonoClass *k;
7884 int len, i;
7885 gboolean found;
7886 MonoThread *thread_obj;
7887 MonoInternalThread *thread = NULL;
7888 guint32 special_static_type;
7890 if (command == CMD_TYPE_GET_VALUES_2) {
7891 int objid = decode_objid (p, &p, end);
7892 int err;
7894 err = get_object (objid, (MonoObject**)&thread_obj);
7895 if (err)
7896 return err;
7898 thread = THREAD_TO_INTERNAL (thread_obj);
7901 len = decode_int (p, &p, end);
7902 for (i = 0; i < len; ++i) {
7903 f = decode_fieldid (p, &p, end, NULL, &err);
7904 if (err)
7905 return err;
7907 if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC))
7908 return ERR_INVALID_FIELDID;
7909 special_static_type = mono_class_field_get_special_static_type (f);
7910 if (special_static_type != SPECIAL_STATIC_NONE) {
7911 if (!(thread && special_static_type == SPECIAL_STATIC_THREAD))
7912 return ERR_INVALID_FIELDID;
7915 /* Check that the field belongs to the object */
7916 found = FALSE;
7917 for (k = klass; k; k = k->parent) {
7918 if (k == f->parent) {
7919 found = TRUE;
7920 break;
7923 if (!found)
7924 return ERR_INVALID_FIELDID;
7926 vtable = mono_class_vtable (domain, f->parent);
7927 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
7928 mono_field_static_get_value_for_thread (thread ? thread : mono_thread_internal_current (), vtable, f, val);
7929 buffer_add_value (buf, f->type, val, domain);
7930 g_free (val);
7932 break;
7934 case CMD_TYPE_SET_VALUES: {
7935 guint8 *val;
7936 MonoClassField *f;
7937 MonoVTable *vtable;
7938 MonoClass *k;
7939 int len, i;
7940 gboolean found;
7942 len = decode_int (p, &p, end);
7943 for (i = 0; i < len; ++i) {
7944 f = decode_fieldid (p, &p, end, NULL, &err);
7945 if (err)
7946 return err;
7948 if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC))
7949 return ERR_INVALID_FIELDID;
7950 if (mono_class_field_is_special_static (f))
7951 return ERR_INVALID_FIELDID;
7953 /* Check that the field belongs to the object */
7954 found = FALSE;
7955 for (k = klass; k; k = k->parent) {
7956 if (k == f->parent) {
7957 found = TRUE;
7958 break;
7961 if (!found)
7962 return ERR_INVALID_FIELDID;
7964 // FIXME: Check for literal/const
7966 vtable = mono_class_vtable (domain, f->parent);
7967 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
7968 err = decode_value (f->type, domain, val, p, &p, end);
7969 if (err) {
7970 g_free (val);
7971 return err;
7973 if (MONO_TYPE_IS_REFERENCE (f->type))
7974 mono_field_static_set_value (vtable, f, *(gpointer*)val);
7975 else
7976 mono_field_static_set_value (vtable, f, val);
7977 g_free (val);
7979 break;
7981 case CMD_TYPE_GET_OBJECT: {
7982 MonoObject *o = (MonoObject*)mono_type_get_object (domain, &klass->byval_arg);
7983 buffer_add_objid (buf, o);
7984 break;
7986 case CMD_TYPE_GET_SOURCE_FILES:
7987 case CMD_TYPE_GET_SOURCE_FILES_2: {
7988 char *source_file, *base;
7989 GPtrArray *files;
7990 int i;
7992 files = get_source_files_for_type (klass);
7994 buffer_add_int (buf, files->len);
7995 for (i = 0; i < files->len; ++i) {
7996 source_file = g_ptr_array_index (files, i);
7997 if (command == CMD_TYPE_GET_SOURCE_FILES_2) {
7998 buffer_add_string (buf, source_file);
7999 } else {
8000 base = g_path_get_basename (source_file);
8001 buffer_add_string (buf, base);
8002 g_free (base);
8004 g_free (source_file);
8006 g_ptr_array_free (files, TRUE);
8007 break;
8009 case CMD_TYPE_IS_ASSIGNABLE_FROM: {
8010 MonoClass *oklass = decode_typeid (p, &p, end, NULL, &err);
8012 if (err)
8013 return err;
8014 if (mono_class_is_assignable_from (klass, oklass))
8015 buffer_add_byte (buf, 1);
8016 else
8017 buffer_add_byte (buf, 0);
8018 break;
8020 case CMD_TYPE_GET_METHODS_BY_NAME_FLAGS: {
8021 char *name = decode_string (p, &p, end);
8022 int i, flags = decode_int (p, &p, end);
8023 MonoException *ex = NULL;
8024 GPtrArray *array = mono_class_get_methods_by_name (klass, name, flags & ~BINDING_FLAGS_IGNORE_CASE, (flags & BINDING_FLAGS_IGNORE_CASE) != 0, TRUE, &ex);
8026 if (!array)
8027 return ERR_LOADER_ERROR;
8028 buffer_add_int (buf, array->len);
8029 for (i = 0; i < array->len; ++i) {
8030 MonoMethod *method = g_ptr_array_index (array, i);
8031 buffer_add_methodid (buf, domain, method);
8034 g_ptr_array_free (array, TRUE);
8035 g_free (name);
8036 break;
8038 case CMD_TYPE_GET_INTERFACES: {
8039 MonoClass *parent;
8040 GHashTable *iface_hash = g_hash_table_new (NULL, NULL);
8041 MonoError error;
8042 MonoClass *tclass, *iface;
8043 GHashTableIter iter;
8045 tclass = klass;
8047 for (parent = tclass; parent; parent = parent->parent) {
8048 mono_class_setup_interfaces (parent, &error);
8049 if (!mono_error_ok (&error))
8050 return ERR_LOADER_ERROR;
8051 collect_interfaces (parent, iface_hash, &error);
8052 if (!mono_error_ok (&error))
8053 return ERR_LOADER_ERROR;
8056 buffer_add_int (buf, g_hash_table_size (iface_hash));
8058 g_hash_table_iter_init (&iter, iface_hash);
8059 while (g_hash_table_iter_next (&iter, NULL, (void**)&iface))
8060 buffer_add_typeid (buf, domain, iface);
8061 g_hash_table_destroy (iface_hash);
8062 break;
8064 case CMD_TYPE_GET_INTERFACE_MAP: {
8065 int tindex, ioffset;
8066 gboolean variance_used;
8067 MonoClass *iclass;
8068 int len, nmethods, i;
8069 gpointer iter;
8070 MonoMethod *method;
8072 len = decode_int (p, &p, end);
8073 mono_class_setup_vtable (klass);
8075 for (tindex = 0; tindex < len; ++tindex) {
8076 iclass = decode_typeid (p, &p, end, NULL, &err);
8077 if (err)
8078 return err;
8080 ioffset = mono_class_interface_offset_with_variance (klass, iclass, &variance_used);
8081 if (ioffset == -1)
8082 return ERR_INVALID_ARGUMENT;
8084 nmethods = mono_class_num_methods (iclass);
8085 buffer_add_int (buf, nmethods);
8087 iter = NULL;
8088 while ((method = mono_class_get_methods (iclass, &iter))) {
8089 buffer_add_methodid (buf, domain, method);
8091 for (i = 0; i < nmethods; ++i)
8092 buffer_add_methodid (buf, domain, klass->vtable [i + ioffset]);
8094 break;
8096 case CMD_TYPE_IS_INITIALIZED: {
8097 MonoVTable *vtable = mono_class_vtable (domain, klass);
8099 if (vtable)
8100 buffer_add_int (buf, (vtable->initialized || vtable->init_failed) ? 1 : 0);
8101 else
8102 buffer_add_int (buf, 0);
8103 break;
8105 case CMD_TYPE_CREATE_INSTANCE: {
8106 MonoObject *obj;
8108 obj = mono_object_new (domain, klass);
8109 buffer_add_objid (buf, obj);
8110 break;
8112 default:
8113 return ERR_NOT_IMPLEMENTED;
8116 return ERR_NONE;
8119 static ErrorCode
8120 type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
8122 MonoClass *klass;
8123 MonoDomain *old_domain;
8124 MonoDomain *domain;
8125 int err;
8127 klass = decode_typeid (p, &p, end, &domain, &err);
8128 if (err)
8129 return err;
8131 old_domain = mono_domain_get ();
8133 mono_domain_set (domain, TRUE);
8135 err = type_commands_internal (command, klass, domain, p, end, buf);
8137 mono_domain_set (old_domain, TRUE);
8139 return err;
8142 static ErrorCode
8143 method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf)
8145 MonoMethodHeader *header;
8146 int err;
8148 switch (command) {
8149 case CMD_METHOD_GET_NAME: {
8150 buffer_add_string (buf, method->name);
8151 break;
8153 case CMD_METHOD_GET_DECLARING_TYPE: {
8154 buffer_add_typeid (buf, domain, method->klass);
8155 break;
8157 case CMD_METHOD_GET_DEBUG_INFO: {
8158 MonoDebugMethodInfo *minfo;
8159 char *source_file;
8160 int i, j, n_il_offsets;
8161 int *il_offsets;
8162 int *line_numbers;
8163 int *column_numbers;
8164 int *end_line_numbers;
8165 int *end_column_numbers;
8166 int *source_files;
8167 GPtrArray *source_file_list;
8169 header = mono_method_get_header (method);
8170 if (!header) {
8171 buffer_add_int (buf, 0);
8172 buffer_add_string (buf, "");
8173 buffer_add_int (buf, 0);
8174 break;
8177 minfo = mono_debug_lookup_method (method);
8178 if (!minfo) {
8179 buffer_add_int (buf, header->code_size);
8180 buffer_add_string (buf, "");
8181 buffer_add_int (buf, 0);
8182 mono_metadata_free_mh (header);
8183 break;
8186 mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, &n_il_offsets, &il_offsets, &line_numbers, &column_numbers, &source_files, &end_line_numbers, &end_column_numbers);
8187 buffer_add_int (buf, header->code_size);
8188 if (CHECK_PROTOCOL_VERSION (2, 13)) {
8189 buffer_add_int (buf, source_file_list->len);
8190 for (i = 0; i < source_file_list->len; ++i) {
8191 MonoDebugSourceInfo *sinfo = g_ptr_array_index (source_file_list, i);
8192 buffer_add_string (buf, sinfo->source_file);
8193 if (CHECK_PROTOCOL_VERSION (2, 14)) {
8194 for (j = 0; j < 16; ++j)
8195 buffer_add_byte (buf, sinfo->hash [j]);
8198 } else {
8199 buffer_add_string (buf, source_file);
8201 buffer_add_int (buf, n_il_offsets);
8202 DEBUG (10, fprintf (log_file, "Line number table for method %s:\n", mono_method_full_name (method, TRUE)));
8203 for (i = 0; i < n_il_offsets; ++i) {
8204 const char *srcfile = "";
8206 if (source_files [i] != -1) {
8207 MonoDebugSourceInfo *sinfo = g_ptr_array_index (source_file_list, source_files [i]);
8208 srcfile = sinfo->source_file;
8210 DEBUG (10, fprintf (log_file, "IL%x -> %s:%d %d\n", il_offsets [i], srcfile, line_numbers [i], column_numbers ? column_numbers [i] : -1));
8211 buffer_add_int (buf, il_offsets [i]);
8212 buffer_add_int (buf, line_numbers [i]);
8213 if (CHECK_PROTOCOL_VERSION (2, 13))
8214 buffer_add_int (buf, source_files [i]);
8215 if (CHECK_PROTOCOL_VERSION (2, 19))
8216 buffer_add_int (buf, column_numbers ? column_numbers [i] : -1);
8217 if (CHECK_PROTOCOL_VERSION (2, 32)) {
8218 buffer_add_int (buf, end_line_numbers ? end_line_numbers [i] : -1);
8219 buffer_add_int (buf, end_column_numbers ? end_column_numbers [i] : -1);
8222 g_free (source_file);
8223 g_free (il_offsets);
8224 g_free (line_numbers);
8225 g_free (column_numbers);
8226 g_free (end_line_numbers);
8227 g_free (end_column_numbers);
8228 g_free (source_files);
8229 g_ptr_array_free (source_file_list, TRUE);
8230 mono_metadata_free_mh (header);
8231 break;
8233 case CMD_METHOD_GET_PARAM_INFO: {
8234 MonoMethodSignature *sig = mono_method_signature (method);
8235 guint32 i;
8236 char **names;
8238 /* FIXME: mono_class_from_mono_type () and byrefs */
8240 /* FIXME: Use a smaller encoding */
8241 buffer_add_int (buf, sig->call_convention);
8242 buffer_add_int (buf, sig->param_count);
8243 buffer_add_int (buf, sig->generic_param_count);
8244 buffer_add_typeid (buf, domain, mono_class_from_mono_type (sig->ret));
8245 for (i = 0; i < sig->param_count; ++i) {
8246 /* FIXME: vararg */
8247 buffer_add_typeid (buf, domain, mono_class_from_mono_type (sig->params [i]));
8250 /* Emit parameter names */
8251 names = g_new (char *, sig->param_count);
8252 mono_method_get_param_names (method, (const char **) names);
8253 for (i = 0; i < sig->param_count; ++i)
8254 buffer_add_string (buf, names [i]);
8255 g_free (names);
8257 break;
8259 case CMD_METHOD_GET_LOCALS_INFO: {
8260 int i, j, num_locals;
8261 MonoDebugLocalsInfo *locals;
8263 header = mono_method_get_header (method);
8264 if (!header)
8265 return ERR_INVALID_ARGUMENT;
8267 buffer_add_int (buf, header->num_locals);
8269 /* Types */
8270 for (i = 0; i < header->num_locals; ++i)
8271 buffer_add_typeid (buf, domain, mono_class_from_mono_type (header->locals [i]));
8273 /* Names */
8274 locals = mono_debug_lookup_locals (method);
8275 if (locals)
8276 num_locals = locals->num_locals;
8277 else
8278 num_locals = 0;
8279 for (i = 0; i < header->num_locals; ++i) {
8280 for (j = 0; j < num_locals; ++j)
8281 if (locals->locals [j].index == i)
8282 break;
8283 if (j < num_locals)
8284 buffer_add_string (buf, locals->locals [j].name);
8285 else
8286 buffer_add_string (buf, "");
8289 /* Scopes */
8290 for (i = 0; i < header->num_locals; ++i) {
8291 for (j = 0; j < num_locals; ++j)
8292 if (locals->locals [j].index == i)
8293 break;
8294 if (j < num_locals && locals->locals [j].block) {
8295 buffer_add_int (buf, locals->locals [j].block->start_offset);
8296 buffer_add_int (buf, locals->locals [j].block->end_offset);
8297 } else {
8298 buffer_add_int (buf, 0);
8299 buffer_add_int (buf, header->code_size);
8302 mono_metadata_free_mh (header);
8304 if (locals)
8305 mono_debug_symfile_free_locals (locals);
8307 break;
8309 case CMD_METHOD_GET_INFO:
8310 buffer_add_int (buf, method->flags);
8311 buffer_add_int (buf, method->iflags);
8312 buffer_add_int (buf, method->token);
8313 if (CHECK_PROTOCOL_VERSION (2, 12)) {
8314 guint8 attrs = 0;
8315 if (method->is_generic)
8316 attrs |= (1 << 0);
8317 if (mono_method_signature (method)->generic_param_count)
8318 attrs |= (1 << 1);
8319 buffer_add_byte (buf, attrs);
8320 if (method->is_generic || method->is_inflated) {
8321 MonoMethod *result;
8323 if (method->is_generic) {
8324 result = method;
8325 } else {
8326 MonoMethodInflated *imethod = (MonoMethodInflated *)method;
8328 result = imethod->declaring;
8329 if (imethod->context.class_inst) {
8330 MonoClass *klass = ((MonoMethod *) imethod)->klass;
8331 /*Generic methods gets the context of the GTD.*/
8332 if (mono_class_get_context (klass))
8333 result = mono_class_inflate_generic_method_full (result, klass, mono_class_get_context (klass));
8337 buffer_add_methodid (buf, domain, result);
8338 } else {
8339 buffer_add_id (buf, 0);
8341 if (CHECK_PROTOCOL_VERSION (2, 15)) {
8342 if (mono_method_signature (method)->generic_param_count) {
8343 int count, i;
8345 if (method->is_inflated) {
8346 MonoGenericInst *inst = mono_method_get_context (method)->method_inst;
8347 if (inst) {
8348 count = inst->type_argc;
8349 buffer_add_int (buf, count);
8351 for (i = 0; i < count; i++)
8352 buffer_add_typeid (buf, domain, mono_class_from_mono_type (inst->type_argv [i]));
8353 } else {
8354 buffer_add_int (buf, 0);
8356 } else if (method->is_generic) {
8357 MonoGenericContainer *container = mono_method_get_generic_container (method);
8359 count = mono_method_signature (method)->generic_param_count;
8360 buffer_add_int (buf, count);
8361 for (i = 0; i < count; i++) {
8362 MonoGenericParam *param = mono_generic_container_get_param (container, i);
8363 MonoClass *pklass = mono_class_from_generic_parameter (param, method->klass->image, TRUE);
8364 buffer_add_typeid (buf, domain, pklass);
8366 } else {
8367 buffer_add_int (buf, 0);
8369 } else {
8370 buffer_add_int (buf, 0);
8374 break;
8375 case CMD_METHOD_GET_BODY: {
8376 int i;
8378 header = mono_method_get_header (method);
8379 if (!header) {
8380 buffer_add_int (buf, 0);
8382 if (CHECK_PROTOCOL_VERSION (2, 18))
8383 buffer_add_int (buf, 0);
8384 } else {
8385 buffer_add_int (buf, header->code_size);
8386 for (i = 0; i < header->code_size; ++i)
8387 buffer_add_byte (buf, header->code [i]);
8389 if (CHECK_PROTOCOL_VERSION (2, 18)) {
8390 buffer_add_int (buf, header->num_clauses);
8391 for (i = 0; i < header->num_clauses; ++i) {
8392 MonoExceptionClause *clause = &header->clauses [i];
8394 buffer_add_int (buf, clause->flags);
8395 buffer_add_int (buf, clause->try_offset);
8396 buffer_add_int (buf, clause->try_len);
8397 buffer_add_int (buf, clause->handler_offset);
8398 buffer_add_int (buf, clause->handler_len);
8399 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE)
8400 buffer_add_typeid (buf, domain, clause->data.catch_class);
8401 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER)
8402 buffer_add_int (buf, clause->data.filter_offset);
8406 mono_metadata_free_mh (header);
8409 break;
8411 case CMD_METHOD_RESOLVE_TOKEN: {
8412 guint32 token = decode_int (p, &p, end);
8414 // FIXME: Generics
8415 switch (mono_metadata_token_code (token)) {
8416 case MONO_TOKEN_STRING: {
8417 MonoString *s;
8418 char *s2;
8420 s = mono_ldstr (domain, method->klass->image, mono_metadata_token_index (token));
8421 g_assert (s);
8423 s2 = mono_string_to_utf8 (s);
8425 buffer_add_byte (buf, TOKEN_TYPE_STRING);
8426 buffer_add_string (buf, s2);
8427 g_free (s2);
8428 break;
8430 default: {
8431 gpointer val;
8432 MonoClass *handle_class;
8434 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
8435 val = mono_method_get_wrapper_data (method, token);
8436 handle_class = mono_method_get_wrapper_data (method, token + 1);
8438 if (handle_class == NULL) {
8439 // Can't figure out the token type
8440 buffer_add_byte (buf, TOKEN_TYPE_UNKNOWN);
8441 break;
8443 } else {
8444 val = mono_ldtoken (method->klass->image, token, &handle_class, NULL);
8445 g_assert (val);
8448 if (handle_class == mono_defaults.typehandle_class) {
8449 buffer_add_byte (buf, TOKEN_TYPE_TYPE);
8450 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
8451 buffer_add_typeid (buf, domain, (MonoClass *) val);
8452 else
8453 buffer_add_typeid (buf, domain, mono_class_from_mono_type ((MonoType*)val));
8454 } else if (handle_class == mono_defaults.fieldhandle_class) {
8455 buffer_add_byte (buf, TOKEN_TYPE_FIELD);
8456 buffer_add_fieldid (buf, domain, val);
8457 } else if (handle_class == mono_defaults.methodhandle_class) {
8458 buffer_add_byte (buf, TOKEN_TYPE_METHOD);
8459 buffer_add_methodid (buf, domain, val);
8460 } else if (handle_class == mono_defaults.string_class) {
8461 char *s;
8463 s = mono_string_to_utf8 (val);
8464 buffer_add_byte (buf, TOKEN_TYPE_STRING);
8465 buffer_add_string (buf, s);
8466 g_free (s);
8467 } else {
8468 g_assert_not_reached ();
8470 break;
8473 break;
8475 case CMD_METHOD_GET_CATTRS: {
8476 MonoClass *attr_klass;
8477 MonoCustomAttrInfo *cinfo;
8479 attr_klass = decode_typeid (p, &p, end, NULL, &err);
8480 /* attr_klass can be NULL */
8481 if (err)
8482 return err;
8484 cinfo = mono_custom_attrs_from_method (method);
8486 buffer_add_cattrs (buf, domain, method->klass->image, attr_klass, cinfo);
8487 break;
8489 case CMD_METHOD_MAKE_GENERIC_METHOD: {
8490 MonoType **type_argv;
8491 int i, type_argc;
8492 MonoDomain *d;
8493 MonoClass *klass;
8494 MonoGenericInst *ginst;
8495 MonoGenericContext tmp_context;
8496 MonoMethod *inflated;
8498 type_argc = decode_int (p, &p, end);
8499 type_argv = g_new0 (MonoType*, type_argc);
8500 for (i = 0; i < type_argc; ++i) {
8501 klass = decode_typeid (p, &p, end, &d, &err);
8502 if (err) {
8503 g_free (type_argv);
8504 return err;
8506 if (domain != d) {
8507 g_free (type_argv);
8508 return ERR_INVALID_ARGUMENT;
8510 type_argv [i] = &klass->byval_arg;
8512 ginst = mono_metadata_get_generic_inst (type_argc, type_argv);
8513 g_free (type_argv);
8514 tmp_context.class_inst = method->klass->generic_class ? method->klass->generic_class->context.class_inst : NULL;
8515 tmp_context.method_inst = ginst;
8517 inflated = mono_class_inflate_generic_method (method, &tmp_context);
8518 if (!mono_verifier_is_method_valid_generic_instantiation (inflated))
8519 return ERR_INVALID_ARGUMENT;
8520 buffer_add_methodid (buf, domain, inflated);
8521 break;
8523 default:
8524 return ERR_NOT_IMPLEMENTED;
8527 return ERR_NONE;
8530 static ErrorCode
8531 method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
8533 int err;
8534 MonoDomain *old_domain;
8535 MonoDomain *domain;
8536 MonoMethod *method;
8538 method = decode_methodid (p, &p, end, &domain, &err);
8539 if (err)
8540 return err;
8542 old_domain = mono_domain_get ();
8544 mono_domain_set (domain, TRUE);
8546 err = method_commands_internal (command, method, domain, p, end, buf);
8548 mono_domain_set (old_domain, TRUE);
8550 return err;
8553 static ErrorCode
8554 thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
8556 int objid = decode_objid (p, &p, end);
8557 int err;
8558 MonoThread *thread_obj;
8559 MonoInternalThread *thread;
8561 err = get_object (objid, (MonoObject**)&thread_obj);
8562 if (err)
8563 return err;
8565 thread = THREAD_TO_INTERNAL (thread_obj);
8567 switch (command) {
8568 case CMD_THREAD_GET_NAME: {
8569 guint32 name_len;
8570 gunichar2 *s = mono_thread_get_name (thread, &name_len);
8572 if (!s) {
8573 buffer_add_int (buf, 0);
8574 } else {
8575 char *name;
8576 glong len;
8578 name = g_utf16_to_utf8 (s, name_len, NULL, &len, NULL);
8579 g_assert (name);
8580 buffer_add_int (buf, len);
8581 buffer_add_data (buf, (guint8*)name, len);
8582 g_free (s);
8584 break;
8586 case CMD_THREAD_GET_FRAME_INFO: {
8587 DebuggerTlsData *tls;
8588 int i, start_frame, length;
8590 // Wait for suspending if it already started
8591 // FIXME: Races with suspend_count
8592 while (!is_suspended ()) {
8593 if (suspend_count)
8594 wait_for_suspend ();
8597 if (suspend_count)
8598 wait_for_suspend ();
8599 if (!is_suspended ())
8600 return ERR_NOT_SUSPENDED;
8603 start_frame = decode_int (p, &p, end);
8604 length = decode_int (p, &p, end);
8606 if (start_frame != 0 || length != -1)
8607 return ERR_NOT_IMPLEMENTED;
8609 mono_loader_lock ();
8610 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
8611 mono_loader_unlock ();
8612 g_assert (tls);
8614 compute_frame_info (thread, tls);
8616 buffer_add_int (buf, tls->frame_count);
8617 for (i = 0; i < tls->frame_count; ++i) {
8618 buffer_add_int (buf, tls->frames [i]->id);
8619 buffer_add_methodid (buf, tls->frames [i]->domain, tls->frames [i]->actual_method);
8620 buffer_add_int (buf, tls->frames [i]->il_offset);
8622 * Instead of passing the frame type directly to the client, we associate
8623 * it with the previous frame using a set of flags. This avoids lots of
8624 * conditional code in the client, since a frame whose type isn't
8625 * FRAME_TYPE_MANAGED has no method, location, etc.
8627 buffer_add_byte (buf, tls->frames [i]->flags);
8630 break;
8632 case CMD_THREAD_GET_STATE:
8633 buffer_add_int (buf, thread->state);
8634 break;
8635 case CMD_THREAD_GET_INFO:
8636 buffer_add_byte (buf, thread->threadpool_thread);
8637 break;
8638 case CMD_THREAD_GET_ID:
8639 buffer_add_long (buf, (guint64)(gsize)thread);
8640 break;
8641 case CMD_THREAD_GET_TID:
8642 buffer_add_long (buf, (guint64)thread->tid);
8643 break;
8644 case CMD_THREAD_SET_IP: {
8645 DebuggerTlsData *tls;
8646 MonoMethod *method;
8647 MonoDomain *domain;
8648 MonoSeqPointInfo *seq_points;
8649 SeqPoint *sp = NULL;
8650 gint64 il_offset;
8651 int i;
8653 method = decode_methodid (p, &p, end, &domain, &err);
8654 if (err)
8655 return err;
8656 il_offset = decode_long (p, &p, end);
8658 while (!is_suspended ()) {
8659 if (suspend_count)
8660 wait_for_suspend ();
8663 mono_loader_lock ();
8664 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
8665 mono_loader_unlock ();
8666 g_assert (tls);
8668 compute_frame_info (thread, tls);
8669 if (tls->frame_count == 0 || tls->frames [0]->actual_method != method)
8670 return ERR_INVALID_ARGUMENT;
8672 seq_points = get_seq_points (domain, method);
8673 g_assert (seq_points);
8675 for (i = 0; i < seq_points->len; ++i) {
8676 sp = &seq_points->seq_points [i];
8678 if (sp->il_offset == il_offset)
8679 break;
8681 if (i == seq_points->len)
8682 return ERR_INVALID_ARGUMENT;
8684 // FIXME: Check that the ip change is safe
8686 DEBUG (1, fprintf (log_file, "[dbg] Setting IP to %s:0x%0x(0x%0x)\n", tls->frames [0]->actual_method->name, (int)sp->il_offset, (int)sp->native_offset));
8687 MONO_CONTEXT_SET_IP (&tls->restore_ctx, (guint8*)tls->frames [0]->ji->code_start + sp->native_offset);
8688 break;
8690 default:
8691 return ERR_NOT_IMPLEMENTED;
8694 return ERR_NONE;
8697 static ErrorCode
8698 frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
8700 int objid;
8701 int err;
8702 MonoThread *thread_obj;
8703 MonoInternalThread *thread;
8704 int pos, i, len, frame_idx;
8705 DebuggerTlsData *tls;
8706 StackFrame *frame;
8707 MonoDebugMethodJitInfo *jit;
8708 MonoDebugVarInfo *var;
8709 MonoMethodSignature *sig;
8710 gssize id;
8711 MonoMethodHeader *header;
8713 objid = decode_objid (p, &p, end);
8714 err = get_object (objid, (MonoObject**)&thread_obj);
8715 if (err)
8716 return err;
8718 thread = THREAD_TO_INTERNAL (thread_obj);
8720 id = decode_id (p, &p, end);
8722 mono_loader_lock ();
8723 tls = mono_g_hash_table_lookup (thread_to_tls, thread);
8724 mono_loader_unlock ();
8725 g_assert (tls);
8727 for (i = 0; i < tls->frame_count; ++i) {
8728 if (tls->frames [i]->id == id)
8729 break;
8731 if (i == tls->frame_count)
8732 return ERR_INVALID_FRAMEID;
8734 frame_idx = i;
8735 frame = tls->frames [frame_idx];
8737 if (!frame->has_ctx)
8738 return ERR_ABSENT_INFORMATION;
8740 if (!frame->jit) {
8741 frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
8742 if (!frame->jit && frame->api_method->is_inflated)
8743 frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
8744 if (!frame->jit) {
8745 char *s;
8747 /* This could happen for aot images with no jit debug info */
8748 s = mono_method_full_name (frame->api_method, TRUE);
8749 DEBUG (1, fprintf (log_file, "[dbg] No debug information found for '%s'.\n", s));
8750 g_free (s);
8751 return ERR_ABSENT_INFORMATION;
8754 jit = frame->jit;
8756 sig = mono_method_signature (frame->actual_method);
8758 if (!get_seq_points (frame->domain, frame->actual_method))
8760 * The method is probably from an aot image compiled without soft-debug, variables might be dead, etc.
8762 return ERR_ABSENT_INFORMATION;
8764 switch (command) {
8765 case CMD_STACK_FRAME_GET_VALUES: {
8766 len = decode_int (p, &p, end);
8767 header = mono_method_get_header (frame->actual_method);
8769 for (i = 0; i < len; ++i) {
8770 pos = decode_int (p, &p, end);
8772 if (pos < 0) {
8773 pos = - pos - 1;
8775 g_assert (pos >= 0 && pos < jit->num_params);
8777 var = &jit->params [pos];
8779 add_var (buf, jit, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
8780 } else {
8781 g_assert (pos >= 0 && pos < jit->num_locals);
8783 var = &jit->locals [pos];
8785 add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
8788 mono_metadata_free_mh (header);
8789 break;
8791 case CMD_STACK_FRAME_GET_THIS: {
8792 if (frame->api_method->klass->valuetype) {
8793 if (!sig->hasthis) {
8794 MonoObject *p = NULL;
8795 buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
8796 } else {
8797 add_var (buf, jit, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
8799 } else {
8800 if (!sig->hasthis) {
8801 MonoObject *p = NULL;
8802 buffer_add_value (buf, &frame->actual_method->klass->byval_arg, &p, frame->domain);
8803 } else {
8804 add_var (buf, jit, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
8807 break;
8809 case CMD_STACK_FRAME_SET_VALUES: {
8810 guint8 *val_buf;
8811 MonoType *t;
8812 MonoDebugVarInfo *var;
8814 len = decode_int (p, &p, end);
8815 header = mono_method_get_header (frame->actual_method);
8817 for (i = 0; i < len; ++i) {
8818 pos = decode_int (p, &p, end);
8820 if (pos < 0) {
8821 pos = - pos - 1;
8823 g_assert (pos >= 0 && pos < jit->num_params);
8825 t = sig->params [pos];
8826 var = &jit->params [pos];
8827 } else {
8828 g_assert (pos >= 0 && pos < jit->num_locals);
8830 t = header->locals [pos];
8831 var = &jit->locals [pos];
8834 if (MONO_TYPE_IS_REFERENCE (t))
8835 val_buf = g_alloca (sizeof (MonoObject*));
8836 else
8837 val_buf = g_alloca (mono_class_instance_size (mono_class_from_mono_type (t)));
8838 err = decode_value (t, frame->domain, val_buf, p, &p, end);
8839 if (err)
8840 return err;
8842 set_var (t, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_ctx);
8844 mono_metadata_free_mh (header);
8845 break;
8847 default:
8848 return ERR_NOT_IMPLEMENTED;
8851 return ERR_NONE;
8854 static ErrorCode
8855 array_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
8857 MonoArray *arr;
8858 int objid, err, index, len, i, esize;
8859 gpointer elem;
8861 objid = decode_objid (p, &p, end);
8862 err = get_object (objid, (MonoObject**)&arr);
8863 if (err)
8864 return err;
8866 switch (command) {
8867 case CMD_ARRAY_REF_GET_LENGTH:
8868 buffer_add_int (buf, arr->obj.vtable->klass->rank);
8869 if (!arr->bounds) {
8870 buffer_add_int (buf, arr->max_length);
8871 buffer_add_int (buf, 0);
8872 } else {
8873 for (i = 0; i < arr->obj.vtable->klass->rank; ++i) {
8874 buffer_add_int (buf, arr->bounds [i].length);
8875 buffer_add_int (buf, arr->bounds [i].lower_bound);
8878 break;
8879 case CMD_ARRAY_REF_GET_VALUES:
8880 index = decode_int (p, &p, end);
8881 len = decode_int (p, &p, end);
8883 g_assert (index >= 0 && len >= 0);
8884 // Reordered to avoid integer overflow
8885 g_assert (!(index > arr->max_length - len));
8887 esize = mono_array_element_size (arr->obj.vtable->klass);
8888 for (i = index; i < index + len; ++i) {
8889 elem = (gpointer*)((char*)arr->vector + (i * esize));
8890 buffer_add_value (buf, &arr->obj.vtable->klass->element_class->byval_arg, elem, arr->obj.vtable->domain);
8892 break;
8893 case CMD_ARRAY_REF_SET_VALUES:
8894 index = decode_int (p, &p, end);
8895 len = decode_int (p, &p, end);
8897 g_assert (index >= 0 && len >= 0);
8898 // Reordered to avoid integer overflow
8899 g_assert (!(index > arr->max_length - len));
8901 esize = mono_array_element_size (arr->obj.vtable->klass);
8902 for (i = index; i < index + len; ++i) {
8903 elem = (gpointer*)((char*)arr->vector + (i * esize));
8905 decode_value (&arr->obj.vtable->klass->element_class->byval_arg, arr->obj.vtable->domain, elem, p, &p, end);
8907 break;
8908 default:
8909 return ERR_NOT_IMPLEMENTED;
8912 return ERR_NONE;
8915 static ErrorCode
8916 string_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
8918 int objid, err;
8919 MonoString *str;
8920 char *s;
8921 int i, index, length;
8922 gunichar2 *c;
8924 objid = decode_objid (p, &p, end);
8925 err = get_object (objid, (MonoObject**)&str);
8926 if (err)
8927 return err;
8929 switch (command) {
8930 case CMD_STRING_REF_GET_VALUE:
8931 s = mono_string_to_utf8 (str);
8932 buffer_add_string (buf, s);
8933 g_free (s);
8934 break;
8935 case CMD_STRING_REF_GET_LENGTH:
8936 buffer_add_long (buf, mono_string_length (str));
8937 break;
8938 case CMD_STRING_REF_GET_CHARS:
8939 index = decode_long (p, &p, end);
8940 length = decode_long (p, &p, end);
8941 if (index > mono_string_length (str) - length)
8942 return ERR_INVALID_ARGUMENT;
8943 c = mono_string_chars (str) + index;
8944 for (i = 0; i < length; ++i)
8945 buffer_add_short (buf, c [i]);
8946 break;
8947 default:
8948 return ERR_NOT_IMPLEMENTED;
8951 return ERR_NONE;
8954 static ErrorCode
8955 object_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
8957 int objid, err;
8958 MonoObject *obj;
8959 int len, i;
8960 MonoClassField *f;
8961 MonoClass *k;
8962 gboolean found;
8964 if (command == CMD_OBJECT_REF_IS_COLLECTED) {
8965 objid = decode_objid (p, &p, end);
8966 err = get_object (objid, &obj);
8967 if (err)
8968 buffer_add_int (buf, 1);
8969 else
8970 buffer_add_int (buf, 0);
8971 return 0;
8974 objid = decode_objid (p, &p, end);
8975 err = get_object (objid, &obj);
8976 if (err)
8977 return err;
8979 switch (command) {
8980 case CMD_OBJECT_REF_GET_TYPE:
8981 /* This handles transparent proxies too */
8982 buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type));
8983 break;
8984 case CMD_OBJECT_REF_GET_VALUES:
8985 len = decode_int (p, &p, end);
8987 for (i = 0; i < len; ++i) {
8988 MonoClassField *f = decode_fieldid (p, &p, end, NULL, &err);
8989 if (err)
8990 return err;
8992 /* Check that the field belongs to the object */
8993 found = FALSE;
8994 for (k = obj->vtable->klass; k; k = k->parent) {
8995 if (k == f->parent) {
8996 found = TRUE;
8997 break;
9000 if (!found)
9001 return ERR_INVALID_FIELDID;
9003 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) {
9004 guint8 *val;
9005 MonoVTable *vtable;
9007 if (mono_class_field_is_special_static (f))
9008 return ERR_INVALID_FIELDID;
9010 g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC);
9011 vtable = mono_class_vtable (obj->vtable->domain, f->parent);
9012 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
9013 mono_field_static_get_value (vtable, f, val);
9014 buffer_add_value (buf, f->type, val, obj->vtable->domain);
9015 g_free (val);
9016 } else {
9017 buffer_add_value (buf, f->type, (guint8*)obj + f->offset, obj->vtable->domain);
9020 break;
9021 case CMD_OBJECT_REF_SET_VALUES:
9022 len = decode_int (p, &p, end);
9024 for (i = 0; i < len; ++i) {
9025 f = decode_fieldid (p, &p, end, NULL, &err);
9026 if (err)
9027 return err;
9029 /* Check that the field belongs to the object */
9030 found = FALSE;
9031 for (k = obj->vtable->klass; k; k = k->parent) {
9032 if (k == f->parent) {
9033 found = TRUE;
9034 break;
9037 if (!found)
9038 return ERR_INVALID_FIELDID;
9040 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) {
9041 guint8 *val;
9042 MonoVTable *vtable;
9044 if (mono_class_field_is_special_static (f))
9045 return ERR_INVALID_FIELDID;
9047 g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC);
9048 vtable = mono_class_vtable (obj->vtable->domain, f->parent);
9050 val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
9051 err = decode_value (f->type, obj->vtable->domain, val, p, &p, end);
9052 if (err) {
9053 g_free (val);
9054 return err;
9056 mono_field_static_set_value (vtable, f, val);
9057 g_free (val);
9058 } else {
9059 err = decode_value (f->type, obj->vtable->domain, (guint8*)obj + f->offset, p, &p, end);
9060 if (err)
9061 return err;
9064 break;
9065 case CMD_OBJECT_REF_GET_ADDRESS:
9066 buffer_add_long (buf, (gssize)obj);
9067 break;
9068 case CMD_OBJECT_REF_GET_DOMAIN:
9069 buffer_add_domainid (buf, obj->vtable->domain);
9070 break;
9071 case CMD_OBJECT_REF_GET_INFO:
9072 buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type));
9073 buffer_add_domainid (buf, obj->vtable->domain);
9074 break;
9075 default:
9076 return ERR_NOT_IMPLEMENTED;
9079 return ERR_NONE;
9082 static const char*
9083 command_set_to_string (CommandSet command_set)
9085 switch (command_set) {
9086 case CMD_SET_VM:
9087 return "VM";
9088 case CMD_SET_OBJECT_REF:
9089 return "OBJECT_REF";
9090 case CMD_SET_STRING_REF:
9091 return "STRING_REF";
9092 case CMD_SET_THREAD:
9093 return "THREAD";
9094 case CMD_SET_ARRAY_REF:
9095 return "ARRAY_REF";
9096 case CMD_SET_EVENT_REQUEST:
9097 return "EVENT_REQUEST";
9098 case CMD_SET_STACK_FRAME:
9099 return "STACK_FRAME";
9100 case CMD_SET_APPDOMAIN:
9101 return "APPDOMAIN";
9102 case CMD_SET_ASSEMBLY:
9103 return "ASSEMBLY";
9104 case CMD_SET_METHOD:
9105 return "METHOD";
9106 case CMD_SET_TYPE:
9107 return "TYPE";
9108 case CMD_SET_MODULE:
9109 return "MODULE";
9110 case CMD_SET_FIELD:
9111 return "FIELD";
9112 case CMD_SET_EVENT:
9113 return "EVENT";
9114 default:
9115 return "";
9119 static const char* vm_cmds_str [] = {
9120 "VERSION",
9121 "ALL_THREADS",
9122 "SUSPEND",
9123 "RESUME",
9124 "EXIT",
9125 "DISPOSE",
9126 "INVOKE_METHOD",
9127 "SET_PROTOCOL_VERSION",
9128 "ABORT_INVOKE",
9129 "SET_KEEPALIVE"
9130 "GET_TYPES_FOR_SOURCE_FILE",
9131 "GET_TYPES",
9132 "INVOKE_METHODS"
9135 static const char* thread_cmds_str[] = {
9136 "GET_FRAME_INFO",
9137 "GET_NAME",
9138 "GET_STATE",
9139 "GET_INFO",
9140 "GET_ID",
9141 "GET_TID",
9142 "SET_IP"
9145 static const char* event_cmds_str[] = {
9146 "REQUEST_SET",
9147 "REQUEST_CLEAR",
9148 "REQUEST_CLEAR_ALL_BREAKPOINTS"
9151 static const char* appdomain_cmds_str[] = {
9152 "GET_ROOT_DOMAIN",
9153 "GET_FRIENDLY_NAME",
9154 "GET_ASSEMBLIES",
9155 "GET_ENTRY_ASSEMBLY",
9156 "CREATE_STRING",
9157 "GET_CORLIB",
9158 "CREATE_BOXED_VALUE"
9161 static const char* assembly_cmds_str[] = {
9162 "GET_LOCATION",
9163 "GET_ENTRY_POINT",
9164 "GET_MANIFEST_MODULE",
9165 "GET_OBJECT",
9166 "GET_TYPE",
9167 "GET_NAME"
9170 static const char* module_cmds_str[] = {
9171 "GET_INFO",
9174 static const char* field_cmds_str[] = {
9175 "GET_INFO",
9178 static const char* method_cmds_str[] = {
9179 "GET_NAME",
9180 "GET_DECLARING_TYPE",
9181 "GET_DEBUG_INFO",
9182 "GET_PARAM_INFO",
9183 "GET_LOCALS_INFO",
9184 "GET_INFO",
9185 "GET_BODY",
9186 "RESOLVE_TOKEN",
9187 "GET_CATTRS ",
9188 "MAKE_GENERIC_METHOD"
9191 static const char* type_cmds_str[] = {
9192 "GET_INFO",
9193 "GET_METHODS",
9194 "GET_FIELDS",
9195 "GET_VALUES",
9196 "GET_OBJECT",
9197 "GET_SOURCE_FILES",
9198 "SET_VALUES",
9199 "IS_ASSIGNABLE_FROM",
9200 "GET_PROPERTIES ",
9201 "GET_CATTRS",
9202 "GET_FIELD_CATTRS",
9203 "GET_PROPERTY_CATTRS",
9204 "GET_SOURCE_FILES_2",
9205 "GET_VALUES_2",
9206 "GET_METHODS_BY_NAME_FLAGS",
9207 "GET_INTERFACES",
9208 "GET_INTERFACE_MAP",
9209 "IS_INITIALIZED"
9212 static const char* stack_frame_cmds_str[] = {
9213 "GET_VALUES",
9214 "GET_THIS",
9215 "SET_VALUES"
9218 static const char* array_cmds_str[] = {
9219 "GET_LENGTH",
9220 "GET_VALUES",
9221 "SET_VALUES",
9224 static const char* string_cmds_str[] = {
9225 "GET_VALUE",
9226 "GET_LENGTH",
9227 "GET_CHARS"
9230 static const char* object_cmds_str[] = {
9231 "GET_TYPE",
9232 "GET_VALUES",
9233 "IS_COLLECTED",
9234 "GET_ADDRESS",
9235 "GET_DOMAIN",
9236 "SET_VALUES",
9237 "GET_INFO",
9240 static const char*
9241 cmd_to_string (CommandSet set, int command)
9243 const char **cmds;
9244 int cmds_len = 0;
9246 switch (set) {
9247 case CMD_SET_VM:
9248 cmds = vm_cmds_str;
9249 cmds_len = G_N_ELEMENTS (vm_cmds_str);
9250 break;
9251 case CMD_SET_OBJECT_REF:
9252 cmds = object_cmds_str;
9253 cmds_len = G_N_ELEMENTS (object_cmds_str);
9254 break;
9255 case CMD_SET_STRING_REF:
9256 cmds = string_cmds_str;
9257 cmds_len = G_N_ELEMENTS (string_cmds_str);
9258 break;
9259 case CMD_SET_THREAD:
9260 cmds = thread_cmds_str;
9261 cmds_len = G_N_ELEMENTS (thread_cmds_str);
9262 break;
9263 case CMD_SET_ARRAY_REF:
9264 cmds = array_cmds_str;
9265 cmds_len = G_N_ELEMENTS (array_cmds_str);
9266 break;
9267 case CMD_SET_EVENT_REQUEST:
9268 cmds = event_cmds_str;
9269 cmds_len = G_N_ELEMENTS (event_cmds_str);
9270 break;
9271 case CMD_SET_STACK_FRAME:
9272 cmds = stack_frame_cmds_str;
9273 cmds_len = G_N_ELEMENTS (stack_frame_cmds_str);
9274 break;
9275 case CMD_SET_APPDOMAIN:
9276 cmds = appdomain_cmds_str;
9277 cmds_len = G_N_ELEMENTS (appdomain_cmds_str);
9278 break;
9279 case CMD_SET_ASSEMBLY:
9280 cmds = assembly_cmds_str;
9281 cmds_len = G_N_ELEMENTS (assembly_cmds_str);
9282 break;
9283 case CMD_SET_METHOD:
9284 cmds = method_cmds_str;
9285 cmds_len = G_N_ELEMENTS (method_cmds_str);
9286 break;
9287 case CMD_SET_TYPE:
9288 cmds = type_cmds_str;
9289 cmds_len = G_N_ELEMENTS (type_cmds_str);
9290 break;
9291 case CMD_SET_MODULE:
9292 cmds = module_cmds_str;
9293 cmds_len = G_N_ELEMENTS (module_cmds_str);
9294 break;
9295 case CMD_SET_FIELD:
9296 cmds = field_cmds_str;
9297 cmds_len = G_N_ELEMENTS (field_cmds_str);
9298 break;
9299 case CMD_SET_EVENT:
9300 cmds = event_cmds_str;
9301 cmds_len = G_N_ELEMENTS (event_cmds_str);
9302 break;
9303 default:
9304 return NULL;
9306 if (command > 0 && command <= cmds_len)
9307 return cmds [command - 1];
9308 else
9309 return NULL;
9312 static gboolean
9313 wait_for_attach (void)
9315 #ifndef DISABLE_SOCKET_TRANSPORT
9316 if (listen_fd == -1) {
9317 DEBUG (1, fprintf (log_file, "[dbg] Invalid listening socket\n"));
9318 return FALSE;
9321 /* Block and wait for client connection */
9322 conn_fd = socket_transport_accept (listen_fd);
9323 DEBUG (1, fprintf (log_file, "Accepted connection on %d\n", conn_fd));
9324 if (conn_fd == -1) {
9325 DEBUG (1, fprintf (log_file, "[dbg] Bad client connection\n"));
9326 return FALSE;
9328 #else
9329 g_assert_not_reached ();
9330 #endif
9332 /* Handshake */
9333 disconnected = !transport_handshake ();
9334 if (disconnected) {
9335 DEBUG (1, fprintf (log_file, "Transport handshake failed!\n"));
9336 return FALSE;
9339 return TRUE;
9343 * debugger_thread:
9345 * This thread handles communication with the debugger client using a JDWP
9346 * like protocol.
9348 static guint32 WINAPI
9349 debugger_thread (void *arg)
9351 int res, len, id, flags, command_set = 0, command = 0;
9352 guint8 header [HEADER_LENGTH];
9353 guint8 *data, *p, *end;
9354 Buffer buf;
9355 ErrorCode err;
9356 gboolean no_reply;
9357 gboolean attach_failed = FALSE;
9359 DEBUG (1, fprintf (log_file, "[dbg] Agent thread started, pid=%p\n", (gpointer)GetCurrentThreadId ()));
9361 debugger_thread_id = GetCurrentThreadId ();
9363 mono_jit_thread_attach (mono_get_root_domain ());
9365 mono_thread_internal_current ()->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
9367 mono_set_is_debugger_attached (TRUE);
9369 if (agent_config.defer) {
9370 if (!wait_for_attach ()) {
9371 DEBUG (1, fprintf (log_file, "[dbg] Can't attach, aborting debugger thread.\n"));
9372 attach_failed = TRUE; // Don't abort process when we can't listen
9373 } else {
9374 /* Send start event to client */
9375 process_profiler_event (EVENT_KIND_VM_START, mono_thread_get_main ());
9379 while (!attach_failed) {
9380 res = transport_recv (header, HEADER_LENGTH);
9382 /* This will break if the socket is closed during shutdown too */
9383 if (res != HEADER_LENGTH) {
9384 DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, HEADER_LENGTH));
9385 break;
9388 p = header;
9389 end = header + HEADER_LENGTH;
9391 len = decode_int (p, &p, end);
9392 id = decode_int (p, &p, end);
9393 flags = decode_byte (p, &p, end);
9394 command_set = decode_byte (p, &p, end);
9395 command = decode_byte (p, &p, end);
9397 g_assert (flags == 0);
9399 if (log_level) {
9400 const char *cmd_str;
9401 char cmd_num [256];
9403 cmd_str = cmd_to_string (command_set, command);
9404 if (!cmd_str) {
9405 sprintf (cmd_num, "%d", command);
9406 cmd_str = cmd_num;
9409 DEBUG (1, fprintf (log_file, "[dbg] Command %s(%s) [%d][at=%lx].\n", command_set_to_string (command_set), cmd_str, id, (long)mono_100ns_ticks () / 10000));
9412 data = g_malloc (len - HEADER_LENGTH);
9413 if (len - HEADER_LENGTH > 0)
9415 res = transport_recv (data, len - HEADER_LENGTH);
9416 if (res != len - HEADER_LENGTH) {
9417 DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, len - HEADER_LENGTH));
9418 break;
9422 p = data;
9423 end = data + (len - HEADER_LENGTH);
9425 buffer_init (&buf, 128);
9427 err = ERR_NONE;
9428 no_reply = FALSE;
9430 /* Process the request */
9431 switch (command_set) {
9432 case CMD_SET_VM:
9433 err = vm_commands (command, id, p, end, &buf);
9434 if (!err && command == CMD_VM_INVOKE_METHOD)
9435 /* Sent after the invoke is complete */
9436 no_reply = TRUE;
9437 break;
9438 case CMD_SET_EVENT_REQUEST:
9439 err = event_commands (command, p, end, &buf);
9440 break;
9441 case CMD_SET_APPDOMAIN:
9442 err = domain_commands (command, p, end, &buf);
9443 break;
9444 case CMD_SET_ASSEMBLY:
9445 err = assembly_commands (command, p, end, &buf);
9446 break;
9447 case CMD_SET_MODULE:
9448 err = module_commands (command, p, end, &buf);
9449 break;
9450 case CMD_SET_FIELD:
9451 err = field_commands (command, p, end, &buf);
9452 break;
9453 case CMD_SET_TYPE:
9454 err = type_commands (command, p, end, &buf);
9455 break;
9456 case CMD_SET_METHOD:
9457 err = method_commands (command, p, end, &buf);
9458 break;
9459 case CMD_SET_THREAD:
9460 err = thread_commands (command, p, end, &buf);
9461 break;
9462 case CMD_SET_STACK_FRAME:
9463 err = frame_commands (command, p, end, &buf);
9464 break;
9465 case CMD_SET_ARRAY_REF:
9466 err = array_commands (command, p, end, &buf);
9467 break;
9468 case CMD_SET_STRING_REF:
9469 err = string_commands (command, p, end, &buf);
9470 break;
9471 case CMD_SET_OBJECT_REF:
9472 err = object_commands (command, p, end, &buf);
9473 break;
9474 default:
9475 err = ERR_NOT_IMPLEMENTED;
9478 if (!no_reply) {
9479 if (buffer_replies) {
9480 buffer_reply_packet (id, err, &buf);
9481 } else {
9482 send_reply_packet (id, err, &buf);
9483 //DEBUG (1, fprintf (log_file, "[dbg] Sent reply to %d [at=%lx].\n", id, (long)mono_100ns_ticks () / 10000));
9487 if (!err && command_set == CMD_SET_VM) {
9488 switch (command) {
9489 case CMD_VM_STOP_BUFFERING:
9490 send_buffered_reply_packets ();
9491 buffer_replies = FALSE;
9492 break;
9493 case CMD_VM_START_BUFFERING:
9494 buffer_replies = TRUE;
9495 break;
9496 default:
9497 break;
9501 g_free (data);
9502 buffer_free (&buf);
9504 if (command_set == CMD_SET_VM && (command == CMD_VM_DISPOSE || command == CMD_VM_EXIT))
9505 break;
9508 mono_set_is_debugger_attached (FALSE);
9510 mono_mutex_lock (&debugger_thread_exited_mutex);
9511 debugger_thread_exited = TRUE;
9512 mono_cond_signal (&debugger_thread_exited_cond);
9513 mono_mutex_unlock (&debugger_thread_exited_mutex);
9515 DEBUG (1, fprintf (log_file, "[dbg] Debugger thread exited.\n"));
9517 if (!attach_failed && command_set == CMD_SET_VM && command == CMD_VM_DISPOSE && !(vm_death_event_sent || mono_runtime_is_shutting_down ())) {
9518 DEBUG (2, fprintf (log_file, "[dbg] Detached - restarting clean debugger thread.\n"));
9519 start_debugger_thread ();
9522 return 0;
9525 #else /* DISABLE_DEBUGGER_AGENT */
9527 void
9528 mono_debugger_agent_parse_options (char *options)
9530 g_error ("This runtime is configured with the debugger agent disabled.");
9533 void
9534 mono_debugger_agent_init (void)
9538 void
9539 mono_debugger_agent_breakpoint_hit (void *sigctx)
9543 void
9544 mono_debugger_agent_single_step_event (void *sigctx)
9548 void
9549 mono_debugger_agent_free_domain_info (MonoDomain *domain)
9553 gboolean
9554 mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
9556 return FALSE;
9559 void
9560 mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *throw_ctx,
9561 MonoContext *catch_ctx)
9565 void
9566 mono_debugger_agent_begin_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
9570 void
9571 mono_debugger_agent_end_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
9575 void
9576 mono_debugger_agent_user_break (void)
9578 G_BREAKPOINT ();
9581 void
9582 mono_debugger_agent_debug_log (int level, MonoString *category, MonoString *message)
9586 gboolean
9587 mono_debugger_agent_debug_log_is_enabled (void)
9589 return FALSE;
9592 #endif