/mono/mini/debugger-agent.c
C | 8901 lines | 6533 code | 1402 blank | 966 comment | 1422 complexity | 859c5c1a8bab0fc94f2776ea793ecb01 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- * debugger-agent.c: Soft Debugger back-end module
- *
- * Author:
- * Zoltan Varga (vargaz@gmail.com)
- *
- * Copyright 2009-2010 Novell, Inc.
- * Copyright 2011 Xamarin Inc.
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #ifdef HAVE_SYS_TYPES_H
- #include <sys/types.h>
- #endif
- #ifdef HAVE_SYS_SELECT_H
- #include <sys/select.h>
- #endif
- #ifdef HAVE_SYS_SOCKET_H
- #include <sys/socket.h>
- #endif
- #ifdef HAVE_NETINET_TCP_H
- #include <netinet/tcp.h>
- #endif
- #ifdef HAVE_NETINET_IN_H
- #include <netinet/in.h>
- #endif
- #ifdef HAVE_NETDB_H
- #include <netdb.h>
- #endif
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include <errno.h>
- #include <glib.h>
- #ifdef HAVE_PTHREAD_H
- #include <pthread.h>
- #endif
- #ifdef HAVE_UCONTEXT_H
- #include <ucontext.h>
- #endif
- #ifdef HOST_WIN32
- #ifdef _MSC_VER
- #include <winsock2.h>
- #endif
- #include <ws2tcpip.h>
- #ifdef __GNUC__
- /* cygwin's headers do not seem to define these */
- void WSAAPI freeaddrinfo (struct addrinfo*);
- int WSAAPI getaddrinfo (const char*,const char*,const struct addrinfo*,
- struct addrinfo**);
- int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
- char*,DWORD,int);
- #endif
- #endif
- #ifdef PLATFORM_ANDROID
- #include <linux/in.h>
- #include <linux/tcp.h>
- #include <sys/endian.h>
- #endif
- #include <mono/metadata/mono-debug.h>
- #include <mono/metadata/mono-debug-debugger.h>
- #include <mono/metadata/debug-mono-symfile.h>
- #include <mono/metadata/gc-internal.h>
- #include <mono/metadata/threads-types.h>
- #include <mono/metadata/socket-io.h>
- #include <mono/metadata/assembly.h>
- #include <mono/utils/mono-semaphore.h>
- #include <mono/utils/mono-error-internals.h>
- #include <mono/utils/mono-stack-unwinding.h>
- #include <mono/utils/mono-time.h>
- #include <mono/utils/mono-threads.h>
- #include "debugger-agent.h"
- #include "mini.h"
- /*
- 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 <mono/io-layer/mono-mutex.h>
- /* Definitions to make backporting to 2.6 easier */
- //#define MonoInternalThread MonoThread
- //#define mono_thread_internal_current mono_thread_current
- #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;
- } 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;
- /* Number of thread interruptions not yet processed */
- gint32 interrupt_count;
- /* 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;
- /*
- * The callee address of the last mono_runtime_invoke call
- */
- gpointer invoke_addr;
- gboolean abort_requested;
- /*
- * The current mono_runtime_invoke 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 ().
- */
- MonoContext restore_ctx;
- } 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 23
- 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_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_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
- } 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
- } 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
- } 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
- } 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
- } 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
- } 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
- } CmdAssembly;
- typedef enum {
- CMD_MODULE_GET_INFO = 1,
- } CmdModule;
- 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,
- } 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
- } CmdType;
- typedef enum {
- CMD_STACK_FRAME_GET_VALUES = 1,
- CMD_STACK_FRAME_GET_THIS = 2,
- CMD_STACK_FRAME_SET_VALUES = 3
- } 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; /* 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 *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;
- } 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;
- #define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; fflush (log_file); } } while (0)
- #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 */
- static MonoGHashTable *thread_to_tls;
- /* Maps tid -> MonoInternalThread */
- static MonoGHashTable *tid_to_thread;
- /* Maps tid -> MonoThread (not MonoInternalThread) */
- static MonoGHashTable *tid_to_thread_obj;
- static gsize debugger_thread_id;
- static HANDLE debugger_thread_handle;
- static int log_level;
- static gboolean embedding;
- static FILE *log_file;
- /* Assemblies whose assembly load event has no been sent yet */
- 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 mono_cond_t debugger_thread_exited_cond;
- /* Mutex for the cond var above */
- static mono_mutex_t debugger_thread_exited_mutex;
- static DebuggerProfiler debugger_profiler;
- /* The single step request instance */
- static SingleStepReq *ss_req = NULL;
- static gpointer ss_invoke_addr = NULL;
- #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 */
- static GHashTable *domains;
- /* The number of times the runtime is suspended */
- static gint32 suspend_count;
- static void transport_init (void);
- static void transport_connect (const char *address);
- static gboolean transport_handshake (void);
- static void register_transport (DebuggerTransport *trans);
- static guint32 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_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 start_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
- static void end_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
- 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);
- static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, 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 int
- parse_address (char *address, char **host, int *port)
- {
- char *pos = strchr (address, ':');
- if (pos == NULL || pos == address)
- return 1;
- *host = g_malloc (pos - address + 1);
- strncpy (*host, address, pos - address);
- (*host) [pos - address] = '\0';
- *port = atoi (pos + 1);
- return 0;
- }
- static void
- print_usage (void)
- {
- fprintf (stderr, "Usage: mono --debugger-agent=[<option>=<value>,...] ...\n");
- fprintf (stderr, "Available options:\n");
- fprintf (stderr, " transport=<transport>\t\tTransport to use for connecting to the debugger (mandatory, possible values: 'dt_socket')\n");
- fprintf (stderr, " address=<hostname>:<port>\tAddress to connect to (mandatory)\n");
- fprintf (stderr, " loglevel=<n>\t\t\tLog level (defaults to 0)\n");
- fprintf (stderr, " logfile=<file>\t\tFile to log to (defaults to stdout)\n");
- fprintf (stderr, " suspend=y/n\t\t\tWhether to suspend after startup.\n");
- fprintf (stderr, " timeout=<n>\t\t\tTimeout for connecting in milliseconds.\n");
- fprintf (stderr, " server=y/n\t\t\tWhether to listen for a client connection.\n");
- fprintf (stderr, " keepalive=<n>\t\t\tSend keepalive events every n milliseconds.\n");
- fprintf (stderr, " help\t\t\t\tPrint this help.\n");
- }
- static gboolean
- parse_flag (const char *option, char *flag)
- {
- if (!strcmp (flag, "y"))
- return TRUE;
- else if (!strcmp (flag, "n"))
- return FALSE;
- else {
- fprintf (stderr, "debugger-agent: The valid values for the '%s' option are 'y' and 'n'.\n", option);
- exit (1);
- return FALSE;
- }
- }
- void
- mono_debugger_agent_parse_options (char *options)
- {
- char **args, **ptr;
- char *host;
- int port;
- char *extra;
- #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
- fprintf (stderr, "--debugger-agent is not supported on this platform.\n");
- exit (1);
- #endif
- extra = getenv ("MONO_SDB_ENV_OPTIONS");
- if (extra)
- options = g_strdup_printf ("%s,%s", options, extra);
- agent_config.enabled = TRUE;
- agent_config.suspend = TRUE;
- agent_config.server = FALSE;
- agent_config.defer = FALSE;
- agent_config.address = NULL;
- args = g_strsplit (options, ",", -1);
- for (ptr = args; ptr && *ptr; ptr ++) {
- char *arg = *ptr;
- if (strncmp (arg, "transport=", 10) == 0) {
- agent_config.transport = g_strdup (arg + 10);
- } else if (strncmp (arg, "address=", 8) == 0) {
- agent_config.address = g_strdup (arg + 8);
- } else if (strncmp (arg, "loglevel=", 9) == 0) {
- agent_config.log_level = atoi (arg + 9);
- } else if (strncmp (arg, "logfile=", 8) == 0) {
- agent_config.log_file = g_strdup (arg + 8);
- } else if (strncmp (arg, "suspend=", 8) == 0) {
- agent_config.suspend = parse_flag ("suspend", arg + 8);
- } else if (strncmp (arg, "server=", 7) == 0) {
- agent_config.server = parse_flag ("server", arg + 7);
- } else if (strncmp (arg, "onuncaught=", 11) == 0) {
- agent_config.onuncaught = parse_flag ("onuncaught", arg + 11);
- } else if (strncmp (arg, "onthrow=", 8) == 0) {
- /* We support multiple onthrow= options */
- agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (arg + 8));
- } else if (strncmp (arg, "onthrow", 7) == 0) {
- agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (""));
- } else if (strncmp (arg, "help", 4) == 0) {
- print_usage ();
- exit (0);
- } else if (strncmp (arg, "timeout=", 8) == 0) {
- agent_config.timeout = atoi (arg + 8);
- } else if (strncmp (arg, "launch=", 7) == 0) {
- agent_config.launch = g_strdup (arg + 7);
- } else if (strncmp (arg, "embedding=", 10) == 0) {
- agent_config.embedding = atoi (arg + 10) == 1;
- } else if (strncmp (arg, "keepalive=", 10) == 0) {
- agent_config.keepalive = atoi (arg + 10);
- } else {
- print_usage ();
- exit (1);
- }
- }
- if (agent_config.server && !agent_config.suspend) {
- /* Waiting for deferred attachment */
- agent_config.defer = TRUE;
- if (agent_config.address == NULL) {
- agent_config.address = g_strdup_printf ("0.0.0.0:%u", 56000 + (GetCurrentProcessId () % 1000));
- }
- }
- //agent_config.log_level = 0;
- if (agent_config.transport == NULL) {
- fprintf (stderr, "debugger-agent: The 'transport' option is mandatory.\n");
- exit (1);
- }
- if (agent_config.address == NULL && !agent_config.server) {
- fprintf (stderr, "debugger-agent: The 'address' option is mandatory.\n");
- exit (1);
- }
- // FIXME:
- if (!strcmp (agent_config.transport, "dt_socket")) {
- if (agent_config.address && parse_address (agent_config.address, &host, &port)) {
- fprintf (stderr, "debugger-agent: The format of the 'address' options is '<host>:<port>'\n");
- exit (1);
- }
- }
- }
- void
- mono_debugger_agent_init (void)
- {
- if (!agent_config.enabled)
- return;
- transport_init ();
- /* Need to know whenever a thread has acquired the loader mutex */
- mono_loader_lock_track_ownership (TRUE);
- event_requests = g_ptr_array_new ();
- mono_mutex_init (&debugger_thread_exited_mutex, NULL);
- mono_cond_init (&debugger_thread_exited_cond, NULL);
- mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
- mono_profiler_set_events (MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS);
- mono_profiler_install_runtime_initialized (runtime_initialized);
- mono_profiler_install_appdomain (NULL, appdomain_load, NULL, appdomain_unload);
- mono_profiler_install_thread (thread_startup, thread_end);
- mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL);
- mono_profiler_install_jit_end (jit_end);
- mono_profiler_install_method_invoke (start_runtime_invoke, end_runtime_invoke);
- mono_native_tls_alloc (&debugger_tls_id, NULL);
- thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
- MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls);
- tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
- MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread);
- tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
- MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj);
- pending_assembly_loads = g_ptr_array_new ();
- domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
- log_level = agent_config.log_level;
- embedding = agent_config.embedding;
- disconnected = TRUE;
- if (agent_config.log_file) {
- log_file = fopen (agent_config.log_file, "w+");
- if (!log_file) {
- fprintf (stderr, "Unable to create log file '%s': %s.\n", agent_config.log_file, strerror (errno));
- exit (1);
- }
- } else {
- log_file = stdout;
- }
- ids_init ();
- objrefs_init ();
- breakpoints_init ();
- suspend_init ();
- mini_get_debug_options ()->gen_seq_points = TRUE;
- /*
- * This is needed because currently we don't handle liveness info.
- */
- mini_get_debug_options ()->mdb_optimizations = TRUE;
- #ifndef MONO_ARCH_HAVE_CONTEXT_SET_INT_REG
- /* This is needed because we can't set local variables in registers yet */
- mono_disable_optimizations (MONO_OPT_LINEARS);
- #endif
- /*
- * The stack walk done from thread_interrupt () needs to be signal safe, but it
- * isn't, since it can call into mono_aot_find_jit_info () which is not signal
- * safe (#3411). So load AOT info eagerly when the debugger is running as a
- * workaround.
- */
- mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE;
- if (!agent_config.onuncaught && !agent_config.onthrow)
- finish_agent_init (TRUE);
- }
- /*
- * finish_agent_init:
- *
- * Finish the initialization of the agent. This involves connecting the transport
- * and starting the agent thread. This is either done at startup, or
- * in response to some event like an unhandled exception.
- */
- static void
- finish_agent_init (gboolean on_startup)
- {
- int res;
- if (InterlockedCompareExchange (&inited, 1, 0) == 1)
- return;
- if (agent_config.launch) {
- char *argv [16];
- // FIXME: Generated address
- // FIXME: Races with transport_connect ()
- argv [0] = agent_config.launch;
- argv [1] = agent_config.transport;
- argv [2] = agent_config.address;
- argv [3] = NULL;
- res = g_spawn_async_with_pipes (NULL, argv, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
- if (!res) {
- fprintf (stderr, "Failed to execute '%s'.\n", agent_config.launch);
- exit (1);
- }
- }
- transport_connect (agent_config.address);
- if (!on_startup) {
- /* Do some which is usually done after sending the VMStart () event */
- vm_start_event_sent = TRUE;
- start_debugger_thread ();
- }
- }
- static void
- mono_debugger_agent_cleanup (void)
- {
- if (!inited)
- return;
- stop_debugger_thread ();
- breakpoints_cleanup ();
- objrefs_cleanup ();
- ids_cleanup ();
-
- mono_mutex_destroy (&debugger_thread_exited_mutex);
- mono_cond_destroy (&debugger_thread_exited_cond);
- }
- /*
- * SOCKET TRANSPORT
- */
- #ifndef DISABLE_SOCKET_TRANSPORT
- /*
- * recv_length:
- *
- * recv() + handle incomplete reads and EINTR
- */
- static int
- socket_transport_recv (void *buf, int len)
- {
- int res;
- int total = 0;
- int fd = conn_fd;
- int flags = 0;
- static gint32 last_keepalive;
- gint32 msecs;
- do {
- again:
- res = recv (fd, (char *) buf + total, len - total, flags);
- if (res > 0)
- total += res;
- if (agent_config.keepalive) {
- gboolean need_keepalive = FALSE;
- if (res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) {
- need_keepalive = TRUE;
- } else if (res == -1) {
- /* This could happen if recv () is interrupted repeatedly */
- msecs = mono_msec_ticks ();
- if (msecs - last_keepalive >= agent_config.keepalive) {
- need_keepalive = TRUE;
- last_keepalive = msecs;
- }
- }
- if (need_keepalive) {
- process_profiler_event (EVENT_KIND_KEEPALIVE, NULL);
- goto again;
- }
- }
- } while ((res > 0 && total < len) || (res == -1 && get_last_sock_error () == MONO_EINTR));
- return total;
- }
- #ifndef TARGET_PS3
- #define HAVE_GETADDRINFO 1
- #endif
-
- static void
- set_keepalive (void)
- {
- struct timeval tv;
- int result;
- if (!agent_config.keepalive || !conn_fd)
- return;
- tv.tv_sec = agent_config.keepalive / 1000;
- tv.tv_usec = (agent_config.keepalive % 1000) * 1000;
- result = setsockopt (conn_fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof(struct timeval));
- g_assert (result >= 0);
- }
- static int
- socket_transport_accept (int socket_fd)
- {
- conn_fd = accept (socket_fd, NULL, NULL);
- if (conn_fd == -1) {
- fprintf (stderr, "debugger-agent: Unable to listen on %d\n", socket_fd);
- } else {
- DEBUG (1, fprintf (log_file, "Accepted connection from client, connection fd=%d.\n", conn_fd));
- }
-
- return conn_fd;
- }
- static gboolean
- socket_transport_send (void *data, int len)
- {
- int res;
- do {
- res = send (conn_fd, data, len, 0);
- } while (res == -1 && get_last_sock_error () == MONO_EINTR);
- if (res != len)
- return FALSE;
- else
- return TRUE;
- }
- /*
- * socket_transport_connect:
- *
- * Connect/Listen on HOST:PORT. If HOST is NULL, generate an address and listen on it.
- */
- static void
- socket_transport_connect (const char *address)
- {
- #ifdef HAVE_GETADDRINFO
- struct addrinfo hints;
- struct addrinfo *result, *rp;
- #else
- struct hostent *result;
- #endif
- int sfd = -1, s, res;
- char port_string [128];
- char *host;
- int port;
- if (agent_config.address) {
- res = parse_address (agent_config.address, &host, &port);
- g_assert (res == 0);
- } else {
- host = NULL;
- port = 0;
- }
- conn_fd = -1;
- listen_fd = -1;
- if (host) {
- sprintf (port_string, "%d", port);
- mono_network_init ();
- /* Obtain address(es) matching host/port */
- #ifdef HAVE_GETADDRINFO
- memset (&hints, 0, sizeof (struct addrinfo));
- hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
- hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
- hints.ai_flags = 0;
- hints.ai_protocol = 0; /* Any protocol */
- s = getaddrinfo (host, port_string, &hints, &result);
- if (s != 0) {
- fprintf (stderr, "debugger-agent: Unable to resolve %s:%d: %s\n", host, port, gai_strerror (s));
- exit (1);
- }
- #else
- /* The PS3 doesn't even have _r or hstrerror () */
- result = gethostbyname (host);
- if (!result) {
- fprintf (stderr, "debugger-agent: Unable to resolve %s:%d: %d\n", host, port, h_errno);
- }
- #endif
- }
- if (agent_config.server) {
- #ifdef HAVE_GETADDRINFO
- /* Wait for a connection */
- if (!host) {
- struct sockaddr_in addr;
- socklen_t addrlen;
- /* No address, generate one */
- sfd = socket (AF_INET, SOCK_STREAM, 0);
- g_assert (sfd);
- /* This will bind the socket to a random port */
- res = listen (sfd, 16);
- if (res == -1) {
- fprintf (stderr, "debugger-agent: Unable to setup listening socket: %s\n", strerror (get_last_sock_error ()));
- exit (1);
- }
- listen_fd = sfd;
- addrlen = sizeof (addr);
- memset (&addr, 0, sizeof (addr));
- res = getsockname (sfd, (struct sockaddr*)&addr, &addrlen);
- g_assert (res == 0);
- host = (char*)"127.0.0.1";
- port = ntohs (addr.sin_port);
- /* Emit the address to stdout */
- /* FIXME: Should print another interface, not localhost */
- printf ("%s:%d\n", host, port);
- } else {
- /* Listen on the provided address */
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- int n = 1;
- sfd = socket (rp->ai_family, rp->ai_socktype,
- rp->ai_protocol);
- if (sfd == -1)
- continue;
- if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
- continue;
- res = bind (sfd, rp->ai_addr, rp->ai_addrlen);
- if (res == -1)
- continue;
- res = listen (sfd, 16);
- if (res == -1)
- continue;
- listen_fd = sfd;
- break;
- }
- #ifndef HOST_WIN32
- /*
- * this function is not present on win2000 which we still support, and the
- * workaround described here:
- * http://msdn.microsoft.com/en-us/library/ms737931(VS.85).aspx
- * only works with MSVC.
- */
- #ifdef HAVE_GETADDRINFO
- freeaddrinfo (result);
- #endif
- #endif
- }
- if (agent_config.defer)
- return;
- DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
- if (agent_config.timeout) {
- fd_set readfds;
- struct timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = agent_config.timeout * 1000;
- FD_ZERO (&readfds);
- FD_SET (sfd, &readfds);
- res = select (sfd + 1, &readfds, NULL, NULL, &tv);
- if (res == 0) {
- fprintf (stderr, "debugger-agent: Timed out waiting to connect.\n");
- exit (1);
- }
- }
- conn_fd = socket_transport_accept (sfd);
- if (conn_fd == -1)
- exit (1);
- DEBUG (1, fprintf (log_file, "Accepted connection from client, socket fd=%d.\n", conn_fd));
- #else
- NOT_IMPLEMENTED;
- #endif /* HAVE_GETADDRINFO */
- } else {
- /* Connect to the specified address */
- #ifdef HAVE_GETADDRINFO
- /* FIXME: Respect the timeout */
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- sfd = socket (rp->ai_family, rp->ai_socktype,
- rp->ai_protocol);
- if (sfd == -1)
- continue;
- if (connect (sfd, rp->ai_addr, rp->ai_addrlen) != -1)
- break; /* Success */
-
- close (sfd);
- }
- if (rp == 0) {
- fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
- exit (1);
- }
- #else
- sfd = socket (result->h_addrtype, SOCK_STREAM, 0);
- if (sfd == -1)
- g_assert_not_reached ();
- res = connect (sfd, (void*)result->h_addr_list [0], result->h_length);
- if (res == -1)
- g_assert_not_reached ();
- #endif
- conn_fd = sfd;
- #ifndef HOST_WIN32
- /* See the comment above */
- #ifdef HAVE_GETADDRINFO
- freeaddrinfo (result);
- #endif
- #endif
- }
-
- if (!transport_handshake ())
- exit (1);
- }
- static void
- socket_transport_close1 (void)
- {
- /* This will interrupt the agent thread */
- /* Close the read part only so it can still send back replies */
- /* Also shut down the connection listener so that we can exit normally */
- #ifdef HOST_WIN32
- /* SD_RECEIVE doesn't break the recv in the debugger thread */
- shutdown (conn_fd, SD_BOTH);
- shutdown (listen_fd, SD_BOTH);
- closesocket (listen_fd);
- #else
- shutdown (conn_fd, SHUT_RD);
- shutdown (listen_fd, SHUT_RDWR);
- close (listen_fd);
- #endif
- }
- static void
- socket_transport_close2 (void)
- {
- #ifdef HOST_WIN32
- shutdown (conn_fd, SD_BOTH);
- #else
- shutdown (conn_fd, SHUT_RDWR);
- #endif
- }
- static void
- register_socket_transport (void)
- {
- DebuggerTransport trans;
- trans.name = "dt_socket";
- trans.connect = socket_transport_connect;
- trans.close1 = socket_transport_close1;
- trans.close2 = socket_transport_close2;
- trans.send = socket_transport_send;
- trans.recv = socket_transport_recv;
- register_transport (&trans);
- }
- #endif /* DISABLE_SOCKET_TRANSPORT */
- /*
- * TRANSPORT CODE
- */
- #define MAX_TRANSPORTS 16
- static DebuggerTransport *transport;
- static DebuggerTransport transports [MAX_TRANSPORTS];
- static int ntransports;
- void
- mono_debugger_agent_register_transport (DebuggerTransport *trans);
- void
- mono_debugger_agent_register_transport (DebuggerTransport *trans)
- {
- register_transport (trans);
- }
- static void
- register_transport (DebuggerTransport *trans)
- {
- g_assert (ntransports < MAX_TRANSPORTS);
- memcpy (&transports [ntransports], trans, sizeof (DebuggerTransport));
- ntransports ++;
- }
- static void
- transport_init (void)
- {
- int i;
- #ifndef DISABLE_SOCKET_TRANSPORT
- register_socket_transport ();
- #endif
- for (i = 0; i < ntransports; ++i) {
- if (!strcmp (agent_config.transport, transports [i].name))
- break;
- }
- if (i == ntransports) {
- fprintf (stderr, "debugger-agent: The supported values for the 'transport' option are: ");
- for (i = 0; i < ntransports; ++i)
- fprintf (stderr, "%s'%s'", i > 0 ? ", " : "", transports [i].name);
- fprintf (stderr, "\n");
- exit (1);
- }
- transport = &transports [i];
- }
- void
- transport_connect (const char *address)
- {
- transport->connect (address);
- }
- static void
- transport_close1 (void)
- {
- transport->close1 ();
- }
- static void
- transport_close2 (void)
- {
- transport->close2 ();
- }
- static int
- transport_send (void *buf, int len)
- {
- return transport->send (buf, len);
- }
- static int
- transport_recv (void *buf, int len)
- {
- return transport->recv (buf, len);
- }
- gboolean
- mono_debugger_agent_transport_handshake (void)
- {
- return transport_handshake ();
- }
- static gboolean
- transport_handshake (void)
- {
- char handshake_msg [128];
- guint8 buf [128];
- int res;
-
- disconnected = TRUE;
-
- /* Write handshake message */
- sprintf (handshake_msg, "DWP-Handshake");
- do {
- res = transport_send (handshake_msg, strlen (handshake_msg));
- } while (res == -1 && get_last_sock_error () == MONO_EINTR);
- g_assert (res != -1);
- /* Read answer */
- res = transport_recv (buf, strlen (handshake_msg));
- if ((res != strlen (handshake_msg)) || (memcmp (buf, handshake_msg, strlen (handshake_msg) != 0))) {
- fprintf (stderr, "debugger-agent: DWP handshake failed.\n");
- return FALSE;
- }
- /*
- * To support older clients, the client sends its protocol version after connecting
- * using a command. Until that is received, default to our protocol version.
- */
- major_version = MAJOR_VERSION;
- minor_version = MINOR_VERSION;
- protocol_version_set = FALSE;
- #ifndef DISABLE_SOCKET_TRANSPORT
- // FIXME: Move this somewhere else
- /*
- * Set TCP_NODELAY on the socket so the client receives events/command
- * results immediately.
- */
- if (conn_fd) {
- int flag = 1;
- int result = setsockopt (conn_fd,
- IPPROTO_TCP,
- TCP_NODELAY,
- (char *) &flag,
- sizeof(int));
- g_assert (result >= 0);
- }
- set_keepalive ();
- #endif
-
- disconnected = FALSE;
- return TRUE;
- }
- static void
- stop_debugger_thread (void)
- {
- if (!inited)
- return;
- transport_close1 ();
- /*
- * Wait for the thread to exit.
- *
- * If we continue with the shutdown without waiting for it, then the client might
- * not receive an answer to its last command like a resume.
- * The WaitForSingleObject infrastructure doesn't seem to work during shutdown, so
- * use pthreads.
- */
- //WaitForSingleObject (debugger_thread_handle, INFINITE);
- if (GetCurrentThreadId () != debugger_thread_id) {
- do {
- mono_mutex_lock (&debugger_thread_exited_mutex);
- if (!debugger_thread_exited) {
- #ifdef HOST_WIN32
- if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) {
- mono_mutex_unlock (&debugger_thread_exited_mutex);
- Sleep(1);
- mono_mutex_lock (&debugger_thread_exited_mutex);
- }
- #else
- mono_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex);
- #endif
- }
- mono_mutex_unlock (&debugger_thread_exited_mutex);
- } while (!debugger_thread_exited);
- }
- transport_close2 ();
- }
- static void
- start_debugger_thread (void)
- {
- gsize tid;
- debugger_thread_handle = mono_create_thread (NULL, 0, debugger_thread, NULL, 0, &tid);
- g_assert (debugger_thread_handle);
- }
- /*
- * Functions to decode protocol data
- */
- static inline int
- decode_byte (guint8 *buf, guint8 **endbuf, guint8 *limit)
- {
- *endbuf = buf + 1;
- g_assert (*endbuf <= limit);
- return buf [0];
- }
- static inline int
- decode_int (guint8 *buf, guint8 **endbuf, guint8 *limit)
- {
- *endbuf = buf + 4;
- g_assert (*endbuf <= limit);
- return (((int)buf [0]) << 24) | (((int)buf [1]) << 16) | (((int)buf [2]) << 8) | (((int)buf [3]) << 0);
- }
- static inline gint64
- decode_long (guint8 *buf, guint8 **endbuf, guint8 *limit)
- {
- guint32 high = decode_int (buf, &buf, limit);
- guint32 low = decode_int (buf, &buf, limit);
- *endbuf = buf;
- return ((((guint64)high) << 32) | ((guint64)low));
- }
- static inline int
- decode_id (guint8 *buf, guint8 **endbuf, guint8 *limit)
- {
- return decode_int (buf, endbuf, limit);
- }
- static inline char*
- decode_string (guint8 *buf, guint8 **endbuf, guint8 *limit)
- {
- int len = decode_int (buf, &buf, limit);
- char *s;
- if (len < 0) {
- *endbuf = buf;
- return NULL;
- }
- s = g_malloc (len + 1);
- g_assert (s);
- memcpy (s, buf, len);
- s [len] = '\0';
- buf += len;
- *endbuf = buf;
- return s;
- }
- /*
- * Functions to encode protocol data
- */
- typedef struct {
- guint8 *buf, *p, *end;
- } Buffer;
- static inline void
- buffer_init (Buffer *buf, int size)
- {
- buf->buf = g_malloc (size);
- buf->p = buf->buf;
- buf->end = buf->buf + size;
- }
- static inline void
- buffer_make_room (Buffer *buf, int size)
- {
- if (buf->end - buf->p < size) {
- int new_size = buf->end - buf->buf + size + 32;
- guint8 *p = g_realloc (buf->buf, new_size);
- size = buf->p - buf->buf;
- buf->buf = p;
- buf->p = p + size;
- buf->end = buf->buf + new_size;
- }
- }
- static inline void
- buffer_add_byte (Buffer *buf, guint8 val)
- {
- buffer_make_room (buf, 1);
- buf->p [0] = val;
- buf->p++;
- }
- static inline void
- buffer_add_short (Buffer *buf, guint32 val)
- {
- buffer_make_room (buf, 2);
- buf->p [0] = (val >> 8) & 0xff;
- buf->p [1] = (val >> 0) & 0xff;
- buf->p += 2;
- }
- static inline void
- buffer_add_int (Buffer *buf, guint32 val)
- {
- buffer_make_room (buf, 4);
- buf->p [0] = (val >> 24) & 0xff;
- buf->p [1] = (val >> 16) & 0xff;
- buf->p [2] = (val >> 8) & 0xff;
- buf->p [3] = (val >> 0) & 0xff;
- buf->p += 4;
- }
- static inline void
- buffer_add_long (Buffer *buf, guint64 l)
- {
- buffer_add_int (buf, (l >> 32) & 0xffffffff);
- buffer_add_int (buf, (l >> 0) & 0xffffffff);
- }
- static inline void
- buffer_add_id (Buffer *buf, int id)
- {
- buffer_add_int (buf, (guint64)id);
- }
- static inline void
- buffer_add_data (Buffer *buf, guint8 *data, int len)
- {
- buffer_make_room (buf, len);
- memcpy (buf->p, data, len);
- buf->p += len;
- }
- static inline void
- buffer_add_string (Buffer *buf, const char *str)
- {
- int len;
- if (str == NULL) {
- buffer_add_int (buf, 0);
- } else {
- len = strlen (str);
- buffer_add_int (buf, len);
- buffer_add_data (buf, (guint8*)str, len);
- }
- }
- static inline void
- buffer_free (Buffer *buf)
- {
- g_free (buf->buf);
- }
- static gboolean
- send_packet (int command_set, int command, Buffer *data)
- {
- Buffer buf;
- int len, id;
- gboolean res;
- id = InterlockedIncrement (&packet_id);
- len = data->p - data->buf + 11;
- buffer_init (&buf, len);
- buffer_add_int (&buf, len);
- buffer_add_int (&buf, id);
- buffer_add_byte (&buf, 0); /* flags */
- buffer_add_byte (&buf, command_set);
- buffer_add_byte (&buf, command);
- memcpy (buf.buf + 11, data->buf, data->p - data->buf);
- res = transport_send (buf.buf, len);
- buffer_free (&buf);
- return res;
- }
- static gboolean
- send_reply_packet (int id, int error, Buffer *data)
- {
- Buffer buf;
- int len;
- gboolean res;
-
- len = data->p - data->buf + 11;
- buffer_init (&buf, len);
- buffer_add_int (&buf, len);
- buffer_add_int (&buf, id);
- buffer_add_byte (&buf, 0x80); /* flags */
- buffer_add_byte (&buf, (error >> 8) & 0xff);
- buffer_add_byte (&buf, error);
- memcpy (buf.buf + 11, data->buf, data->p - data->buf);
- res = transport_send (buf.buf, len);
- buffer_free (&buf);
- return res;
- }
- /*
- * OBJECT IDS
- */
- /*
- * Represents an object accessible by the debugger client.
- */
- typedef struct {
- /* Unique id used in the wire protocol to refer to objects */
- int id;
- /*
- * A weakref gc handle pointing to the object. The gc handle is used to
- * detect if the object was garbage collected.
- */
- guint32 handle;
- } ObjRef;
- /* Maps objid -> ObjRef */
- static GHashTable *objrefs;
- static void
- free_objref (gpointer value)
- {
- ObjRef *o = value;
- mono_gchandle_free (o->handle);
- g_free (o);
- }
- static void
- objrefs_init (void)
- {
- objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref);
- }
- static void
- objrefs_cleanup (void)
- {
- g_hash_table_destroy (objrefs);
- objrefs = NULL;
- }
- static GHashTable *obj_to_objref;
- static MonoGHashTable *suspended_objs;
- /*
- * Return an ObjRef for OBJ.
- */
- static ObjRef*
- get_objref (MonoObject *obj)
- {
- ObjRef *ref;
- GSList *reflist = NULL, *l;
- int hash = 0;
- if (obj == NULL)
- return 0;
- mono_loader_lock ();
- if (!obj_to_objref) {
- obj_to_objref = g_hash_table_new (NULL, NULL);
- suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
- MONO_GC_REGISTER_ROOT_FIXED (suspended_objs);
- }
- if (suspend_count) {
- /*
- * Have to keep object refs created during suspensions alive for the duration of the suspension, so GCs during invokes don't collect them.
- */
- mono_g_hash_table_insert (suspended_objs, obj, NULL);
- }
-
- /* FIXME: The tables can grow indefinitely */
- if (mono_gc_is_moving ()) {
- /*
- * Objects can move, so use a hash table mapping hash codes to lists of
- * ObjRef structures.
- */
- hash = mono_object_hash (obj);
- reflist = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (hash));
- for (l = reflist; l; l = l->next) {
- ref = l->data;
- if (ref && mono_gchandle_get_target (ref->handle) == obj) {
- mono_loader_unlock ();
- return ref;
- }
- }
- } else {
- /* Use a hash table with masked pointers to internalize object references */
- ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
- /* ref might refer to a different object with the same addr which was GCd */
- if (ref && mono_gchandle_get_target (ref->handle) == obj) {
- mono_loader_unlock ();
- return ref;
- }
- }
- ref = g_new0 (ObjRef, 1);
- ref->id = InterlockedIncrement (&objref_id);
- ref->handle = mono_gchandle_new_weakref (obj, FALSE);
- g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
- if (mono_gc_is_moving ()) {
- reflist = g_slist_append (reflist, ref);
- g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (hash), reflist);
- } else {
- g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
- }
- mono_loader_unlock ();
- return ref;
- }
- static gboolean
- true_pred (gpointer key, gpointer value, gpointer user_data)
- {
- return TRUE;
- }
- static void
- clear_suspended_objs (void)
- {
- mono_loader_lock ();
- mono_g_hash_table_foreach_remove (suspended_objs, true_pred, NULL);
- mono_loader_unlock ();
- }
- static inline int
- get_objid (MonoObject *obj)
- {
- return get_objref (obj)->id;
- }
- /*
- * Set OBJ to the object identified by OBJID.
- * Returns 0 or an error code if OBJID is invalid or the object has been garbage
- * collected.
- */
- static ErrorCode
- get_object_allow_null (int objid, MonoObject **obj)
- {
- ObjRef *ref;
- if (objid == 0) {
- *obj = NULL;
- return 0;
- }
- if (!objrefs)
- return ERR_INVALID_OBJECT;
- mono_loader_lock ();
- ref = g_hash_table_lookup (objrefs, GINT_TO_POINTER (objid));
- if (ref) {
- *obj = mono_gchandle_get_target (ref->handle);
- mono_loader_unlock ();
- if (!(*obj))
- return ERR_INVALID_OBJECT;
- return 0;
- } else {
- mono_loader_unlock ();
- return ERR_INVALID_OBJECT;
- }
- }
- static ErrorCode
- get_object (int objid, MonoObject **obj)
- {
- int err = get_object_allow_null (objid, obj);
- if (err)
- return err;
- if (!(*obj))
- return ERR_INVALID_OBJECT;
- return 0;
- }
- static inline int
- decode_objid (guint8 *buf, guint8 **endbuf, guint8 *limit)
- {
- return decode_id (buf, endbuf, limit);
- }
- static inline void
- buffer_add_objid (Buffer *buf, MonoObject *o)
- {
- buffer_add_id (buf, get_objid (o));
- }
- /*
- * IDS
- */
- typedef enum {
- ID_ASSEMBLY = 0,
- ID_MODULE = 1,
- ID_TYPE = 2,
- ID_METHOD = 3,
- ID_FIELD = 4,
- ID_DOMAIN = 5,
- ID_PROPERTY = 6,
- ID_NUM
- } IdType;
- /*
- * Represents a runtime structure accessible to the debugger client
- */
- typedef struct {
- /* Unique id used in the wire protocol */
- int id;
- /* Domain of the runtime structure, NULL if the domain was unloaded */
- MonoDomain *domain;
- union {
- gpointer val;
- MonoClass *klass;
- MonoMethod *method;
- MonoImage *image;
- MonoAssembly *assembly;
- MonoClassField *field;
- MonoDomain *domain;
- MonoProperty *property;
- } data;
- } Id;
- typedef struct {
- /* Maps runtime structure -> Id */
- GHashTable *val_to_id [ID_NUM];
- /* Classes whose class load event has been sent */
- GHashTable *loaded_classes;
- /* Maps MonoClass->GPtrArray of file names */
- GHashTable *source_files;
- /* Maps source file basename -> GSList of classes */
- GHashTable *source_file_to_class;
- /* Same with ignore-case */
- GHashTable *source_file_to_class_ignorecase;
- } AgentDomainInfo;
- /* Maps id -> Id */
- static GPtrArray *ids [ID_NUM];
- static void
- ids_init (void)
- {
- int i;
- for (i = 0; i < ID_NUM; ++i)
- ids [i] = g_ptr_array_new ();
- }
- static void
- ids_cleanup (void)
- {
- int i, j;
- for (i = 0; i < ID_NUM; ++i) {
- if (ids [i]) {
- for (j = 0; j < ids [i]->len; ++j)
- g_free (g_ptr_array_index (ids [i], j));
- g_ptr_array_free (ids [i], TRUE);
- }
- ids [i] = NULL;
- }
- }
- void
- mono_debugger_agent_free_domain_info (MonoDomain *domain)
- {
- AgentDomainInfo *info = domain_jit_info (domain)->agent_info;
- int i, j;
- GHashTableIter iter;
- GPtrArray *file_names;
- char *basename;
- GSList *l;
- if (info) {
- for (i = 0; i < ID_NUM; ++i)
- if (info->val_to_id [i])
- g_hash_table_destroy (info->val_to_id [i]);
- g_hash_table_destroy (info->loaded_classes);
- g_hash_table_iter_init (&iter, info->source_files);
- while (g_hash_table_iter_next (&iter, NULL, (void**)&file_names)) {
- for (i = 0; i < file_names->len; ++i)
- g_free (g_ptr_array_index (file_names, i));
- g_ptr_array_free (file_names, TRUE);
- }
- g_hash_table_iter_init (&iter, info->source_file_to_class);
- while (g_hash_table_iter_next (&iter, (void**)&basename, (void**)&l)) {
- g_free (basename);
- g_slist_free (l);
- }
- g_hash_table_iter_init (&iter, info->source_file_to_class_ignorecase);
- while (g_hash_table_iter_next (&iter, (void**)&basename, (void**)&l)) {
- g_free (basename);
- g_slist_free (l);
- }
- g_free (info);
- }
- domain_jit_info (domain)->agent_info = NULL;
- /* Clear ids referencing structures in the domain */
- for (i = 0; i < ID_NUM; ++i) {
- if (ids [i]) {
- for (j = 0; j < ids [i]->len; ++j) {
- Id *id = g_ptr_array_index (ids [i], j);
- if (id->domain == do…
Large files files are truncated, but you can click here to view the full file