diff --git a/vm/vmcore/include/jvmti_internal.h b/vm/vmcore/include/jvmti_internal.h index 47ba766..fd5b09c 100644 --- a/vm/vmcore/include/jvmti_internal.h +++ b/vm/vmcore/include/jvmti_internal.h @@ -89,6 +89,19 @@ struct BreakPoint { BreakPoint(TIEnv *_env) : method(NULL), location(0), next(NULL), env(_env) {} }; +struct jvmti_StepLocation +{ + struct Method* method; + unsigned location; +}; + +struct JVMTISingleStepState +{ + bool enabled; + BreakPoint **predicted_breakpoints; + unsigned predicted_bp_count; +}; + /* * Type which will describe one watched field */ @@ -304,12 +317,12 @@ class DebugUtilsTI { return NULL; } - BreakPoint *get_other_breakpoint_same_location(jmethodID m, jlocation l, TIEnv *env) + BreakPoint *get_other_breakpoint_same_location(jmethodID m, jlocation l) { // assert(brkpntlst_lock._lock_or_null()); for (BreakPoint *bp = brkpntlst; NULL != bp; bp = bp->next) - if (bp->method == m && bp->location == l && bp->env != env) + if (bp->method == m && bp->location == l) return bp; return NULL; @@ -452,6 +465,15 @@ class DebugUtilsTI { return global_capabilities & ti_gc; } + bool is_single_step_enabled(void) + { + return single_step_enabled; + } + + // Single step functions + jvmtiError jvmti_single_step_start(void); + jvmtiError jvmti_single_step_stop(void); + private: protected: @@ -469,13 +491,9 @@ class DebugUtilsTI { Class **notifyPrepareList; unsigned prepareListNumber; unsigned global_capabilities; + bool single_step_enabled; }; /* end of class DebugUtilsTI */ -struct jvmti_StepLocation { - struct Method* method; - unsigned location; -}; - jvmtiError add_event_to_thread(jvmtiEnv *env, jvmtiEvent event_type, jthread event_thread); void remove_event_from_thread(jvmtiEnv *env, jvmtiEvent event_type, jthread event_thread); void add_event_to_global(jvmtiEnv *env, jvmtiEvent event_type); @@ -485,6 +503,16 @@ jthread getCurrentThread(); jint load_agentlib(Agent *agent, const char *str, JavaVM_Internal *vm); jint load_agentpath(Agent *agent, const char *str, JavaVM_Internal *vm); +// Breakpoints internal functions +jvmtiError jvmti_get_next_bytecodes_up_stack_from_native(VM_thread *thread, + jvmti_StepLocation **next_step, unsigned *count); +jvmtiError jvmti_set_breakpoint_for_jit(DebugUtilsTI *ti, BreakPoint *bp); +void jvmti_remove_breakpoint_for_jit(DebugUtilsTI *ti, BreakPoint *bp); +jvmtiError jvmti_set_single_step_breakpoints(DebugUtilsTI *ti, + VM_thread *vm_thread, jvmti_StepLocation *locations, + unsigned locations_number); +void jvmti_remove_single_step_breakpoints(DebugUtilsTI *ti, VM_thread *vm_thread); + // Object check functions Boolean is_valid_thread_object(jthread thread); Boolean is_valid_thread_group_object(jthreadGroup group); @@ -494,7 +522,7 @@ Boolean is_valid_class_object(jclass kla jvmtiError jvmti_translate_jit_error(OpenExeJpdaError error); // Single step support -void jvmti_SingleStepLocation( VM_thread* thread, struct Method *method, unsigned location, - jvmti_StepLocation **next_step, unsigned *count); +void jvmti_SingleStepLocation(VM_thread* thread, Method *method, + unsigned location, jvmti_StepLocation **next_step, unsigned *count); #endif /* _JVMTI_INTERNAL_H_ */ diff --git a/vm/vmcore/include/vm_threads.h b/vm/vmcore/include/vm_threads.h index bad3999..470548d 100644 --- a/vm/vmcore/include/vm_threads.h +++ b/vm/vmcore/include/vm_threads.h @@ -58,6 +58,7 @@ enum gc_state { struct jvmti_frame_pop_listener; +struct JVMTISingleStepState; class VmRegisterContext; @@ -101,6 +102,7 @@ public: // has been processed jbyte jvmti_jit_breakpoints_handling_buffer[50]; jvmti_frame_pop_listener *frame_pop_listener; + JVMTISingleStepState *ss_state; // CPU registers. Registers regs; diff --git a/vm/vmcore/src/exception/exceptions_jit.cpp b/vm/vmcore/src/exception/exceptions_jit.cpp index 88ab87d..59a7829 100644 --- a/vm/vmcore/src/exception/exceptions_jit.cpp +++ b/vm/vmcore/src/exception/exceptions_jit.cpp @@ -213,6 +213,19 @@ #endif // VM_STATS interrupted_method_jit = interrupted_cci->get_jit(); } + // Remove single step breakpoints which could have been set on the + // exception bytecode + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + if (ti->isEnabled() && ti->is_single_step_enabled()) + { + VM_thread *vm_thread = p_TLS_vmthread; + if (vm_thread->ss_state->enabled) + { + LMAutoUnlock lock(&ti->brkpntlst_lock); + jvmti_remove_single_step_breakpoints(ti, vm_thread); + } + } + bool same_frame = true; while (!si_is_past_end(si) && !si_is_native(si)) { CodeChunkInfo *cci = si_get_code_chunk_info(si); @@ -248,6 +261,29 @@ #endif // VM_STATS jit->fix_handler_context(method, si_get_jit_context(si)); si_set_ip(si, handler->get_handler_ip(), false); + // Start single step in exception handler + if (ti->isEnabled() && ti->is_single_step_enabled()) + { + VM_thread *vm_thread = p_TLS_vmthread; + if (vm_thread->ss_state->enabled) + { + LMAutoUnlock lock(&ti->brkpntlst_lock); + + uint16 bc; + OpenExeJpdaError UNREF result = + jit->get_bc_location_for_native( + method, handler->get_handler_ip(), &bc); + assert(EXE_ERROR_NONE == result); + + jvmti_StepLocation method_start = {(Method *)method, bc}; + + jvmtiError UNREF errorCode = + jvmti_set_single_step_breakpoints(ti, vm_thread, + &method_start, 1); + assert(JVMTI_ERROR_NONE == errorCode); + } + } + // Create exception if necessary if (!*exn_obj) { if (handler->is_exc_obj_dead()) { diff --git a/vm/vmcore/src/jit/ini.cpp b/vm/vmcore/src/jit/ini.cpp index f4c06eb..1e29924 100644 --- a/vm/vmcore/src/jit/ini.cpp +++ b/vm/vmcore/src/jit/ini.cpp @@ -36,8 +36,32 @@ vm_execute_java_method_array(jmethodID m assert(NULL != VM_Global_State::loader_env); assert(NULL != VM_Global_State::loader_env->em_interface); assert(NULL != VM_Global_State::loader_env->em_interface->ExecuteMethod); + + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + if (ti->isEnabled() && ti->is_single_step_enabled()) + { + VM_thread *vm_thread = p_TLS_vmthread; + if (vm_thread->ss_state->enabled) + { + // Start single stepping a new Java method + LMAutoUnlock lock(&ti->brkpntlst_lock); + jvmti_remove_single_step_breakpoints(ti, vm_thread); + + jvmti_StepLocation method_start = {(Method *)method, 0}; + jvmtiError UNREF errorCode = jvmti_set_single_step_breakpoints( + ti, vm_thread, &method_start, 1); + assert(JVMTI_ERROR_NONE == errorCode); + } + } + VM_Global_State::loader_env->em_interface->ExecuteMethod(method, result, args); //DEBUG_POP_LOCK(JAVA_CODE_PSEUDO_LOCK); - + + // gregory - When method executes *return bytecode in Java it will + // set a breakpoint on the bytecode after invoke* or another + // bytecode which caused managed to VM transition. This is a + // general case of *return handling. So it seems here we don't + // have to do anything, *return handling will set a breakpoint up + // the stack in java automatically. } diff --git a/vm/vmcore/src/jvmti/jvmti.cpp b/vm/vmcore/src/jvmti/jvmti.cpp index 6f77cc7..1565011 100644 --- a/vm/vmcore/src/jvmti/jvmti.cpp +++ b/vm/vmcore/src/jvmti/jvmti.cpp @@ -307,7 +307,8 @@ DebugUtilsTI::DebugUtilsTI() : MAX_NOTIFY_LIST(1000), loadListNumber(0), prepareListNumber(0), - global_capabilities(0) + global_capabilities(0), + single_step_enabled(false) { jvmtiError UNUSED res = _allocate( MAX_NOTIFY_LIST * sizeof(Class**), (unsigned char**)¬ifyLoadList ); diff --git a/vm/vmcore/src/jvmti/jvmti_break.cpp b/vm/vmcore/src/jvmti/jvmti_break.cpp index 57abf62..75c631e 100644 --- a/vm/vmcore/src/jvmti/jvmti_break.cpp +++ b/vm/vmcore/src/jvmti/jvmti_break.cpp @@ -166,7 +166,77 @@ #endif // Copy disassembler instance in case a breakpoint is deleted // inside of callbacks InstructionDisassembler idisasm(*bp->disasm); + JNIEnv *jni_env = (JNIEnv *)jni_native_intf; + BreakPoint *ss_breakpoint = NULL; + // Check if there are Single Step type breakpoints in TLS + if (ti->is_single_step_enabled()) + { + VM_thread *vm_thread = p_TLS_vmthread; + if (vm_thread->ss_state->enabled) + { + JVMTISingleStepState *ss_state = vm_thread->ss_state; + for(unsigned iii = 0; iii < ss_state->predicted_bp_count; iii++) + { + if (ss_state->predicted_breakpoints[iii]->native_location == + native_location) + { + ss_breakpoint = ss_state->predicted_breakpoints[iii]; + + TIEnv *ti_env = ti->getEnvironments(); + TIEnv *next_env; + jlocation location = ss_breakpoint->location; + jmethodID method = ss_breakpoint->method; + + while (NULL != ti_env) + { + next_env = ti_env->next; + jvmtiEventSingleStep func = + (jvmtiEventSingleStep)ti_env->get_event_callback(JVMTI_EVENT_SINGLE_STEP); + if (NULL != func) + { + if (ti_env->global_events[JVMTI_EVENT_SINGLE_STEP - JVMTI_MIN_EVENT_TYPE_VAL]) + { + TRACE2("jvmti.break.ss", + "Calling JIT global SingleStep breakpoint callback method = " << + ((Method*)method)->get_name() << " location = " << location); + // fire global event + ti->brkpntlst_lock._unlock(); + func((jvmtiEnv*)ti_env, jni_env, (jthread)hThread, method, location); + ti->brkpntlst_lock._lock(); + TRACE2("jvmti.break.ss", + "Finished JIT global SingleStep breakpoint callback method = " << + ((Method*)method)->get_name() << " location = " << location); + ti_env = next_env; + continue; + } + + // fire local events + for(TIEventThread* ti_et = ti_env->event_threads[JVMTI_EVENT_SINGLE_STEP - JVMTI_MIN_EVENT_TYPE_VAL]; + ti_et != NULL; ti_et = ti_et->next) + if (ti_et->thread == hythread_self()) + { + TRACE2("jvmti.break.ss", + "Calling JIT local SingleStep breakpoint callback method = " << + ((Method*)method)->get_name() << " location = " << location); + ti->brkpntlst_lock._unlock(); + func((jvmtiEnv*)ti_env, jni_env, + (jthread)hThread, method, location); + ti->brkpntlst_lock._lock(); + TRACE2("jvmti.break.ss", + "Finished JIT local SingleStep breakpoint callback method = " << + ((Method*)method)->get_name() << " location = " << location); + } + } + ti_env = next_env; + } + } + } + } + } + + // Send events for all normally set breakpoints for this location + // if there are any do { TIEnv *env = bp->env; @@ -174,12 +244,16 @@ #endif jmethodID method = bp->method; BreakPoint *next_bp = ti->find_next_bpt(bp, native_location); + if (bp == ss_breakpoint) + // Don't send breakpoint event for breakpoint which was + // actually SingleStep breakpoint + continue; + if (env->global_events[JVMTI_EVENT_BREAKPOINT - JVMTI_MIN_EVENT_TYPE_VAL]) { jvmtiEventBreakpoint func = (jvmtiEventBreakpoint)env->get_event_callback(JVMTI_EVENT_BREAKPOINT); if (NULL != func) { - JNIEnv *jni_env = (JNIEnv *)jni_native_intf; TRACE2("jvmti.break", "Calling JIT global breakpoint callback method = " << ((Method*)method)->get_name() << " location = " << location); @@ -308,6 +382,26 @@ #else regs->rip = (POINTER_SIZE_INT)instruction_buffer; #endif + // Set breakpoints on bytecodes after the current one + if (ti->is_single_step_enabled()) + { + VM_thread *vm_thread = p_TLS_vmthread; + if (vm_thread->ss_state->enabled) + { + jvmti_remove_single_step_breakpoints(ti, vm_thread); + + jvmti_StepLocation *locations; + unsigned locations_count; + + jvmti_SingleStepLocation(vm_thread, (Method *)ss_breakpoint->method, + (unsigned)ss_breakpoint->location, &locations, &locations_count); + + jvmtiError UNREF errorCode = jvmti_set_single_step_breakpoints( + ti, vm_thread, locations, locations_count); + assert(JVMTI_ERROR_NONE == errorCode); + } + } + ti->brkpntlst_lock._unlock(); tmn_suspend_disable(); @@ -318,6 +412,8 @@ #endif jvmtiError jvmti_set_jit_mode_breakpoint(BreakPoint *bp) { + // Function is always executed under global TI breakpoints lock + // Find native location in the method code NativeCodePtr np = NULL; Method *m = (Method *)bp->method; @@ -385,6 +481,44 @@ void jvmti_set_pending_breakpoints(Metho STD_FREE(locations); } +jvmtiError jvmti_set_breakpoint_for_jit(DebugUtilsTI *ti, BreakPoint *bp) +{ + // Function is always executed under global TI breakpoints lock + + BreakPoint *other_bp = ti->get_other_breakpoint_same_location(bp->method, + bp->location); + + if (NULL == other_bp) // No other breakpoints were set in this place + { + Method *m = (Method *)bp->method; + + if (m->get_state() == Method::ST_Compiled) + { + jvmtiError errorCode = jvmti_set_jit_mode_breakpoint(bp); + + if (JVMTI_ERROR_NONE != errorCode) + return JVMTI_ERROR_INTERNAL; + } + else + { + TRACE2("jvmti.break", "Skipping setting breakpoing in method " << + m->get_class()->name->bytes << "." << + m->get_name()->bytes << " " << m->get_descriptor()->bytes << + " because it is not compiled yet"); + m->insert_pending_breakpoint(); + } + } + else + { + bp->id = other_bp->id; + if (NULL != bp->disasm) + bp->disasm = new InstructionDisassembler(*bp->disasm); + } + + ti->add_breakpoint(bp); + return JVMTI_ERROR_NONE; +} + /* * Set Breakpoint * @@ -457,44 +591,47 @@ #endif bp->env = p_env; bp->disasm = NULL; - BreakPoint *other_bp = ti->get_other_breakpoint_same_location(method, location, p_env); - - if (NULL == other_bp) // No more environments set breakpoints here + if (interpreter_enabled()) { - if (interpreter_enabled()) - bp->id = interpreter.interpreter_ti_set_breakpoint(method, location); - else - { - Method *m = (Method *)method; + BreakPoint *other_bp = ti->get_other_breakpoint_same_location(method, location); - if (m->get_state() == Method::ST_Compiled) - errorCode = jvmti_set_jit_mode_breakpoint(bp); - else - { - TRACE2("jvmti.break", "Skipping setting breakpoing in method " << - m->get_class()->name->bytes << "." << - m->get_name()->bytes << " " << m->get_descriptor()->bytes << - " because it is not compiled yet"); - m->insert_pending_breakpoint(); - } + if (NULL == other_bp) // No other breakpoints were set in this place + bp->id = interpreter.interpreter_ti_set_breakpoint(method, location); - if (errorCode != JVMTI_ERROR_NONE) - return JVMTI_ERROR_INTERNAL; - } + ti->add_breakpoint(bp); } else { - bp->id = other_bp->id; - if (NULL != bp->disasm) - bp->disasm = new InstructionDisassembler(*bp->disasm); - } + errorCode = jvmti_set_breakpoint_for_jit(ti, bp); - ti->add_breakpoint(bp); + if (JVMTI_ERROR_NONE != errorCode) + return JVMTI_ERROR_INTERNAL; + } TRACE2("jvmti.break", "SetBreakpoint successfull"); return JVMTI_ERROR_NONE; } +void jvmti_remove_breakpoint_for_jit(DebugUtilsTI *ti, BreakPoint *bp) +{ + // Function is always executed under global TI breakpoints lock + + if (NULL == ti->get_other_breakpoint_same_location(bp->method, bp->location)) + { + Method *m = (Method *)bp->method; + + if (m->get_state() == Method::ST_Compiled) + { + jbyte *target_instruction = (jbyte *)bp->native_location; + *target_instruction = (POINTER_SIZE_INT)bp->id; + } + else + m->remove_pending_breakpoint(); + } + + ti->remove_breakpoint(bp); +} + /* * Clear Breakpoint * @@ -558,26 +695,16 @@ #endif if (NULL == bp) return JVMTI_ERROR_NOT_FOUND; - if (NULL == ti->get_other_breakpoint_same_location(method, location, p_env)) + if (interpreter_enabled()) { - // No more environments set breakpoints here - if (interpreter_enabled()) + if (NULL == ti->get_other_breakpoint_same_location(method, location)) + // No other breakpoints were set in this place interpreter.interpreter_ti_clear_breakpoint(method, location, bp->id); - else - { - Method *m = (Method *)method; - if (m->get_state() == Method::ST_Compiled) - { - jbyte *target_instruction = (jbyte *)bp->native_location; - *target_instruction = (POINTER_SIZE_INT)bp->id; - } - else - m->remove_pending_breakpoint(); - } + ti->remove_breakpoint(bp); } - - ti->remove_breakpoint(bp); + else + jvmti_remove_breakpoint_for_jit(ti, bp); TRACE2("jvmti.break", "ClearBreakpoint successfull"); return JVMTI_ERROR_NONE; diff --git a/vm/vmcore/src/jvmti/jvmti_capability.cpp b/vm/vmcore/src/jvmti/jvmti_capability.cpp index 0c1f7d2..503b04b 100644 --- a/vm/vmcore/src/jvmti/jvmti_capability.cpp +++ b/vm/vmcore/src/jvmti/jvmti_capability.cpp @@ -83,7 +83,7 @@ static const jvmtiCapabilities jvmti_sup 1, // can_get_source_debug_extension 1, // can_access_local_variables 0, // can_maintain_original_method_order - 0, // can_generate_single_step_events + 1, // can_generate_single_step_events 1, // can_generate_exception_events 1, // can_generate_frame_pop_events 1, // can_generate_breakpoint_events @@ -122,7 +122,7 @@ static const jvmtiCapabilities jvmti_ena 1, // can_get_source_debug_extension 0, // can_access_local_variables 0, // can_maintain_original_method_order - 0, // can_generate_single_step_events + 1, // can_generate_single_step_events 0, // can_generate_exception_events 0, // can_generate_frame_pop_events 0, // can_generate_breakpoint_events diff --git a/vm/vmcore/src/jvmti/jvmti_event.cpp b/vm/vmcore/src/jvmti/jvmti_event.cpp index 8f62072..77dbf41 100644 --- a/vm/vmcore/src/jvmti/jvmti_event.cpp +++ b/vm/vmcore/src/jvmti/jvmti_event.cpp @@ -307,6 +307,48 @@ jvmtiSetEventNotificationMode(jvmtiEnv* if (event_enabled(event_type) != (jboolean)old_state) { if (interpreter_enabled()) interpreter.interpreter_ti_set_notification_mode(event_type, !old_state); + else + { + if (JVMTI_EVENT_SINGLE_STEP == event_type) + { + DebugUtilsTI *ti = ((TIEnv *)env)->vm->vm_env->TI; + + if (JVMTI_ENABLE == mode && !ti->is_single_step_enabled()) + { + jvmtiError errorCode = ti->jvmti_single_step_start(); + + if (JVMTI_ERROR_NONE != errorCode) + return errorCode; + } + else if (JVMTI_DISABLE == mode && ti->is_single_step_enabled()) + { + // Check that no environment has SingleStep enabled + LMAutoUnlock lock(&ti->TIenvs_lock); + bool disable = true; + + for (TIEnv *ti_env = ti->getEnvironments(); ti_env; + ti_env = ti_env->next) + { + if (ti_env->global_events[JVMTI_EVENT_SINGLE_STEP - + JVMTI_MIN_EVENT_TYPE_VAL] || + NULL != ti_env->event_threads[JVMTI_EVENT_SINGLE_STEP - + JVMTI_MIN_EVENT_TYPE_VAL]) + { + disable = false; + break; + } + } + + if (disable) + { + jvmtiError errorCode = ti->jvmti_single_step_stop(); + + if (JVMTI_ERROR_NONE != errorCode) + return errorCode; + } + } + } + } } return JVMTI_ERROR_NONE; @@ -1501,10 +1543,44 @@ static void process_jvmti_event(jvmtiEve } -void jvmti_send_thread_start_end_event(int is_start) { - is_start ? process_jvmti_event(JVMTI_EVENT_THREAD_START, 0, 0) - :process_jvmti_event(JVMTI_EVENT_THREAD_END, 1, 0); - } +void jvmti_send_thread_start_end_event(int is_start) +{ + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + + if (is_start) + { + process_jvmti_event(JVMTI_EVENT_THREAD_START, 0, 0); + + if (ti->is_single_step_enabled()) + { + // Init single step state for the thread + VM_thread *vm_thread = p_TLS_vmthread; + + jvmtiError UNREF errorCode = _allocate(sizeof(JVMTISingleStepState), + (unsigned char **)&vm_thread->ss_state); + assert(JVMTI_ERROR_NONE == errorCode); + + vm_thread->ss_state->predicted_breakpoints = NULL; + vm_thread->ss_state->predicted_bp_count = 0; + vm_thread->ss_state->enabled = true; + } + } + else + { + process_jvmti_event(JVMTI_EVENT_THREAD_END, 1, 0); + + if (ti->is_single_step_enabled()) + { + // Shut down single step state for the thread + VM_thread *vm_thread = p_TLS_vmthread; + LMAutoUnlock lock(&ti->brkpntlst_lock); + jvmti_remove_single_step_breakpoints(ti, vm_thread); + + vm_thread->ss_state->enabled = false; + _deallocate((unsigned char *)vm_thread->ss_state); + } + } +} void jvmti_send_wait_monitor_event(jobject monitor, jlong timeout) { TRACE2("jvmti.monitor.wait", "Monitor wait event, monitor = " << monitor); diff --git a/vm/vmcore/src/jvmti/jvmti_step.cpp b/vm/vmcore/src/jvmti/jvmti_step.cpp index b42786c..18934de 100644 --- a/vm/vmcore/src/jvmti/jvmti_step.cpp +++ b/vm/vmcore/src/jvmti/jvmti_step.cpp @@ -28,6 +28,8 @@ #include "stack_iterator.h" #include "interpreter.h" #include "open/bytecodes.h" +static JNIEnv * jvmti_test_jenv = jni_native_intf; + static inline short jvmti_GetHalfWordValue( const unsigned char *bytecode, unsigned location) @@ -326,7 +328,7 @@ jvmti_SingleStepLocation( VM_thread* thr assert( !is_wide ); { // create stack iterator, current stack frame should be native - StackIterator *si = si_create_from_native(); + StackIterator *si = si_create_from_native(thread); assert(si_is_native(si)); // get previous stack frame, it should be java frame si_goto_previous(si); @@ -351,7 +353,11 @@ jvmti_SingleStepLocation( VM_thread* thr error = _allocate( sizeof(jvmti_StepLocation), (unsigned char**)next_step ); assert( error == JVMTI_ERROR_NONE ); (*next_step)->method = func; - (*next_step)->location = jvmti_GetNextBytecodeAfterInvoke( func, bc ); + // gregory - IP in stack iterator points to a + // bytecode next after the one which caused call + // of the method. So next location is the BC which + // IP points to. + (*next_step)->location = bc; } } break; @@ -454,3 +460,197 @@ jvmti_SingleStepLocation( VM_thread* thr return; } // jvmti_SingleStepLocation + +jvmtiError jvmti_set_single_step_breakpoints(DebugUtilsTI *ti, VM_thread *vm_thread, + jvmti_StepLocation *locations, unsigned locations_number) +{ + // Function is always executed under global TI breakpoints lock + BreakPoint **thread_breakpoints; + jvmtiError errorCode = _allocate(sizeof(BreakPoint), + (unsigned char **)&thread_breakpoints); + if (JVMTI_ERROR_NONE != errorCode) + return errorCode; + + for (unsigned iii = 0; iii < locations_number; iii++) + { + BreakPoint *bp; + errorCode = _allocate(sizeof(BreakPoint), (unsigned char **)&bp); + if (JVMTI_ERROR_NONE != errorCode) + return errorCode; + + bp->method = (jmethodID)locations[iii].method; + bp->location = locations[iii].location; + bp->env = NULL; + bp->disasm = NULL; + + errorCode = jvmti_set_breakpoint_for_jit(ti, bp); + if (JVMTI_ERROR_NONE != errorCode) + return errorCode; + + thread_breakpoints[iii] = bp; + } + + JVMTISingleStepState *ss_state = vm_thread->ss_state; + ss_state->predicted_breakpoints = thread_breakpoints; + ss_state->predicted_bp_count = locations_number; + + return JVMTI_ERROR_NONE; +} + +void jvmti_remove_single_step_breakpoints(DebugUtilsTI *ti, VM_thread *vm_thread) +{ + // Function is always executed under global TI breakpoints lock + JVMTISingleStepState *ss_state = vm_thread->ss_state; + + for (unsigned iii = 0; iii < ss_state->predicted_bp_count; iii++) + { + BreakPoint *bp = ss_state->predicted_breakpoints[iii]; + jvmti_remove_breakpoint_for_jit(ti, bp); + } + + _deallocate((unsigned char *)ss_state->predicted_breakpoints); + ss_state->predicted_bp_count = 0; +} + +jvmtiError jvmti_get_next_bytecodes_up_stack_from_native(VM_thread *thread, + jvmti_StepLocation **next_step, + unsigned *count) +{ + StackIterator *si = si_create_from_native(thread); + + // Find first Java frame in the thread stack + while (!si_is_past_end(si)) + if (!si_is_native(si)) + break; + + *count = 0; + + if (!si_is_past_end(si)) + { + Method *m = si_get_method(si); + assert(m); + + CodeChunkInfo *cci = si_get_code_chunk_info(si); + JIT *jit = cci->get_jit(); + // IP address in stack iterator points to the next bytecode after + // call + NativeCodePtr ip = si_get_ip(si); + uint16 bc; + + OpenExeJpdaError UNREF result = + jit->get_bc_location_for_native(m, ip, &bc); + assert(result == EXE_ERROR_NONE); + + jvmtiError errorCode = _allocate(sizeof(jvmti_StepLocation), + (unsigned char **)next_step); + + if (JVMTI_ERROR_NONE != errorCode) + { + si_free(si); + return errorCode; + } + (*next_step)->method = (Method *)m; + (*next_step)->location = bc; + } + + si_free(si); + return JVMTI_ERROR_NONE; +} + +jvmtiError DebugUtilsTI::jvmti_single_step_start(void) +{ + assert(hythread_is_suspend_enabled()); + LMAutoUnlock lock(&brkpntlst_lock); + + hythread_iterator_t threads_iterator; + + // Suspend all threads except current + IDATA tm_ret = hythread_suspend_all(&threads_iterator, NULL); + if (TM_ERROR_NONE != tm_ret) + return JVMTI_ERROR_INTERNAL; + + hythread_t ht; + + // Set single step in all threads + while ((ht = hythread_iterator_next(&threads_iterator)) != NULL) + { + VM_thread *vm_thread = get_vm_thread(ht); + + // Init single step state for the thread + jvmtiError errorCode = _allocate(sizeof(JVMTISingleStepState), + (unsigned char **)&vm_thread->ss_state); + + if (JVMTI_ERROR_NONE != errorCode) + { + hythread_resume_all(NULL); + return errorCode; + } + + vm_thread->ss_state->predicted_breakpoints = NULL; + vm_thread->ss_state->predicted_bp_count = 0; + vm_thread->ss_state->enabled = true; + + vm_thread->ss_state->enabled = true; + + jvmti_StepLocation *locations; + unsigned locations_number; + + errorCode = jvmti_get_next_bytecodes_up_stack_from_native( + vm_thread, &locations, &locations_number); + + if (JVMTI_ERROR_NONE != errorCode) + { + hythread_resume_all(NULL); + return errorCode; + } + + errorCode = jvmti_set_single_step_breakpoints(this, vm_thread, locations, + locations_number); + + if (JVMTI_ERROR_NONE != errorCode) + { + hythread_resume_all(NULL); + return errorCode; + } + } + + single_step_enabled = true; + + tm_ret = hythread_resume_all(NULL); + if (TM_ERROR_NONE != tm_ret) + return JVMTI_ERROR_INTERNAL; + + return JVMTI_ERROR_NONE; +} + +jvmtiError DebugUtilsTI::jvmti_single_step_stop(void) +{ + assert(hythread_is_suspend_enabled()); + LMAutoUnlock lock(&brkpntlst_lock); + + hythread_iterator_t threads_iterator; + + // Suspend all threads except current + IDATA tm_ret = hythread_suspend_all(&threads_iterator, NULL); + if (TM_ERROR_NONE != tm_ret) + return JVMTI_ERROR_INTERNAL; + + hythread_t ht; + + // Clear single step in all threads + while ((ht = hythread_iterator_next(&threads_iterator)) != NULL) + { + VM_thread *vm_thread = get_vm_thread(ht); + jvmti_remove_single_step_breakpoints(this, vm_thread); + vm_thread->ss_state->enabled = false; + _deallocate((unsigned char *)vm_thread->ss_state); + } + + single_step_enabled = false; + + tm_ret = hythread_resume_all(NULL); + if (TM_ERROR_NONE != tm_ret) + return JVMTI_ERROR_INTERNAL; + + return JVMTI_ERROR_NONE; +}