/** * \file * Soft Debugger back-end module * * Author: * Zoltan Varga (vargaz@gmail.com) * * Copyright 2009-2010 Novell, Inc. * Copyright 2011 Xamarin Inc. * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_TCP_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_PTHREAD_H #include #endif #ifdef HOST_WIN32 #ifdef _MSC_VER #include #include #endif #include #endif #ifdef PLATFORM_ANDROID #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debugger-agent.h" #include "mini.h" #include "seq-points.h" #include /* * On iOS we can't use System.Environment.Exit () as it will do the wrong * shutdown sequence. */ #if !defined (TARGET_IOS) #define TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT #endif #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED #define DISABLE_DEBUGGER_AGENT 1 #endif #ifdef DISABLE_SOFT_DEBUG #define DISABLE_DEBUGGER_AGENT 1 #endif #ifndef DISABLE_DEBUGGER_AGENT #include #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread typedef struct { gboolean enabled; char *transport; char *address; int log_level; char *log_file; gboolean suspend; gboolean server; gboolean onuncaught; GSList *onthrow; int timeout; char *launch; gboolean embedding; gboolean defer; int keepalive; gboolean setpgid; } AgentConfig; typedef struct { int id; guint32 il_offset, native_offset; MonoDomain *domain; MonoMethod *method; /* * If method is gshared, this is the actual instance, otherwise this is equal to * method. */ MonoMethod *actual_method; /* * This is the method which is visible to debugger clients. Same as method, * except for native-to-managed wrappers. */ MonoMethod *api_method; MonoContext ctx; MonoDebugMethodJitInfo *jit; MonoJitInfo *ji; int flags; mgreg_t *reg_locations [MONO_MAX_IREGS]; /* * Whenever ctx is set. This is FALSE for the last frame of running threads, since * the frame can become invalid. */ gboolean has_ctx; } StackFrame; typedef struct _InvokeData InvokeData; struct _InvokeData { int id; int flags; guint8 *p; guint8 *endp; /* This is the context which needs to be restored after the invoke */ MonoContext ctx; gboolean has_ctx; /* * If this is set, invoke this method with the arguments given by ARGS. */ MonoMethod *method; gpointer *args; guint32 suspend_count; int nmethods; InvokeData *last_invoke; }; typedef struct { MonoThreadUnwindState context; /* This is computed on demand when it is requested using the wire protocol */ /* It is freed up when the thread is resumed */ int frame_count; StackFrame **frames; /* * Whenever the frame info is up-to-date. If not, compute_frame_info () will need to * re-compute it. */ gboolean frames_up_to_date; /* * Points to data about a pending invoke which needs to be executed after the thread * resumes. */ InvokeData *pending_invoke; /* * Set to TRUE if this thread is suspended in suspend_current () or it is executing * native code. */ gboolean suspended; /* * Signals whenever the thread is in the process of suspending, i.e. it will suspend * within a finite amount of time. */ gboolean suspending; /* * Set to TRUE if this thread is suspended in suspend_current (). */ gboolean really_suspended; /* Used to pass the context to the breakpoint/single step handler */ MonoContext handler_ctx; /* Whenever thread_stop () was called for this thread */ gboolean terminated; /* Whenever to disable breakpoints (used during invokes) */ gboolean disable_breakpoints; /* * Number of times this thread has been resumed using resume_thread (). */ guint32 resume_count; MonoInternalThread *thread; /* * Information about the frame which transitioned to native code for running * threads. */ StackFrameInfo async_last_frame; /* * The context where the stack walk can be started for running threads. */ MonoThreadUnwindState async_state; /* * The context used for filter clauses */ MonoThreadUnwindState filter_state; gboolean abort_requested; /* * The current mono_runtime_invoke_checked invocation. */ InvokeData *invoke; /* * The context where single stepping should resume while the thread is suspended because * of an EXCEPTION event. */ MonoThreadUnwindState catch_state; /* * The context which needs to be restored after handling a single step/breakpoint * event. This is the same as the ctx at step/breakpoint site, but includes changes * to caller saved registers done by set_var (). */ MonoThreadUnwindState restore_state; /* Frames computed from restore_state */ int restore_frame_count; StackFrame **restore_frames; /* The currently unloading appdomain */ MonoDomain *domain_unloading; } DebuggerTlsData; typedef struct { const char *name; void (*connect) (const char *address); void (*close1) (void); void (*close2) (void); gboolean (*send) (void *buf, int len); int (*recv) (void *buf, int len); } DebuggerTransport; /* * Wire Protocol definitions */ #define HEADER_LENGTH 11 #define MAJOR_VERSION 2 #define MINOR_VERSION 45 typedef enum { CMD_SET_VM = 1, CMD_SET_OBJECT_REF = 9, CMD_SET_STRING_REF = 10, CMD_SET_THREAD = 11, CMD_SET_ARRAY_REF = 13, CMD_SET_EVENT_REQUEST = 15, CMD_SET_STACK_FRAME = 16, CMD_SET_APPDOMAIN = 20, CMD_SET_ASSEMBLY = 21, CMD_SET_METHOD = 22, CMD_SET_TYPE = 23, CMD_SET_MODULE = 24, CMD_SET_FIELD = 25, CMD_SET_EVENT = 64 } CommandSet; typedef enum { EVENT_KIND_VM_START = 0, EVENT_KIND_VM_DEATH = 1, EVENT_KIND_THREAD_START = 2, EVENT_KIND_THREAD_DEATH = 3, EVENT_KIND_APPDOMAIN_CREATE = 4, EVENT_KIND_APPDOMAIN_UNLOAD = 5, EVENT_KIND_METHOD_ENTRY = 6, EVENT_KIND_METHOD_EXIT = 7, EVENT_KIND_ASSEMBLY_LOAD = 8, EVENT_KIND_ASSEMBLY_UNLOAD = 9, EVENT_KIND_BREAKPOINT = 10, EVENT_KIND_STEP = 11, EVENT_KIND_TYPE_LOAD = 12, EVENT_KIND_EXCEPTION = 13, EVENT_KIND_KEEPALIVE = 14, EVENT_KIND_USER_BREAK = 15, EVENT_KIND_USER_LOG = 16 } EventKind; typedef enum { SUSPEND_POLICY_NONE = 0, SUSPEND_POLICY_EVENT_THREAD = 1, SUSPEND_POLICY_ALL = 2 } SuspendPolicy; typedef enum { ERR_NONE = 0, ERR_INVALID_OBJECT = 20, ERR_INVALID_FIELDID = 25, ERR_INVALID_FRAMEID = 30, ERR_NOT_IMPLEMENTED = 100, ERR_NOT_SUSPENDED = 101, ERR_INVALID_ARGUMENT = 102, ERR_UNLOADED = 103, ERR_NO_INVOCATION = 104, ERR_ABSENT_INFORMATION = 105, ERR_NO_SEQ_POINT_AT_IL_OFFSET = 106, ERR_INVOKE_ABORTED = 107, ERR_LOADER_ERROR = 200, /*XXX extend the protocol to pass this information down the pipe */ } ErrorCode; typedef enum { MOD_KIND_COUNT = 1, MOD_KIND_THREAD_ONLY = 3, MOD_KIND_LOCATION_ONLY = 7, MOD_KIND_EXCEPTION_ONLY = 8, MOD_KIND_STEP = 10, MOD_KIND_ASSEMBLY_ONLY = 11, MOD_KIND_SOURCE_FILE_ONLY = 12, MOD_KIND_TYPE_NAME_ONLY = 13, MOD_KIND_NONE = 14 } ModifierKind; typedef enum { STEP_DEPTH_INTO = 0, STEP_DEPTH_OVER = 1, STEP_DEPTH_OUT = 2 } StepDepth; typedef enum { STEP_SIZE_MIN = 0, STEP_SIZE_LINE = 1 } StepSize; typedef enum { STEP_FILTER_NONE = 0, STEP_FILTER_STATIC_CTOR = 1, STEP_FILTER_DEBUGGER_HIDDEN = 2, STEP_FILTER_DEBUGGER_STEP_THROUGH = 4, STEP_FILTER_DEBUGGER_NON_USER_CODE = 8 } StepFilter; typedef enum { TOKEN_TYPE_STRING = 0, TOKEN_TYPE_TYPE = 1, TOKEN_TYPE_FIELD = 2, TOKEN_TYPE_METHOD = 3, TOKEN_TYPE_UNKNOWN = 4 } DebuggerTokenType; typedef enum { VALUE_TYPE_ID_NULL = 0xf0, VALUE_TYPE_ID_TYPE = 0xf1, VALUE_TYPE_ID_PARENT_VTYPE = 0xf2 } ValueTypeId; typedef enum { FRAME_FLAG_DEBUGGER_INVOKE = 1, FRAME_FLAG_NATIVE_TRANSITION = 2 } StackFrameFlags; typedef enum { INVOKE_FLAG_DISABLE_BREAKPOINTS = 1, INVOKE_FLAG_SINGLE_THREADED = 2, INVOKE_FLAG_RETURN_OUT_THIS = 4, INVOKE_FLAG_RETURN_OUT_ARGS = 8, INVOKE_FLAG_VIRTUAL = 16 } InvokeFlags; typedef enum { BINDING_FLAGS_IGNORE_CASE = 0x70000000, } BindingFlagsExtensions; typedef enum { CMD_VM_VERSION = 1, CMD_VM_ALL_THREADS = 2, CMD_VM_SUSPEND = 3, CMD_VM_RESUME = 4, CMD_VM_EXIT = 5, CMD_VM_DISPOSE = 6, CMD_VM_INVOKE_METHOD = 7, CMD_VM_SET_PROTOCOL_VERSION = 8, CMD_VM_ABORT_INVOKE = 9, CMD_VM_SET_KEEPALIVE = 10, CMD_VM_GET_TYPES_FOR_SOURCE_FILE = 11, CMD_VM_GET_TYPES = 12, CMD_VM_INVOKE_METHODS = 13, CMD_VM_START_BUFFERING = 14, CMD_VM_STOP_BUFFERING = 15 } CmdVM; typedef enum { CMD_THREAD_GET_FRAME_INFO = 1, CMD_THREAD_GET_NAME = 2, CMD_THREAD_GET_STATE = 3, CMD_THREAD_GET_INFO = 4, CMD_THREAD_GET_ID = 5, CMD_THREAD_GET_TID = 6, CMD_THREAD_SET_IP = 7 } CmdThread; typedef enum { CMD_EVENT_REQUEST_SET = 1, CMD_EVENT_REQUEST_CLEAR = 2, CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS = 3 } CmdEvent; typedef enum { CMD_COMPOSITE = 100 } CmdComposite; typedef enum { CMD_APPDOMAIN_GET_ROOT_DOMAIN = 1, CMD_APPDOMAIN_GET_FRIENDLY_NAME = 2, CMD_APPDOMAIN_GET_ASSEMBLIES = 3, CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4, CMD_APPDOMAIN_CREATE_STRING = 5, CMD_APPDOMAIN_GET_CORLIB = 6, CMD_APPDOMAIN_CREATE_BOXED_VALUE = 7 } CmdAppDomain; typedef enum { CMD_ASSEMBLY_GET_LOCATION = 1, CMD_ASSEMBLY_GET_ENTRY_POINT = 2, CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3, CMD_ASSEMBLY_GET_OBJECT = 4, CMD_ASSEMBLY_GET_TYPE = 5, CMD_ASSEMBLY_GET_NAME = 6, CMD_ASSEMBLY_GET_DOMAIN = 7 } CmdAssembly; typedef enum { CMD_MODULE_GET_INFO = 1, } CmdModule; typedef enum { CMD_FIELD_GET_INFO = 1, } CmdField; typedef enum { CMD_METHOD_GET_NAME = 1, CMD_METHOD_GET_DECLARING_TYPE = 2, CMD_METHOD_GET_DEBUG_INFO = 3, CMD_METHOD_GET_PARAM_INFO = 4, CMD_METHOD_GET_LOCALS_INFO = 5, CMD_METHOD_GET_INFO = 6, CMD_METHOD_GET_BODY = 7, CMD_METHOD_RESOLVE_TOKEN = 8, CMD_METHOD_GET_CATTRS = 9, CMD_METHOD_MAKE_GENERIC_METHOD = 10 } CmdMethod; typedef enum { CMD_TYPE_GET_INFO = 1, CMD_TYPE_GET_METHODS = 2, CMD_TYPE_GET_FIELDS = 3, CMD_TYPE_GET_VALUES = 4, CMD_TYPE_GET_OBJECT = 5, CMD_TYPE_GET_SOURCE_FILES = 6, CMD_TYPE_SET_VALUES = 7, CMD_TYPE_IS_ASSIGNABLE_FROM = 8, CMD_TYPE_GET_PROPERTIES = 9, CMD_TYPE_GET_CATTRS = 10, CMD_TYPE_GET_FIELD_CATTRS = 11, CMD_TYPE_GET_PROPERTY_CATTRS = 12, CMD_TYPE_GET_SOURCE_FILES_2 = 13, CMD_TYPE_GET_VALUES_2 = 14, CMD_TYPE_GET_METHODS_BY_NAME_FLAGS = 15, CMD_TYPE_GET_INTERFACES = 16, CMD_TYPE_GET_INTERFACE_MAP = 17, CMD_TYPE_IS_INITIALIZED = 18, CMD_TYPE_CREATE_INSTANCE = 19 } CmdType; typedef enum { CMD_STACK_FRAME_GET_VALUES = 1, CMD_STACK_FRAME_GET_THIS = 2, CMD_STACK_FRAME_SET_VALUES = 3, CMD_STACK_FRAME_GET_DOMAIN = 4, CMD_STACK_FRAME_SET_THIS = 5, } CmdStackFrame; typedef enum { CMD_ARRAY_REF_GET_LENGTH = 1, CMD_ARRAY_REF_GET_VALUES = 2, CMD_ARRAY_REF_SET_VALUES = 3, } CmdArray; typedef enum { CMD_STRING_REF_GET_VALUE = 1, CMD_STRING_REF_GET_LENGTH = 2, CMD_STRING_REF_GET_CHARS = 3 } CmdString; typedef enum { CMD_OBJECT_REF_GET_TYPE = 1, CMD_OBJECT_REF_GET_VALUES = 2, CMD_OBJECT_REF_IS_COLLECTED = 3, CMD_OBJECT_REF_GET_ADDRESS = 4, CMD_OBJECT_REF_GET_DOMAIN = 5, CMD_OBJECT_REF_SET_VALUES = 6, CMD_OBJECT_REF_GET_INFO = 7, } CmdObject; typedef struct { ModifierKind kind; union { int count; /* For kind == MOD_KIND_COUNT */ MonoInternalThread *thread; /* For kind == MOD_KIND_THREAD_ONLY */ MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */ MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */ GHashTable *source_files; /* For kind == MONO_KIND_SOURCE_FILE_ONLY */ GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */ StepFilter filter; /* For kind == MOD_KIND_STEP */ } data; gboolean caught, uncaught, subclasses; /* For kind == MOD_KIND_EXCEPTION_ONLY */ } Modifier; typedef struct{ int id; int event_kind; int suspend_policy; int nmodifiers; gpointer info; Modifier modifiers [MONO_ZERO_LEN_ARRAY]; } EventRequest; /* * Describes a single step request. */ typedef struct { EventRequest *req; MonoInternalThread *thread; StepDepth depth; StepSize size; StepFilter filter; gpointer last_sp; gpointer start_sp; MonoMethod *start_method; MonoMethod *last_method; int last_line; /* Whenever single stepping is performed using start/stop_single_stepping () */ gboolean global; /* The list of breakpoints used to implement step-over */ GSList *bps; /* The number of frames at the start of a step-over */ int nframes; /* If set, don't stop in methods that are not part of user assemblies */ MonoAssembly** user_assemblies; /* Used to distinguish stepping breakpoint hits in parallel tasks executions */ int async_id; /* Used to know if we are in process of async step-out and distishing from exception breakpoints */ MonoMethod* async_stepout_method; } SingleStepReq; /* * Contains additional information for an event */ typedef struct { /* For EVENT_KIND_EXCEPTION */ MonoObject *exc; MonoContext catch_ctx; gboolean caught; /* For EVENT_KIND_USER_LOG */ int level; char *category, *message; /* For EVENT_KIND_TYPE_LOAD */ MonoClass *klass; } EventInfo; /* Dummy structure used for the profiler callbacks */ typedef struct { void* dummy; } DebuggerProfiler; typedef struct { guint8 *buf, *p, *end; } Buffer; typedef struct ReplyPacket { int id; int error; Buffer *data; } ReplyPacket; #define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; fflush (log_file); } } while (0) #ifdef PLATFORM_ANDROID #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0) #else #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (log_file, __VA_ARGS__); fflush (log_file); } } while (0) #endif #ifdef HOST_WIN32 #define get_last_sock_error() WSAGetLastError() #define MONO_EWOULDBLOCK WSAEWOULDBLOCK #define MONO_EINTR WSAEINTR #else #define get_last_sock_error() errno #define MONO_EWOULDBLOCK EWOULDBLOCK #define MONO_EINTR EINTR #endif #define CHECK_PROTOCOL_VERSION(major,minor) \ (protocol_version_set && (major_version > (major) || (major_version == (major) && minor_version >= (minor)))) /* * Globals */ static AgentConfig agent_config; /* * Whenever the agent is fully initialized. * When using the onuncaught or onthrow options, only some parts of the agent are * initialized on startup, and the full initialization which includes connection * establishment and the startup of the agent thread is only done in response to * an event. */ static gint32 inited; #ifndef DISABLE_SOCKET_TRANSPORT static int conn_fd; static int listen_fd; #endif static int packet_id = 0; static int objref_id = 0; static int event_request_id = 0; static int frame_id = 0; static GPtrArray *event_requests; static MonoNativeTlsKey debugger_tls_id; static gboolean vm_start_event_sent, vm_death_event_sent, disconnected; /* Maps MonoInternalThread -> DebuggerTlsData */ /* Protected by the loader lock */ static MonoGHashTable *thread_to_tls; /* Maps tid -> MonoInternalThread */ /* Protected by the loader lock */ static MonoGHashTable *tid_to_thread; /* Maps tid -> MonoThread (not MonoInternalThread) */ /* Protected by the loader lock */ static MonoGHashTable *tid_to_thread_obj; static MonoNativeThreadId debugger_thread_id; static MonoThreadHandle *debugger_thread_handle; static int log_level; static gboolean embedding; static FILE *log_file; /* Assemblies whose assembly load event has no been sent yet */ /* Protected by the dbg lock */ static GPtrArray *pending_assembly_loads; /* Whenever the debugger thread has exited */ static gboolean debugger_thread_exited; /* Cond variable used to wait for debugger_thread_exited becoming true */ static MonoCoopCond debugger_thread_exited_cond; /* Mutex for the cond var above */ static MonoCoopMutex debugger_thread_exited_mutex; static DebuggerProfiler debugger_profiler; /* The single step request instance */ static SingleStepReq *ss_req; #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED /* Number of single stepping operations in progress */ static int ss_count; #endif /* The protocol version of the client */ static int major_version, minor_version; /* Whenever the variables above are set by the client */ static gboolean protocol_version_set; /* A hash table containing all active domains */ /* Protected by the loader lock */ static GHashTable *domains; /* The number of times the runtime is suspended */ static gint32 suspend_count; /* Whenever to buffer reply messages and send them together */ static gboolean buffer_replies; /* Buffered reply packets */ static ReplyPacket reply_packets [128]; int nreply_packets; #define dbg_lock() mono_coop_mutex_lock (&debug_mutex) #define dbg_unlock() mono_coop_mutex_unlock (&debug_mutex) static MonoCoopMutex debug_mutex; static void transport_init (void); static void transport_connect (const char *address); static gboolean transport_handshake (void); static void register_transport (DebuggerTransport *trans); static gsize WINAPI debugger_thread (void *arg); static void runtime_initialized (MonoProfiler *prof); static void runtime_shutdown (MonoProfiler *prof); static void thread_startup (MonoProfiler *prof, uintptr_t tid); static void thread_end (MonoProfiler *prof, uintptr_t tid); static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result); static void appdomain_start_unload (MonoProfiler *prof, MonoDomain *domain); static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain); static void emit_appdomain_load (gpointer key, gpointer value, gpointer user_data); static void emit_thread_start (gpointer key, gpointer value, gpointer user_data); static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_data); static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result); static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly); static void emit_assembly_load (gpointer assembly, gpointer user_data); static void emit_type_load (gpointer key, gpointer type, gpointer user_data); static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result); static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *jinfo); static void start_single_stepping (void); static void stop_single_stepping (void); static void suspend_current (void); static void clear_event_requests_for_assembly (MonoAssembly *assembly); static void clear_types_for_assembly (MonoAssembly *assembly); static void clear_breakpoints_for_domain (MonoDomain *domain); static void process_profiler_event (EventKind event, gpointer arg); /* Submodule init/cleanup */ static void breakpoints_init (void); static void breakpoints_cleanup (void); static void objrefs_init (void); static void objrefs_cleanup (void); static void ids_init (void); static void ids_cleanup (void); static void suspend_init (void); static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch, StackFrame **frames, int nframes); static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilter filter, EventRequest *req); static void ss_destroy (SingleStepReq *req); static void start_debugger_thread (void); static void stop_debugger_thread (void); static void finish_agent_init (gboolean on_startup); static void process_profiler_event (EventKind event, gpointer arg); static void invalidate_frames (DebuggerTlsData *tls); #ifndef DISABLE_SOCKET_TRANSPORT static void register_socket_transport (void); #endif static inline gboolean is_debugger_thread (void) { MonoInternalThread *internal; internal = mono_thread_internal_current (); if (!internal) return FALSE; return internal->debugger_thread; } static int parse_address (char *address, char **host, int *port) { char *pos = strchr (address, ':'); if (pos == NULL || pos == address) return 1; size_t len = pos - address; *host = (char *)g_malloc (len + 1); memcpy (*host, address, len); (*host) [len] = '\0'; *port = atoi (pos + 1); return 0; } static void print_usage (void) { fprintf (stderr, "Usage: mono --debugger-agent=[