X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fdebug-mini.c;h=123ff4f374efc61b4656b68f9fa0c41ce71855f1;hb=bb2bc9ae429c671323bfe64cac16a5cf75021a4c;hp=2b12382e5cdb7472073e05a3b43e718457f5063c;hpb=1083cc11c791387badf74df2c585d7079ef7b5f9;p=mono.git diff --git a/mono/mini/debug-mini.c b/mono/mini/debug-mini.c index 2b12382e5cd..123ff4f374e 100644 --- a/mono/mini/debug-mini.c +++ b/mono/mini/debug-mini.c @@ -20,9 +20,7 @@ #include #include "debug-mini.h" -#ifdef HAVE_VALGRIND_H -#include -#endif +#include #ifdef MONO_DEBUGGER_SUPPORTED #include @@ -41,6 +39,24 @@ typedef struct guint32 breakpoint_id; } MiniDebugMethodInfo; +typedef struct { + MonoObject *last_exception; + guint32 stopped_on_exception : 1; + guint32 stopped_on_unhandled : 1; +} MonoDebuggerExceptionState; + +typedef enum { + MONO_DEBUGGER_THREAD_FLAGS_NONE = 0, + MONO_DEBUGGER_THREAD_FLAGS_INTERNAL = 1, + MONO_DEBUGGER_THREAD_FLAGS_THREADPOOL = 2 +} MonoDebuggerThreadFlags; + +typedef enum { + MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_NONE = 0, + MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE = 1, + MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED = 2 +} MonoDebuggerInternalThreadFlags; + struct _MonoDebuggerThreadInfo { guint64 tid; guint64 lmf_addr; @@ -59,13 +75,26 @@ struct _MonoDebuggerThreadInfo { guint32 stack_size; guint32 signal_stack_size; + guint32 thread_flags; + /* * The debugger doesn't access anything beyond this point. */ + MonoDebuggerExceptionState exception_state; + + guint32 internal_flags; + MonoJitTlsData *jit_tls; - MonoThread *thread; + MonoInternalThread *thread; }; +typedef struct { + gpointer stack_pointer; + MonoObject *exception_obj; + guint32 stop; + guint32 stop_unhandled; +} MonoDebuggerExceptionInfo; + MonoDebuggerThreadInfo *mono_debugger_thread_table = NULL; static inline void @@ -107,7 +136,7 @@ mono_debug_open_method (MonoCompile *cfg) mono_class_init (cfg->method->klass); - header = mono_method_get_header (cfg->method); + header = cfg->header; g_assert (header); info->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1); @@ -125,10 +154,20 @@ write_variable (MonoInst *inst, MonoDebugVarInfo *var) var->index = inst->dreg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER; else if (inst->flags & MONO_INST_IS_DEAD) var->index = MONO_DEBUG_VAR_ADDRESS_MODE_DEAD; - else { + else if (inst->opcode == OP_REGOFFSET) { /* the debug interface needs fixing to allow 0(%base) address */ var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET; var->offset = inst->inst_offset; + } else if (inst->opcode == OP_GSHAREDVT_ARG_REGOFFSET) { + var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR; + var->offset = inst->inst_offset; + } else if (inst->opcode == OP_GSHAREDVT_LOCAL) { + var->index = inst->inst_imm | MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL; + } else if (inst->opcode == OP_VTARG_ADDR) { + var->offset = inst->inst_offset; + var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR; + } else { + g_assert_not_reached (); } } @@ -230,6 +269,7 @@ mono_debug_add_vg_method (MonoMethod *method, MonoDebugMethodJitInfo *jit) g_free (addresses); g_free (lines); + mono_metadata_free_mh (header); #endif /* VALGRIND_ADD_LINE_INFO */ } @@ -252,7 +292,7 @@ mono_debug_close_method (MonoCompile *cfg) } method = cfg->method; - header = mono_method_get_header (method); + header = cfg->header; sig = mono_method_signature (method); jit = info->jit; @@ -277,24 +317,41 @@ mono_debug_close_method (MonoCompile *cfg) for (i = 0; i < jit->num_params; i++) write_variable (cfg->args [i + sig->hasthis], &jit->params [i]); + if (cfg->gsharedvt_info_var) { + jit->gsharedvt_info_var = g_new0 (MonoDebugVarInfo, 1); + jit->gsharedvt_locals_var = g_new0 (MonoDebugVarInfo, 1); + write_variable (cfg->gsharedvt_info_var, jit->gsharedvt_info_var); + write_variable (cfg->gsharedvt_locals_var, jit->gsharedvt_locals_var); + } + jit->num_line_numbers = info->line_numbers->len; jit->line_numbers = g_new0 (MonoDebugLineNumberEntry, jit->num_line_numbers); for (i = 0; i < jit->num_line_numbers; i++) jit->line_numbers [i] = g_array_index (info->line_numbers, MonoDebugLineNumberEntry, i); - debug_info = mono_debug_add_method (method, jit, cfg->domain); + debug_info = mono_debug_add_method (cfg->method_to_register, jit, cfg->domain); mono_debug_add_vg_method (method, jit); - if (info->breakpoint_id) - mono_debugger_breakpoint_callback (method, info->breakpoint_id); - mono_debugger_check_breakpoints (method, debug_info); mono_debug_free_method_jit_info (jit); - g_array_free (info->line_numbers, TRUE); - g_free (info); + mono_debug_free_method (cfg); +} + +void +mono_debug_free_method (MonoCompile *cfg) +{ + MiniDebugMethodInfo *info; + + info = (MiniDebugMethodInfo *) cfg->debug_info; + if (info) { + if (info->line_numbers) + g_array_free (info->line_numbers, TRUE); + g_free (info); + cfg->debug_info = NULL; + } } void @@ -308,7 +365,7 @@ mono_debug_record_line_number (MonoCompile *cfg, MonoInst *ins, guint32 address) if (!info || !info->jit || !ins->cil_code) return; - header = mono_method_get_header (cfg->method); + header = cfg->header; g_assert (header); if ((ins->cil_code < header->code) || @@ -335,7 +392,7 @@ mono_debug_open_block (MonoCompile *cfg, MonoBasicBlock *bb, guint32 address) if (!info || !info->jit || !bb->cil_code) return; - header = mono_method_get_header (cfg->method); + header = cfg->header; g_assert (header); if ((bb->cil_code < header->code) || @@ -428,8 +485,11 @@ serialize_variable (MonoDebugVarInfo *var, guint8 *p, guint8 **endbuf) case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: break; case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: encode_value (var->offset, p, &p); break; + case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: + case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: break; default: @@ -468,6 +528,14 @@ mono_debug_serialize_debug_info (MonoCompile *cfg, guint8 **out_buf, guint32 *bu for (i = 0; i < jit->num_locals; i++) serialize_variable (&jit->locals [i], p, &p); + if (jit->gsharedvt_info_var) { + encode_value (1, p, &p); + serialize_variable (jit->gsharedvt_info_var, p, &p); + serialize_variable (jit->gsharedvt_locals_var, p, &p); + } else { + encode_value (0, p, &p); + } + encode_value (jit->num_line_numbers, p, &p); prev_offset = 0; @@ -500,8 +568,11 @@ deserialize_variable (MonoDebugVarInfo *var, guint8 *p, guint8 **endbuf) case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: break; case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: var->offset = decode_value (p, &p); break; + case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: + case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: break; default: @@ -545,6 +616,13 @@ deserialize_debug_info (MonoMethod *method, guint8 *code_start, guint8 *buf, gui for (i = 0; i < jit->num_locals; i++) deserialize_variable (&jit->locals [i], p, &p); + if (decode_value (p, &p)) { + jit->gsharedvt_info_var = g_new0 (MonoDebugVarInfo, 1); + jit->gsharedvt_locals_var = g_new0 (MonoDebugVarInfo, 1); + deserialize_variable (jit->gsharedvt_info_var, p, &p); + deserialize_variable (jit->gsharedvt_locals_var, p, &p); + } + jit->num_line_numbers = decode_value (p, &p); jit->line_numbers = g_new0 (MonoDebugLineNumberEntry, jit->num_line_numbers); @@ -563,6 +641,7 @@ deserialize_debug_info (MonoMethod *method, guint8 *code_start, guint8 *buf, gui prev_native_offset = native_offset; } + mono_metadata_free_mh (header); return jit; } @@ -613,6 +692,15 @@ print_var_info (MonoDebugVarInfo *info, int idx, const char *name, const char *t case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: g_print ("%s %s (%d) in memory: base register %s + %d\n", type, name, idx, mono_arch_regname (info->index & (~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS)), info->offset); break; + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: + g_print ("%s %s (%d) in indir memory: base register %s + %d\n", type, name, idx, mono_arch_regname (info->index & (~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS)), info->offset); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: + g_print ("%s %s (%d) gsharedvt local.\n", type, name, idx); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: + g_print ("%s %s (%d) vt address: base register %s + %d\n", type, name, idx, mono_arch_regname (info->index & (~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS)), info->offset); + break; case MONO_DEBUG_VAR_ADDRESS_MODE_TWO_REGISTERS: default: g_assert_not_reached (); @@ -641,14 +729,14 @@ mono_debug_print_vars (gpointer ip, gboolean only_arguments) if (!ji) return; - jit = mono_debug_find_method (mono_jit_info_get_method (ji), domain); + jit = mono_debug_find_method (jinfo_get_method (ji), domain); if (!jit) return; if (only_arguments) { char **names; names = g_new (char *, jit->num_params); - mono_method_get_param_names (mono_jit_info_get_method (ji), (const char **) names); + mono_method_get_param_names (jinfo_get_method (ji), (const char **) names); if (jit->this_var) print_var_info (jit->this_var, 0, "this", "Arg"); for (i = 0; i < jit->num_params; ++i) { @@ -731,7 +819,7 @@ mono_debugger_method_has_breakpoint (MonoMethod *method) { int i; - if (!breakpoints || (method->wrapper_type != MONO_WRAPPER_NONE)) + if (!breakpoints) return 0; for (i = 0; i < breakpoints->len; i++) { @@ -753,7 +841,7 @@ mono_debugger_breakpoint_callback (MonoMethod *method, guint32 index) } void -mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit_tls) +mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit_tls, gpointer func) { #ifdef MONO_DEBUGGER_SUPPORTED size_t stsize = 0; @@ -769,7 +857,7 @@ mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit info = g_new0 (MonoDebuggerThreadInfo, 1); info->tid = tid; - info->thread = thread; + info->thread = thread->internal_thread; info->stack_start = (guint64) (gsize) staddr; info->signal_stack_start = (guint64) (gsize) jit_tls->signal_stack; info->stack_size = stsize; @@ -778,6 +866,11 @@ mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit info->lmf_addr = (guint64) (gsize) mono_get_lmf_addr (); info->jit_tls = jit_tls; + if (func) + info->thread_flags = MONO_DEBUGGER_THREAD_FLAGS_INTERNAL; + if (thread->internal_thread->threadpool_thread) + info->thread_flags |= MONO_DEBUGGER_THREAD_FLAGS_THREADPOOL; + info->next = mono_debugger_thread_table; mono_debugger_thread_table = info; @@ -822,7 +915,7 @@ mono_debugger_extended_notification (MonoDebuggerEvent event, guint64 data, guin { #ifdef MONO_DEBUGGER_SUPPORTED MonoDebuggerThreadInfo **ptr; - MonoThread *thread = mono_thread_current (); + MonoInternalThread *thread = mono_thread_internal_current (); if (!mono_debug_using_mono_debugger ()) return; @@ -846,8 +939,335 @@ mono_debugger_extended_notification (MonoDebuggerEvent event, guint64 data, guin } void -mono_debugger_trampoline_compiled (MonoMethod *method, const guint8 *code) +mono_debugger_trampoline_compiled (const guint8 *trampoline, MonoMethod *method, const guint8 *code) { - mono_debugger_extended_notification (MONO_DEBUGGER_EVENT_TRAMPOLINE, +#ifdef MONO_DEBUGGER_SUPPORTED + struct { + const guint8 * trampoline; + MonoMethod *method; + const guint8 *code; + } info = { trampoline, method, code }; + + mono_debugger_extended_notification (MONO_DEBUGGER_EVENT_OLD_TRAMPOLINE, (guint64) (gsize) method, (guint64) (gsize) code); + mono_debugger_extended_notification (MONO_DEBUGGER_EVENT_TRAMPOLINE, + (guint64) (gsize) &info, 0); +#endif +} + +#if MONO_DEBUGGER_SUPPORTED +static MonoDebuggerThreadInfo * +find_debugger_thread_info (MonoInternalThread *thread) +{ + MonoDebuggerThreadInfo **ptr; + + for (ptr = &mono_debugger_thread_table; *ptr; ptr = &(*ptr)->next) { + MonoDebuggerThreadInfo *info = *ptr; + + if (info->thread == thread) + return info; + } + + return NULL; +} +#endif + +MonoDebuggerExceptionAction +_mono_debugger_throw_exception (gpointer addr, gpointer stack, MonoObject *exc) +{ +#ifdef MONO_DEBUGGER_SUPPORTED + MonoDebuggerExceptionInfo exc_info; + MonoDebuggerThreadInfo *thread_info; + + if (!mono_debug_using_mono_debugger ()) + return MONO_DEBUGGER_EXCEPTION_ACTION_NONE; + + mono_debugger_lock (); + + thread_info = find_debugger_thread_info (mono_thread_internal_current ()); + if (!thread_info) { + mono_debugger_unlock (); + return MONO_DEBUGGER_EXCEPTION_ACTION_NONE; + } + + if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) { + mono_debugger_unlock (); + return MONO_DEBUGGER_EXCEPTION_ACTION_NONE; + } + + if (thread_info->exception_state.stopped_on_exception || + thread_info->exception_state.stopped_on_unhandled) { + thread_info->exception_state.stopped_on_exception = 0; + mono_debugger_unlock (); + return MONO_DEBUGGER_EXCEPTION_ACTION_NONE; + } + + /* Protect the exception object from being garbage collected. */ + + thread_info->exception_state.stopped_on_unhandled = 0; + thread_info->exception_state.stopped_on_exception = 1; + thread_info->exception_state.last_exception = exc; + + /* + * Backwards compatibility: + * + * Older debugger versions only know `exc_info.stop' and older runtime versions check + * `exc_info.stop != 0'. + * + * The debugger must check for `mono_debug_debugger_version >= 5' before accessing the + * `stop_unhandled' field. + */ + + exc_info.stack_pointer = stack; + exc_info.exception_obj = exc; + exc_info.stop = 0; + exc_info.stop_unhandled = 0; + + mono_debugger_event (MONO_DEBUGGER_EVENT_THROW_EXCEPTION, (guint64) (gsize) &exc_info, + (guint64) (gsize) addr); + + if (!exc_info.stop) { + thread_info->exception_state.stopped_on_exception = 0; + thread_info->exception_state.last_exception = NULL; + } + + mono_debugger_unlock (); + + if (exc_info.stop) + return MONO_DEBUGGER_EXCEPTION_ACTION_STOP; + else if (exc_info.stop_unhandled) + return MONO_DEBUGGER_EXCEPTION_ACTION_STOP_UNHANDLED; +#endif + + return MONO_DEBUGGER_EXCEPTION_ACTION_NONE; +} + +gboolean +_mono_debugger_unhandled_exception (gpointer addr, gpointer stack, MonoObject *exc) +{ +#ifdef MONO_DEBUGGER_SUPPORTED + MonoDebuggerThreadInfo *thread_info; + + if (!mono_debug_using_mono_debugger ()) + return FALSE; + + if (exc) { + const gchar *name = mono_class_get_name (mono_object_get_class (exc)); + if (!strcmp (name, "ThreadAbortException")) + return FALSE; + } + + mono_debugger_lock (); + + thread_info = find_debugger_thread_info (mono_thread_internal_current ()); + if (!thread_info) { + mono_debugger_unlock (); + return FALSE; + } + + if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) { + mono_debugger_unlock (); + return FALSE; + } + + if (thread_info->exception_state.stopped_on_unhandled) { + thread_info->exception_state.stopped_on_unhandled = 0; + mono_debugger_unlock (); + return FALSE; + } + + thread_info->exception_state.stopped_on_unhandled = 1; + thread_info->exception_state.last_exception = exc; + + mono_debugger_event (MONO_DEBUGGER_EVENT_UNHANDLED_EXCEPTION, + (guint64) (gsize) exc, (guint64) (gsize) addr); + + return TRUE; +#else + return FALSE; +#endif +} + +/* + * mono_debugger_call_exception_handler: + * + * Called from mono_handle_exception_internal() to tell the debugger that we're about + * to invoke an exception handler. + * + * The debugger may choose to set a breakpoint at @addr. This is used if the user is + * single-stepping from a `try' into a `catch' block, for instance. + */ + +void +mono_debugger_call_exception_handler (gpointer addr, gpointer stack, MonoObject *exc) +{ +#ifdef MONO_DEBUGGER_SUPPORTED + MonoDebuggerThreadInfo *thread_info; + MonoDebuggerExceptionInfo exc_info; + + if (!mono_debug_using_mono_debugger ()) + return; + + mono_debugger_lock (); + + thread_info = find_debugger_thread_info (mono_thread_internal_current ()); + if (!thread_info) { + mono_debugger_unlock (); + return; + } + + if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) { + mono_debugger_unlock (); + return; + } + + // Prevent the object from being finalized. + thread_info->exception_state.last_exception = exc; + + exc_info.stack_pointer = stack; + exc_info.exception_obj = exc; + exc_info.stop = 0; + exc_info.stop_unhandled = 0; + + mono_debugger_event (MONO_DEBUGGER_EVENT_HANDLE_EXCEPTION, (guint64) (gsize) &exc_info, + (guint64) (gsize) addr); + + mono_debugger_unlock (); +#endif } + +#ifdef MONO_DEBUGGER_SUPPORTED + +static gchar * +get_exception_message (MonoObject *exc) +{ + char *message = NULL; + MonoString *str; + MonoMethod *method; + MonoClass *klass; + gint i; + + if (mono_object_isinst (exc, mono_defaults.exception_class)) { + klass = exc->vtable->klass; + method = NULL; + while (klass && method == NULL) { + for (i = 0; i < klass->method.count; ++i) { + method = klass->methods [i]; + if (!strcmp ("ToString", method->name) && + mono_method_signature (method)->param_count == 0 && + method->flags & METHOD_ATTRIBUTE_VIRTUAL && + method->flags & METHOD_ATTRIBUTE_PUBLIC) { + break; + } + method = NULL; + } + + if (method == NULL) + klass = klass->parent; + } + + g_assert (method); + + str = (MonoString *) mono_runtime_invoke (method, exc, NULL, NULL); + if (str) + message = mono_string_to_utf8 (str); + } + + return message; +} + +MonoObject * +mono_debugger_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc) +{ + MonoDebuggerThreadInfo *thread_info; + MonoDebuggerExceptionState saved_exception_state; + MonoObject *retval; + gchar *message; + + mono_debugger_lock (); + + thread_info = find_debugger_thread_info (mono_thread_internal_current ()); + if (!thread_info) { + mono_debugger_unlock (); + return NULL; + } + + saved_exception_state = thread_info->exception_state; + + thread_info->exception_state.last_exception = NULL; + thread_info->exception_state.stopped_on_unhandled = 0; + thread_info->exception_state.stopped_on_exception = 0; + + thread_info->internal_flags |= MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE; + + mono_debugger_unlock (); + + if (!strcmp (method->name, ".ctor")) { + retval = obj = mono_object_new (mono_domain_get (), method->klass); + + mono_runtime_invoke (method, obj, params, exc); + } else + retval = mono_runtime_invoke (method, obj, params, exc); + + mono_debugger_lock (); + + thread_info->exception_state = saved_exception_state; + thread_info->internal_flags &= ~MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE; + + if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) { + thread_info->internal_flags &= ~MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED; + mono_thread_internal_reset_abort (thread_info->thread); + + mono_debugger_unlock (); + + *exc = NULL; + return NULL; + } + + mono_debugger_unlock (); + + if (!exc || (*exc == NULL)) + return retval; + + retval = *exc; + message = get_exception_message (*exc); + if (message) { + *exc = (MonoObject *) mono_string_new_wrapper (message); + g_free (message); + } + + return retval; +} + +gboolean +mono_debugger_abort_runtime_invoke () +{ + MonoInternalThread *thread = mono_thread_internal_current (); + MonoDebuggerThreadInfo *thread_info; + + mono_debugger_lock (); + + thread_info = find_debugger_thread_info (thread); + if (!thread_info) { + mono_debugger_unlock (); + return FALSE; + } + + if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE) == 0) { + mono_debugger_unlock (); + return FALSE; + } + + if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) { + mono_debugger_unlock (); + return TRUE; + } + + thread_info->internal_flags |= MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED; + ves_icall_System_Threading_Thread_Abort (thread_info->thread, NULL); + + mono_debugger_unlock (); + return TRUE; +} + +#endif