diff --git a/vm/include/interpreter_exports.h b/vm/include/interpreter_exports.h index 9df6fe1..5bee26b 100644 --- a/vm/include/interpreter_exports.h +++ b/vm/include/interpreter_exports.h @@ -67,17 +67,16 @@ #endif * Set breakpoint in place identified by method and location. * No more then one breakpoint will be set at any specific place. Handling * for multiple jvmti environments is done by jvmti framework. - * @return ID, a black box pointer. The pointer will be returned by jvmti as - * a result of breakpoint event callback: jvmti_process_breakpoint_event(...). + * @return bytecode has been replaced by instrumentation */ - void* (*interpreter_ti_set_breakpoint)(jmethodID method, jlocation location); + jbyte (*interpreter_ti_set_breakpoint)(jmethodID method, jlocation location); /** * Clear breakpoint in place identified by method and location. - * Breakpoint ID (returned by interpreter_ti_set_breakpoint(..) also passed - * as a parameter. + * Replaced bytecode (returned by interpreter_ti_set_breakpoint(..) + * is also passed as a parameter. */ - void (*interpreter_ti_clear_breakpoint)(jmethodID method, jlocation location, void *id); + void (*interpreter_ti_clear_breakpoint)(jmethodID method, jlocation location, jbyte saved); /** * Set callback to notify JVMTI about frame pop event. diff --git a/vm/include/interpreter_imports.h b/vm/include/interpreter_imports.h index 4cce319..35a9bf2 100644 --- a/vm/include/interpreter_imports.h +++ b/vm/include/interpreter_imports.h @@ -32,7 +32,7 @@ VMEXPORT void class_throw_linking_error_ VMEXPORT struct JNIEnv_Internal* get_jni_native_intf(); -VMEXPORT void* jvmti_process_interpreter_breakpoint_event(jmethodID method, jlocation loc); +VMEXPORT jbyte jvmti_process_interpreter_breakpoint_event(jmethodID method, jlocation loc); VMEXPORT void jvmti_process_single_step_event(jmethodID method, jlocation location); VMEXPORT void jvmti_process_frame_pop_event(jvmtiEnv *env, diff --git a/vm/interpreter/src/interp_exports.cpp b/vm/interpreter/src/interp_exports.cpp index 9d613ae..edbccb7 100644 --- a/vm/interpreter/src/interp_exports.cpp +++ b/vm/interpreter/src/interp_exports.cpp @@ -84,8 +84,8 @@ uint64* interpreter_get_stacked_register extern void interpreter_execute_method(Method *method, jvalue *return_value, jvalue *args); extern void interpreter_ti_set_notification_mode(jvmtiEvent event_type, bool enable); -extern void* interpreter_ti_set_breakpoint(jmethodID method, jlocation location); -extern void interpreter_ti_clear_breakpoint(jmethodID method, jlocation location, void *id); +extern jbyte interpreter_ti_set_breakpoint(jmethodID method, jlocation location); +extern void interpreter_ti_clear_breakpoint(jmethodID method, jlocation location, jbyte saved); extern jvmtiError interpreter_ti_pop_frame(jvmtiEnv*, VM_thread *thread); extern void stack_dump(VM_thread *thread); diff --git a/vm/interpreter/src/interpreter_ti.cpp b/vm/interpreter/src/interpreter_ti.cpp index bdaa7e3..e0a3cc2 100644 --- a/vm/interpreter/src/interpreter_ti.cpp +++ b/vm/interpreter/src/interpreter_ti.cpp @@ -357,18 +357,18 @@ Opcode_BREAKPOINT(StackFrame& frame) { return (uint8) (POINTER_SIZE_INT) jvmti_process_interpreter_breakpoint_event((jmethodID)m, l); } -void* interpreter_ti_set_breakpoint(jmethodID method, jlocation location) { +jbyte interpreter_ti_set_breakpoint(jmethodID method, jlocation location) { Method *m = (Method*) method; uint8 *bytecodes = (uint8*) m->get_byte_code_addr(); uint8 b = bytecodes[location]; bytecodes[location] = OPCODE_BREAKPOINT; - return (void*) (POINTER_SIZE_INT) b; + return b; } -void interpreter_ti_clear_breakpoint(jmethodID method, jlocation location, void* id) { +void interpreter_ti_clear_breakpoint(jmethodID method, jlocation location, jbyte saved) { Method *m = (Method*) method; uint8 *bytecodes = (uint8*) m->get_byte_code_addr(); - bytecodes[location] = (uint8) (POINTER_SIZE_INT) id; + bytecodes[location] = saved; } int interpreter_ti_notification_mode = 0; diff --git a/vm/vmcore/include/jvmti_break_intf.h b/vm/vmcore/include/jvmti_break_intf.h new file mode 100755 index 0000000..725d899 --- /dev/null +++ b/vm/vmcore/include/jvmti_break_intf.h @@ -0,0 +1,179 @@ +/* + * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Ilya Berezhniuk + * @version $Revision$ + */ +/** + * @file jvmti_break_intf.h + * @brief JVMTI native breakpoints API + */ +#if !defined(__JVMTI_BREAK_INTF_H__) +#define __JVMTI_BREAK_INTF_H__ + +#include "open/types.h" +#include "jni.h" +#include "lock_manager.h" +#include "jvmti_dasm.h" +#include "environment.h" + + +class VmBrkptIntf; +struct VmBrkpt; +struct VmBrkptRef; + +// Callbacks are called for interfaces according to its priority +#define BREAK_MAX_PRIORITY 0 +#define BREAK_MIN_PRIORITY 1 + +// Pointer to interface callback function +typedef bool (*fn_interface_callback)(VmBrkptIntf* intf, VmBrkptRef* bp_ref); + +class VmBreakpoints +{ +public: + VmBreakpoints(); + ~VmBreakpoints(); + + void lock() {m_lock._lock();} + void unlock() {m_lock._unlock();} + // Is used for LMAutoUnlock + Lock_Manager* get_lock() + {return &this->m_lock;} + + // Returns interface for breakpoint handling + VmBrkptIntf* query_intf(fn_interface_callback callback, bool is_interp); + // Destroys interface and deletes all its breakpoints + void release_intf(VmBrkptIntf* intf); + + // Inserts breakpoint into global list and performs instrumentation + bool insert_breakpoint(VmBrkpt* brpt); + // Removes breakpoint from global list and restores instrumented area + bool remove_breakpoint(VmBrkpt* brpt); + + // Search operations + VmBrkpt* find_breakpoint(jmethodID method, jlocation location); + VmBrkpt* find_breakpoint(NativeCodePtr addr); + VmBrkpt* find_next_breakpoint(VmBrkpt* prev, NativeCodePtr addr); + + // Search breakpoints for given method + bool has_breakpoint(jmethodID method); + VmBrkpt* find_first(jmethodID method); + VmBrkpt* find_next(VmBrkpt* prev, jmethodID method); + + // Checks if given breakpoint is set by other interfaces + VmBrkptRef* find_other(VmBrkptIntf* intf, jmethodID method, jlocation location); + VmBrkptRef* find_other(VmBrkptIntf* intf, NativeCodePtr addr); + VmBrkptRef* find_other(VmBrkptIntf* intf, VmBrkpt* brpt); + + // General callback functions + void process_native_breakpoint(); + jbyte process_interpreter_breakpoint(jmethodID method, jlocation location); + +protected: + void clear_processed_flags(); + VmBrkptIntf* get_intf_to_process(int priority); + +protected: + VmBrkptIntf* m_intf_list; + VmBrkpt* m_brpt_list; + + Lock_Manager m_lock; +}; + +class VmBrkptIntf +{ + friend class VmBreakpoints; + +public: + void lock(); + void unlock(); + Lock_Manager* get_lock(); + + // Default is BREAK_MIN_PRIORITY + bool set_priority(int priority); + int get_priority() const { return m_priority; } + + // Iteration + VmBrkptRef* get_first(); + VmBrkptRef* get_next(VmBrkptRef* prev); + + // Basic operations + + // 'data' must be allocated with JVMTI Allocate (or internal _allocate) + // Users must not deallocate 'data', it will be deallocated by 'remove' + VmBrkptRef* add(jmethodID method, jlocation location, void* data); + // To specify address explicitly + VmBrkptRef* add(jmethodID method, jlocation location, + NativeCodePtr addr, void* data); + VmBrkptRef* add(NativeCodePtr addr, void* data); + bool remove(VmBrkptRef* ref); + VmBrkptRef* find(jmethodID method, jlocation location); + VmBrkptRef* find(NativeCodePtr addr); + VmBrkptRef* find(VmBrkpt* brpt); + void remove_all(); + +protected: + VmBrkptIntf(fn_interface_callback callback, bool is_interp); + VmBrkptIntf(); + ~VmBrkptIntf(); + + void set_processed(bool processed) { m_processed = processed; } + bool is_processed() const { return (m_processed == true); } + +protected: + VmBrkptRef* m_list; + fn_interface_callback m_callback; + bool m_is_interp; + int m_priority; + bool m_processed; + + VmBrkptIntf* m_next; +}; + +struct VmBrkpt +{ + bool is_interp; + NativeCodePtr addr; + jmethodID method; + jlocation location; + jbyte saved_byte; + InstructionDisassembler* disasm; + + VmBrkpt* next; +}; + +// Breakpoint reference +struct VmBrkptRef +{ + VmBrkpt* brpt; + void* data; + + VmBrkptRef* next; +}; + + +// Callback function for native breakpoint processing +bool jvmti_jit_breakpoint_handler(Registers *regs); + +// Callback function for interpreter breakpoint processing +VMEXPORT jbyte +jvmti_process_interpreter_breakpoint_event(jmethodID method, jlocation location); + +// Callback for JIT method compile +void jvmti_set_pending_breakpoints(Method *method); + +#endif // __JVMTI_BREAK_INTF_H__ diff --git a/vm/vmcore/include/jvmti_direct.h b/vm/vmcore/include/jvmti_direct.h index bb8b00f..f1195dc 100644 --- a/vm/vmcore/include/jvmti_direct.h +++ b/vm/vmcore/include/jvmti_direct.h @@ -32,6 +32,7 @@ struct TIEventThread struct Agent; extern Agent *current_loading_agent; +class VmBrkptIntf; /* * Type that describes TI environment created by GetEnv function @@ -45,6 +46,7 @@ struct TIEnv jvmtiEventCallbacks event_table; jvmtiExtensionEvent *extension_event_table; jvmtiCapabilities posessed_capabilities; + VmBrkptIntf *brpt_intf; TIEnv* next; bool global_events[JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL]; @@ -75,7 +77,6 @@ struct TIEnv jint JNICALL create_jvmti_environment(JavaVM *vm, void **env, jint version); jint get_thread_stack_depth(VM_thread *thread, jint* pskip = NULL); -void jvmti_set_pending_breakpoints(Method *method); void jvmti_get_compilation_flags(OpenMethodExecutionParams *flags); // Marks topmost frame of the specified thead to be popped diff --git a/vm/vmcore/include/jvmti_internal.h b/vm/vmcore/include/jvmti_internal.h index d550480..c5b1e54 100644 --- a/vm/vmcore/include/jvmti_internal.h +++ b/vm/vmcore/include/jvmti_internal.h @@ -75,18 +75,11 @@ struct jvmti_frame_pop_listener }; /* - * Type which will describe one breakpoint + * Type which will be attached to each JVMTI breakpoint */ -struct BreakPoint { - jmethodID method; - jlocation location; - NativeCodePtr native_location; - InstructionDisassembler *disasm; - void *id; - BreakPoint *next; +struct TIBrptData +{ TIEnv *env; - - BreakPoint(TIEnv *_env) : method(NULL), location(0), next(NULL), env(_env) {} }; struct jvmti_StepLocation @@ -98,8 +91,7 @@ struct jvmti_StepLocation struct JVMTISingleStepState { - BreakPoint **predicted_breakpoints; - unsigned predicted_bp_count; + VmBrkptIntf* predicted_breakpoints; }; /* @@ -153,6 +145,8 @@ public: }; typedef struct Class Class; +class VmBreakpoints; +struct VmBrkptRef; /* * JVMTI state of the VM @@ -160,9 +154,9 @@ typedef struct Class Class; class DebugUtilsTI { public: jint agent_counter; - Lock_Manager brkpntlst_lock; Lock_Manager TIenvs_lock; Lock_Manager dcList_lock; + VmBreakpoints* vm_brpt; DebugUtilsTI(); @@ -229,179 +223,6 @@ class DebugUtilsTI { return p_TIenvs; } - BreakPoint *find_breakpoint(jmethodID m, jlocation l, TIEnv *env) - { - // 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) - return bp; - - return NULL; - } - - bool have_breakpoint(jmethodID m) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = brkpntlst; NULL != bp; bp = bp->next) - if (bp->method == m) - return true; - - return false; - } - - BreakPoint* find_first_bpt(jmethodID m) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = brkpntlst; NULL != bp; bp = bp->next) - if (bp->method == m) - return bp; - - return NULL; - } - - BreakPoint* find_next_bpt(BreakPoint* bpt, jmethodID m) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = bpt->next; NULL != bp; bp = bp->next) - if (bp->method == m) - return bp; - - return NULL; - } - - BreakPoint* find_first_bpt(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) - return bp; - - return NULL; - } - - BreakPoint* find_next_bpt(BreakPoint* bpt, jmethodID m, jlocation l) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = bpt->next; NULL != bp; bp = bp->next) - if (bp->method == m && bp->location == l) - return bp; - - return NULL; - } - - BreakPoint* find_first_bpt(NativeCodePtr np) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = brkpntlst; NULL != bp; bp = bp->next) - if (bp->native_location == np) - return bp; - - return NULL; - } - - BreakPoint* find_next_bpt(BreakPoint* bpt, NativeCodePtr np) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = bpt->next; NULL != bp; bp = bp->next) - if (bp->native_location == np) - return bp; - - return NULL; - } - - BreakPoint *get_other_breakpoint_same_location(BreakPoint *this_bp) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = brkpntlst; NULL != bp; bp = bp->next) - if (bp != this_bp && - bp->method == this_bp->method && bp->location == this_bp->location) - return bp; - - return NULL; - } - - BreakPoint *get_other_breakpoint_same_native_location(BreakPoint *this_bp) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint *bp = brkpntlst; NULL != bp; bp = bp->next) - if (bp != this_bp && bp->native_location == this_bp->native_location) - return bp; - - return NULL; - } - - BreakPoint *get_breakpoint_from_location(jmethodID method, jlocation location) - { - for (BreakPoint *bp = brkpntlst; NULL != bp; bp = bp->next) - if (bp->method == method && bp->location == location) - return bp; - - return NULL; - } - - void add_breakpoint(BreakPoint *bp) - { - // assert(brkpntlst_lock._lock_or_null()); - - bp->next = brkpntlst; - brkpntlst = bp; - } - - void remove_breakpoint(BreakPoint *bp) - { - // assert(brkpntlst_lock._lock_or_null()); - assert(brkpntlst); - - if (bp == brkpntlst) - { - brkpntlst = bp->next; - if (NULL != bp->disasm) - delete bp->disasm; - _deallocate((unsigned char *)bp); - return; - } - - for (BreakPoint *p_bp = brkpntlst; NULL != p_bp->next; p_bp = p_bp->next) - if (p_bp->next == bp) - { - p_bp->next = bp->next; - _deallocate((unsigned char *)bp); - return; - } - - ABORT("Can't find the breakpoint"); - } - - void remove_all_breakpoints_env(TIEnv *env) - { - // assert(brkpntlst_lock._lock_or_null()); - - for (BreakPoint **pp_bp = &brkpntlst; NULL != *pp_bp; ) - { - BreakPoint *p_bp = *pp_bp; - - if (p_bp->env == env) - { - *pp_bp = p_bp->next; - _deallocate((unsigned char *)p_bp); - } - else - { - pp_bp = &(p_bp->next); - } - } - } - // Watched fields' support Watch** get_access_watch_list() @@ -500,7 +321,6 @@ class DebugUtilsTI { protected: friend jint JNICALL create_jvmti_environment(JavaVM *vm, void **env, jint version); - BreakPoint *brkpntlst; Watch *access_watch_list; Watch *modification_watch_list; bool status; @@ -528,9 +348,7 @@ jint load_agentpath(Agent *agent, const // Breakpoints internal functions jvmtiError jvmti_get_next_bytecodes_stack_from_native(VM_thread *thread, jvmti_StepLocation **next_step, unsigned *count, bool step_up); -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, +void 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); @@ -547,4 +365,7 @@ jvmtiError jvmti_translate_jit_error(Ope void jvmti_SingleStepLocation(VM_thread* thread, Method *method, unsigned location, jvmti_StepLocation **next_step, unsigned *count); +// Callback function for JVMTI breakpoint processing +bool jvmti_process_breakpoint_event(VmBrkptIntf* intf, VmBrkptRef* bp_ref); + #endif /* _JVMTI_INTERNAL_H_ */ diff --git a/vm/vmcore/src/exception/exceptions_jit.cpp b/vm/vmcore/src/exception/exceptions_jit.cpp index a262c9c..324d1c5 100644 --- a/vm/vmcore/src/exception/exceptions_jit.cpp +++ b/vm/vmcore/src/exception/exceptions_jit.cpp @@ -40,6 +40,7 @@ #include "m2n.h" #include "mon_enter_exit.h" #include "stack_iterator.h" #include "vm_stats.h" +#include "jvmti_break_intf.h" #ifdef _IPF_ #elif defined _EM64T_ @@ -222,7 +223,7 @@ #endif // VM_STATS VM_thread *vm_thread = p_TLS_vmthread; if (NULL != vm_thread->ss_state) { - LMAutoUnlock lock(&ti->brkpntlst_lock); + LMAutoUnlock lock(ti->vm_brpt->get_lock()); jvmti_remove_single_step_breakpoints(ti, vm_thread); } } @@ -269,7 +270,7 @@ #endif // VM_STATS VM_thread *vm_thread = p_TLS_vmthread; if (NULL != vm_thread->ss_state) { - LMAutoUnlock lock(&ti->brkpntlst_lock); + LMAutoUnlock lock(ti->vm_brpt->get_lock()); uint16 bc; NativeCodePtr ip = handler->get_handler_ip(); @@ -279,10 +280,8 @@ #endif // VM_STATS jvmti_StepLocation method_start = {(Method *)method, bc, ip}; - jvmtiError UNREF errorCode = - jvmti_set_single_step_breakpoints(ti, vm_thread, + jvmti_set_single_step_breakpoints(ti, vm_thread, &method_start, 1); - assert(JVMTI_ERROR_NONE == errorCode); } } diff --git a/vm/vmcore/src/jit/compile.cpp b/vm/vmcore/src/jit/compile.cpp index 06e3ec9..e68cfae 100644 --- a/vm/vmcore/src/jit/compile.cpp +++ b/vm/vmcore/src/jit/compile.cpp @@ -36,6 +36,7 @@ #include "jit_runtime_support.h" #include "lil_code_generator.h" #include "stack_iterator.h" #include "interpreter.h" +#include "jvmti_break_intf.h" #include "vm_stats.h" #include "dump.h" diff --git a/vm/vmcore/src/jit/ini.cpp b/vm/vmcore/src/jit/ini.cpp index 3f77dbb..2bdf2b1 100644 --- a/vm/vmcore/src/jit/ini.cpp +++ b/vm/vmcore/src/jit/ini.cpp @@ -25,6 +25,7 @@ #include #include "ini.h" #include "environment.h" #include "open/em_vm.h" +#include "jvmti_break_intf.h" void @@ -45,13 +46,11 @@ vm_execute_java_method_array(jmethodID m if (NULL != vm_thread->ss_state) { // Start single stepping a new Java method - LMAutoUnlock lock(&ti->brkpntlst_lock); + LMAutoUnlock lock(ti->vm_brpt->get_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); + jvmti_set_single_step_breakpoints(ti, vm_thread, &method_start, 1); } } diff --git a/vm/vmcore/src/jvmti/jvmti.cpp b/vm/vmcore/src/jvmti/jvmti.cpp index a7ef3bb..87a6c61 100644 --- a/vm/vmcore/src/jvmti/jvmti.cpp +++ b/vm/vmcore/src/jvmti/jvmti.cpp @@ -33,6 +33,8 @@ #include "open/vm_util.h" #include "environment.h" #include #include "properties.h" +#include "jvmti_break_intf.h" +#include "interpreter_exports.h" #include "port_filepath.h" #include "port_dso.h" @@ -262,6 +264,11 @@ jint JNICALL create_jvmti_environment(Ja memset(&newenv->global_events, 0, sizeof(newenv->global_events)); memset(&newenv->event_threads, 0, sizeof(newenv->event_threads)); + // Acquire interface for breakpoint handling + newenv->brpt_intf = + vm->vm_env->TI->vm_brpt->query_intf(jvmti_process_breakpoint_event, + interpreter_enabled()); + LMAutoUnlock lock(&vm->vm_env->TI->TIenvs_lock); vm->vm_env->TI->addEnvironment(newenv); *env = newenv; @@ -288,7 +295,6 @@ void DebugUtilsTI::setExecutionMode(Glob DebugUtilsTI::DebugUtilsTI() : agent_counter(1), - brkpntlst(NULL), access_watch_list(NULL), modification_watch_list(NULL), status(false), @@ -306,12 +312,15 @@ DebugUtilsTI::DebugUtilsTI() : res = _allocate( MAX_NOTIFY_LIST * sizeof(Class**), (unsigned char**)¬ifyPrepareList ); assert(res == JVMTI_ERROR_NONE); + vm_brpt = new VmBreakpoints(); + assert(vm_brpt); return; } DebugUtilsTI::~DebugUtilsTI() { ReleaseNotifyLists(); + delete vm_brpt; return; } diff --git a/vm/vmcore/src/jvmti/jvmti_break.cpp b/vm/vmcore/src/jvmti/jvmti_break.cpp index 27c0de2..0b85636 100644 --- a/vm/vmcore/src/jvmti/jvmti_break.cpp +++ b/vm/vmcore/src/jvmti/jvmti_break.cpp @@ -28,635 +28,100 @@ #include "environment.h" #include "Class.h" #include "cxxlog.h" -#include "interpreter_exports.h" -#include "interpreter_imports.h" #include "suspend_checker.h" -#include "jit_intf_cpp.h" -#include "encoder.h" -#include "m2n.h" -#include "exceptions.h" -#include "stack_iterator.h" - - -#define INSTRUMENTATION_BYTE_HLT 0xf4 // HLT instruction -#define INSTRUMENTATION_BYTE_CLI 0xfa // CLI instruction -#define INSTRUMENTATION_BYTE_INT3 0xcc // INT 3 instruction -#define INSTRUMENTATION_BYTE INSTRUMENTATION_BYTE_INT3 - +#include "interpreter_exports.h" +#include "jvmti_break_intf.h" #include "open/jthread.h" #include "open/hythread_ext.h" -// Called when current thread reached breakpoint. -// Returns breakpoint ID received from executing engine on creating of the -// breakpoint -VMEXPORT void* -jvmti_process_interpreter_breakpoint_event(jmethodID method, jlocation location) -{ - TRACE2("jvmti.break", "BREAKPOINT occured: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) << method_get_descriptor((Method*)method) - << " :" << location); - ObjectHandle hThread = oh_allocate_local_handle(); - hThread->object = (Java_java_lang_Thread *)jthread_get_java_thread(hythread_self())->object; - tmn_suspend_enable(); - - DebugUtilsTI *ti = VM_Global_State::loader_env->TI; - - ti->brkpntlst_lock._lock(); - - BreakPoint *bp = ti->find_first_bpt(method, location); - assert(bp); // Can't be bytecode without breakpoint - assert(bp->env); // Can't be breakpoint with no environments - void *id = bp->id; - - do - { - TIEnv *env = bp->env; - BreakPoint *next_bp = ti->find_next_bpt(bp, method, location); - - 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 interpreter global breakpoint callback: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location); - - ti->brkpntlst_lock._unlock(); - func((jvmtiEnv*)env, jni_env, (jthread)hThread, method, location); - ti->brkpntlst_lock._lock(); - - TRACE2("jvmti.break", "Finished interpreter global breakpoint callback: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location); - } - bp = next_bp; - continue; // Don't send local events - } - - TIEventThread *next_et; - for (TIEventThread *et = env->event_threads[JVMTI_EVENT_BREAKPOINT - JVMTI_MIN_EVENT_TYPE_VAL]; - NULL != et; et = next_et) - { - next_et = et->next; - if (et->thread == hythread_self()) - { - jvmtiEventBreakpoint func = (jvmtiEventBreakpoint)env->get_event_callback(JVMTI_EVENT_BREAKPOINT); - if (NULL != func) - { - JNIEnv *jni_env = (JNIEnv *)jni_native_intf; - TRACE2("jvmti.break", "Calling interpreter local breakpoint callback: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location); - - ti->brkpntlst_lock._unlock(); - func((jvmtiEnv*)env, jni_env, (jthread)hThread, method, location); - ti->brkpntlst_lock._lock(); - - TRACE2("jvmti.break", "Finished interpreter local breakpoint callback: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location); - } - } - - et = next_et; - } - - bp = next_bp; - } while(bp); - - ti->brkpntlst_lock._unlock(); - - tmn_suspend_disable(); - oh_discard_local_handle(hThread); - return id; -} - -ConditionCode get_condition_code(InstructionDisassembler::CondJumpType jump_type) -{ - // Right now InstructionDisassembler::CondJumpType enum values are - // equal to enums in ia32/em64t encoder, so this statement is ok - return (ConditionCode)jump_type; -} - -static BreakPoint * -jvmti_check_and_get_single_step_breakpoint( DebugUtilsTI *ti, - VM_thread *vm_thread, - NativeCodePtr native_location) -{ - BreakPoint *ss_breakpoint = NULL; - - if (ti->is_single_step_enabled()) - { - if (NULL != vm_thread->ss_state) - { - for(unsigned iii = 0; iii < vm_thread->ss_state->predicted_bp_count; iii++) - { - if (vm_thread->ss_state->predicted_breakpoints[iii]->native_location == - native_location) - { - assert(!ss_breakpoint); - ss_breakpoint = - vm_thread->ss_state->predicted_breakpoints[iii]; - } - } - } - } - return ss_breakpoint; -} // jvmti_check_and_get_single_step_breakpoint -static void jvmti_send_jit_breakpoint_event(void) +// Callback function for JVMTI breakpoint processing +bool jvmti_process_breakpoint_event(VmBrkptIntf* intf, VmBrkptRef* bp_ref) { - // When we get here we know already that breakpoint occurred in JITted code, - // JVMTI handles it, and registers context is saved for us in TLS - VM_thread *vm_thread = p_TLS_vmthread; - Registers regs = vm_thread->jvmti_saved_exception_registers; - -#if PLATFORM_POSIX && INSTRUMENTATION_BYTE == INSTRUMENTATION_BYTE_INT3 - // Int3 exception address points to the instruction after it - NativeCodePtr native_location = (NativeCodePtr)(((POINTER_SIZE_INT)regs.get_ip()) - 1); -#else - NativeCodePtr native_location = (NativeCodePtr)regs.get_ip(); -#endif - - DebugUtilsTI *ti = VM_Global_State::loader_env->TI; - BreakPoint *bp = ti->find_first_bpt(native_location); + VmBrkpt* bp = bp_ref->brpt; assert(bp); - assert(ti->isEnabled()); - assert(!interpreter_enabled()); - assert(ti->getPhase() == JVMTI_PHASE_LIVE); - M2nFrame *m2nf = m2n_push_suspended_frame(®s); - jbyte *instruction_buffer; - BEGIN_RAISE_AREA; + TRACE2("jvmti.break", "BREAKPOINT occured: location=" << bp->location); - // need to be able to pop the frame - frame_type m2nf_type = m2n_get_frame_type(m2nf); - m2nf_type = (frame_type) (m2nf_type | FRAME_POPABLE); - m2n_set_frame_type(m2nf, m2nf_type); + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + if (!ti->isEnabled() || ti->getPhase() != JVMTI_PHASE_LIVE) + return false; + jlocation location = bp->location; + jmethodID method = bp->method; + TIBrptData* data = (TIBrptData*)bp_ref->data; + TIEnv *env = data->env; + hythread_t h_thread = hythread_self(); jthread j_thread = jthread_get_java_thread(h_thread); ObjectHandle hThread = oh_allocate_local_handle(); hThread->object = (Java_java_lang_Thread *)j_thread->object; tmn_suspend_enable(); - void *id = bp->id; - jbyte original_byte = (POINTER_SIZE_INT)id; - // 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 = jvmti_check_and_get_single_step_breakpoint( ti, - vm_thread, native_location ); - // Check if there are Single Step type breakpoints in TLS - if (ss_breakpoint) - { - 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: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_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: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_location); - ti_env = next_env; - continue; - } - - TIEventThread* next_ti_et; - // 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 = next_ti_et) - { - next_ti_et = ti_et->next; - if (ti_et->thread == hythread_self()) - { - TRACE2("jvmti.break.ss", - "Calling JIT local SingleStep breakpoint callback: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_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: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_location); - } - } - } - ti_env = next_env; - } - - // Reinitialize breakpoint after SingleStep because this - // breakpoint could have been deleted inside of callback - // if agent terminated single step - bp = ti->find_first_bpt(native_location); - } + jvmtiEventBreakpoint func = + (jvmtiEventBreakpoint)env->get_event_callback(JVMTI_EVENT_BREAKPOINT); - - // Send events for all normally set breakpoints for this location - // if there are any - while (bp) + if (NULL != func) { - TIEnv *env = bp->env; - jlocation location = bp->location; - jmethodID method = bp->method; - BreakPoint *next_bp = ti->find_next_bpt(bp, native_location); - - // check if this is a single step breakpoint - if (!bp->env) - { - // Don't send breakpoint event for breakpoint which was - // actually SingleStep breakpoint - bp = next_bp; - 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) - { - TRACE2("jvmti.break", "Calling JIT global breakpoint callback: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_location); - - ti->brkpntlst_lock._unlock(); - func((jvmtiEnv*)env, jni_env, (jthread)hThread, method, location); - ti->brkpntlst_lock._lock(); - - TRACE2("jvmti.break", "Finished JIT global breakpoint callback: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_location); - } - bp = next_bp; - continue; // Don't send local events + TRACE2("jvmti.break", "Calling global breakpoint callback: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location); + + intf->unlock(); + func((jvmtiEnv*)env, jni_env, (jthread)hThread, method, location); + intf->lock(); + + TRACE2("jvmti.break", "Finished global breakpoint callback: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location); } - - TIEventThread *next_et; - for (TIEventThread *et = env->event_threads[JVMTI_EVENT_BREAKPOINT - JVMTI_MIN_EVENT_TYPE_VAL]; - NULL != et; et = next_et) + else { - next_et = et->next; - if (et->thread == hythread_self()) + TIEventThread *next_et; + TIEventThread *first_et = + env->event_threads[JVMTI_EVENT_BREAKPOINT - JVMTI_MIN_EVENT_TYPE_VAL]; + + for (TIEventThread *et = first_et; NULL != et; et = next_et) { - jvmtiEventBreakpoint func = (jvmtiEventBreakpoint)env->get_event_callback(JVMTI_EVENT_BREAKPOINT); - if (NULL != func) + next_et = et->next; + + if (et->thread == hythread_self()) { - JNIEnv *jni_env = (JNIEnv *)jni_native_intf; - TRACE2("jvmti.break", "Calling JIT local breakpoint callback: " + TRACE2("jvmti.break", "Calling local breakpoint callback: " << class_get_name(method_get_class((Method*)method)) << "." << method_get_name((Method*)method) << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_location); + << " :" << location); - ti->brkpntlst_lock._unlock(); + intf->unlock(); func((jvmtiEnv*)env, jni_env, (jthread)hThread, method, location); - ti->brkpntlst_lock._lock(); + intf->lock(); - TRACE2("jvmti.break", "Finished JIT local breakpoint callback: " + TRACE2("jvmti.break", "Finished local breakpoint callback: " << class_get_name(method_get_class((Method*)method)) << "." << method_get_name((Method*)method) << method_get_descriptor((Method*)method) - << " :" << location << " :" << native_location); + << " :" << location); } } - - et = next_et; } - - bp = next_bp; - } - - // Now we need to return back to normal code execution, it is - // necessary to execute the original instruction The idea is to - // recreate the original instriction in a special thread local - // buffer followed by a jump to an instruction next after it. In - // case the instruction was a relative jump or call it requires - // special handling. - InstructionDisassembler::Type type = idisasm.get_type(); - - instruction_buffer = vm_thread->jvmti_jit_breakpoints_handling_buffer; - jbyte *interrupted_instruction = (jbyte *)native_location; - jint instruction_length = idisasm.get_length_with_prefix(); - - switch(type) - { - case InstructionDisassembler::UNKNOWN: - { - char *next_instruction = (char *)interrupted_instruction + - instruction_length; - - // Copy original instruction to the exeuction buffer - *instruction_buffer = original_byte; - memcpy(instruction_buffer + 1, interrupted_instruction + 1, - instruction_length - 1); - - // Create JMP $next_instruction instruction in the execution buffer - jump((char *)instruction_buffer + instruction_length, - next_instruction); - break; } - case InstructionDisassembler::RELATIVE_JUMP: - { - jint instruction_length = idisasm.get_length_with_prefix(); - char *jump_target = (char *)idisasm.get_jump_target_address(); - - // Create JMP to the absolute address which conditional jump - // had in the execution buffer - jump((char *)instruction_buffer, jump_target); - break; - } - case InstructionDisassembler::RELATIVE_COND_JUMP: - { - char *code = (char *)instruction_buffer; - InstructionDisassembler::CondJumpType jump_type = - idisasm.get_cond_jump_type(); - char *next_instruction = (char *)interrupted_instruction + - instruction_length; - char *jump_target = (char *)idisasm.get_jump_target_address(); - - // Create a conditional JMP of the same type over 1 - // instruction forward, the next instruction is JMP to the - // $next_instruction - code = branch8(code, get_condition_code(jump_type), Imm_Opnd(size_8, 0)); - char *branch_address = code - 1; - - code = jump(code, next_instruction); - jint offset = code - branch_address - 1; - *branch_address = offset; - - jump(code, jump_target); - break; - } - case InstructionDisassembler::ABSOLUTE_CALL: - case InstructionDisassembler::RELATIVE_CALL: - { - jbyte *next_instruction = interrupted_instruction + instruction_length; - char *jump_target = (char *)idisasm.get_jump_target_address(); - char *code = (char *)instruction_buffer; - - // Push "return address" to the $next_instruction - code = push(code, Imm_Opnd((POINTER_SIZE_INT)next_instruction)); - - // Jump to the target address of the call instruction - jump(code, jump_target); - break; - } - } - - // Set breakpoints on bytecodes after the current one - ss_breakpoint = jvmti_check_and_get_single_step_breakpoint( ti, - vm_thread, native_location ); - if (ss_breakpoint) - { - jvmti_StepLocation *locations; - unsigned locations_count; - - jvmti_SingleStepLocation(vm_thread, (Method *)ss_breakpoint->method, - (unsigned)ss_breakpoint->location, &locations, &locations_count); - - jvmti_remove_single_step_breakpoints(ti, vm_thread); - - 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(); oh_discard_local_handle(hThread); - END_RAISE_AREA; - - // This function does not return. It restores register context and - // transfers execution control to the instruction buffer to - // execute the original instruction with the registers which it - // had before breakpoint happened - StackIterator *si = si_create_from_registers(®s, false, m2n_get_previous_frame(m2nf)); - - m2n_set_last_frame(m2n_get_previous_frame(m2nf)); - STD_FREE(m2nf); - - si_set_ip(si, instruction_buffer, false); - si_transfer_control(si); -} - -bool jvmti_jit_breakpoint_handler(Registers *regs) -{ -#if PLATFORM_POSIX && INSTRUMENTATION_BYTE == INSTRUMENTATION_BYTE_INT3 - // Int3 exception address points to the instruction after it - NativeCodePtr native_location = (NativeCodePtr)(((POINTER_SIZE_INT)regs->get_ip()) - 1); -#else - NativeCodePtr native_location = (NativeCodePtr)regs->get_ip(); -#endif - - TRACE2("jvmti.break", "BREAKPOINT occured: " << native_location); - - DebugUtilsTI *ti = VM_Global_State::loader_env->TI; - if (!ti->isEnabled() || ti->getPhase() != JVMTI_PHASE_LIVE) - return false; - - ti->brkpntlst_lock._lock(); - BreakPoint *bp = ti->find_first_bpt(native_location); - if (NULL == bp) - { - ti->brkpntlst_lock._unlock(); - return false; - } - - assert(ti->isEnabled()); - assert(!interpreter_enabled()); - - // Now it is necessary to set up a transition to - // jvmti_send_jit_breakpoint_event from the exception/signal - // handler - VM_thread *vm_thread = p_TLS_vmthread; - // Copy original registers to TLS - vm_thread->jvmti_saved_exception_registers = *regs; -#ifndef _EM64T_ - regs->eip = (POINTER_SIZE_INT)jvmti_send_jit_breakpoint_event; -#else - regs->rip = (POINTER_SIZE_INT)jvmti_send_jit_breakpoint_event; -#endif - - // Breakpoints list lock is not released until it is unlocked - // inside of jvmti_send_jit_breakpoint_event return true; } -jvmtiError jvmti_set_jit_mode_breakpoint(DebugUtilsTI *ti, BreakPoint *bp) -{ - // Function is always executed under global TI breakpoints lock - // Find native location in the method code - Method *m = (Method *)bp->method; - assert( m->get_state() == Method::ST_Compiled ); - - NativeCodePtr np = bp->native_location; - if( !np ) { - OpenExeJpdaError res = EXE_ERROR_NONE; - for (CodeChunkInfo* cci = m->get_first_JIT_specific_info(); cci; cci = cci->_next) - { - JIT *jit = cci->get_jit(); - res = jit->get_native_location_for_bc(m, (uint16)bp->location, &np); - if (res == EXE_ERROR_NONE) - break; - } - assert(res == EXE_ERROR_NONE); - } - - if (NULL == np) - return JVMTI_ERROR_INTERNAL; - - TRACE2("jvmti.break", "Set breakpoint: " - << class_get_name(method_get_class((Method *)bp->method)) << "." - << method_get_name((Method *)bp->method) - << method_get_descriptor((Method *)bp->method) - << " :" << bp->location << " :" << np); - - bp->native_location = np; - - BreakPoint *other_bp = ti->get_other_breakpoint_same_native_location(bp); - if (other_bp) // No other breakpoints were set in this place - { - assert(NULL == bp->disasm); - bp->id = other_bp->id; - bp->disasm = new InstructionDisassembler(*other_bp->disasm); - } else { - bp->disasm = new InstructionDisassembler(np); - - jbyte *target_instruction = (jbyte *)np; - bp->id = (void *)(POINTER_SIZE_INT)*target_instruction; - *target_instruction = (jbyte)INSTRUMENTATION_BYTE; - } - - return JVMTI_ERROR_NONE; -} - -void jvmti_set_pending_breakpoints(Method *method) -{ - DebugUtilsTI *ti = VM_Global_State::loader_env->TI; - if (!ti->isEnabled()) - return; - - assert(method->get_pending_breakpoints() > 0); - - LMAutoUnlock lock(&ti->brkpntlst_lock); - - jmethodID mid = (jmethodID)method; - BreakPoint *bp = ti->find_first_bpt(mid); - assert(bp); - - jlocation *locations = (jlocation *)STD_MALLOC(sizeof(jlocation) * - method->get_pending_breakpoints()); - assert(locations); - uint32 location_count = 0; - - do - { - // It is necessary to set breakpoints only once for each - // location, so we need to filter out duplicate breakpoints - for (uint32 iii = 0; iii < location_count; iii++) - if (bp->location == locations[iii]) - continue; - - jvmti_set_jit_mode_breakpoint(ti, bp); - locations[location_count++] = bp->location; - - method->remove_pending_breakpoint(); - bp = ti->find_next_bpt(bp, mid); - } - while(NULL != bp); - - assert(method->get_pending_breakpoints() == 0); - 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); - - 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(ti, bp); - - if (JVMTI_ERROR_NONE != errorCode) - return JVMTI_ERROR_INTERNAL; - } - else - { - TRACE2("jvmti.break", "Skipping setting breakpoint: " - << class_get_name(method_get_class((Method *)bp->method)) << "." - << method_get_name((Method *)bp->method) - << method_get_descriptor((Method *)bp->method) - << " :" << bp->location << ", because it is not compiled yet"); - m->insert_pending_breakpoint(); - } - } - else - { - bp->id = other_bp->id; - bp->native_location = other_bp->native_location; - assert(NULL == bp->disasm); - bp->disasm = new InstructionDisassembler(*other_bp->disasm); - } - - ti->add_breakpoint(bp); - return JVMTI_ERROR_NONE; -} - /* * Set Breakpoint * @@ -670,12 +135,7 @@ jvmtiSetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) { - TRACE2("jvmti.break", "SetBreakpoint called: " - << class_get_name(method_get_class((Method *)method)) << "." - << method_get_name((Method *)method) - << method_get_descriptor((Method *)method) - << " :" << location); - + TRACE2("jvmti.break", "SetBreakpoint called, method = " << method << " , location = " << location); SuspendEnabledChecker sec; jvmtiError errorCode; @@ -721,72 +181,31 @@ #endif return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; TIEnv *p_env = (TIEnv *)env; - DebugUtilsTI *ti = p_env->vm->vm_env->TI; - LMAutoUnlock lock(&ti->brkpntlst_lock); - BreakPoint *bp = ti->find_breakpoint(method, location, p_env); + VmBrkptIntf* brpt_intf = p_env->brpt_intf; + LMAutoUnlock lock(brpt_intf->get_lock()); + + VmBrkptRef* bp = brpt_intf->find(method, location); if (NULL != bp) return JVMTI_ERROR_DUPLICATE; - errorCode = _allocate(sizeof(BreakPoint), (unsigned char **)&bp); + TIBrptData* data; + errorCode = _allocate(sizeof(TIBrptData), (unsigned char**)&data); if (JVMTI_ERROR_NONE != errorCode) return errorCode; - memset( bp, 0, sizeof(BreakPoint)); - bp->method = method; - bp->location = location; - bp->env = p_env; - - if (interpreter_enabled()) - { - BreakPoint *other_bp = ti->get_other_breakpoint_same_location(bp); - - if (NULL == other_bp) // No other breakpoints were set in this place - bp->id = interpreter.interpreter_ti_set_breakpoint(method, location); + data->env = p_env; - ti->add_breakpoint(bp); - } - else + if (!brpt_intf->add(method, location, data)) { - errorCode = jvmti_set_breakpoint_for_jit(ti, bp); - - if (JVMTI_ERROR_NONE != errorCode) - return JVMTI_ERROR_INTERNAL; + _deallocate((unsigned char*)data); + 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 *m = (Method *)bp->method; - - if (m->get_state() == Method::ST_Compiled) - { - if (NULL == ti->get_other_breakpoint_same_native_location(bp)) - { - jbyte *target_instruction = (jbyte *)bp->native_location; - *target_instruction = (POINTER_SIZE_INT)bp->id; - } - } - else - m->remove_pending_breakpoint(); - } - - TRACE2("jvmti.break", "Remove breakpoint: " - << class_get_name(method_get_class((Method *)bp->method)) << "." - << method_get_name((Method *)bp->method) - << method_get_descriptor((Method *)bp->method) - << " :" << bp->location << " :" << bp->native_location); - - ti->remove_breakpoint(bp); -} - /* * Clear Breakpoint * @@ -800,11 +219,7 @@ jvmtiClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) { - TRACE2("jvmti.break", "ClearBreakpoint called: " - << class_get_name(method_get_class((Method*)method)) << "." - << method_get_name((Method*)method) - << method_get_descriptor((Method*)method) - << " :" << location); + TRACE2("jvmti.break", "ClearBreakpoint called, method = " << method << " , location = " << location); SuspendEnabledChecker sec; jvmtiError errorCode; @@ -850,23 +265,16 @@ #endif return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; TIEnv *p_env = (TIEnv *)env; - DebugUtilsTI *ti = p_env->vm->vm_env->TI; - LMAutoUnlock lock(&ti->brkpntlst_lock); - BreakPoint *bp = ti->find_breakpoint(method, location, p_env); + VmBrkptIntf* brpt_intf = p_env->brpt_intf; + LMAutoUnlock lock(brpt_intf->get_lock()); + + VmBrkptRef* bp = brpt_intf->find(method, location); if (NULL == bp) return JVMTI_ERROR_NOT_FOUND; - if (interpreter_enabled()) - { - if (NULL == ti->get_other_breakpoint_same_location(bp)) - // No other breakpoints were set in this place - interpreter.interpreter_ti_clear_breakpoint(method, location, bp->id); - - ti->remove_breakpoint(bp); - } - else - jvmti_remove_breakpoint_for_jit(ti, bp); + if (!brpt_intf->remove(bp)) + return JVMTI_ERROR_INTERNAL; TRACE2("jvmti.break", "ClearBreakpoint successfull"); return JVMTI_ERROR_NONE; diff --git a/vm/vmcore/src/jvmti/jvmti_break_intf.cpp b/vm/vmcore/src/jvmti/jvmti_break_intf.cpp new file mode 100755 index 0000000..264d3a3 --- /dev/null +++ b/vm/vmcore/src/jvmti/jvmti_break_intf.cpp @@ -0,0 +1,1045 @@ +/* + * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Ilya Berezhniuk, Gregory Shimansky + * @version $Revision$ + */ +/** + * @file jvmti_break_intf.cpp + * @brief JVMTI native breakpoints API implementation + */ + +#include "cxxlog.h" +#include "environment.h" +#include "encoder.h" +#include "interpreter_exports.h" +#include "jit_intf_cpp.h" +#include "method_lookup.h" +#include "exceptions.h" +#include "m2n.h" +#include "stack_iterator.h" + +#include "jvmti_break_intf.h" + + +// Forvard declarations +static ConditionCode +get_condition_code(InstructionDisassembler::CondJumpType jump_type); +static bool set_jit_mode_breakpoint(VmBreakpoints* vm_brpt, VmBrkpt* bp); +static bool set_native_breakpoint(VmBrkpt* bp); +static bool clear_native_breakpoint(VmBrkpt* bp); + + +////////////////////////////////////////////////////////////////////////////// +// VmBreakpoints implementation +VmBreakpoints::VmBreakpoints(): + m_intf_list(NULL), m_brpt_list(NULL) +{} + +VmBreakpoints::~VmBreakpoints() +{ + lock(); + + while (m_intf_list) + { + VmBrkptIntf* next = m_intf_list->m_next; + delete m_intf_list; + m_intf_list = next; + } + + assert(m_brpt_list == NULL); + + unlock(); +} + + +VmBrkptIntf* VmBreakpoints::query_intf(fn_interface_callback callback, + bool is_interp) +{ + if (!callback) + return NULL; + + VmBrkptIntf* intf = + new VmBrkptIntf(callback, is_interp); + + if (!intf) + return NULL; + + lock(); + intf->m_next = m_intf_list; + m_intf_list = intf; + unlock(); + + return intf; +} + +void VmBreakpoints::release_intf(VmBrkptIntf* intf) +{ + assert(intf); + + LMAutoUnlock lock(get_lock()); + + for (VmBrkptIntf** cur_ptr = &m_intf_list; + *cur_ptr; cur_ptr = &((*cur_ptr)->m_next)) + { + if (*cur_ptr == intf) + { + *cur_ptr = (*cur_ptr)->m_next; + + delete intf; + return; + } + } +} + +bool VmBreakpoints::insert_breakpoint(VmBrkpt* brpt) +{ + assert(brpt); + assert(!brpt->method || !find_breakpoint(brpt->method, brpt->location)); + assert(brpt->method || (brpt->addr && (!find_breakpoint(brpt->addr) || find_breakpoint(brpt->addr)->method))); + + if (brpt->is_interp) + { + assert(interpreter_enabled()); + brpt->saved_byte = (POINTER_SIZE_INT) + interpreter.interpreter_ti_set_breakpoint(brpt->method, brpt->location); + } + else + { + if (brpt->method != NULL) + { // JIT breakpoint + Method *m = (Method *)brpt->method; + + if (m->get_state() == Method::ST_Compiled) + { + if (!set_jit_mode_breakpoint(this, brpt)) + return false; + } + else + { + assert(brpt->addr == NULL); + TRACE2("jvmti.break.intf", "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 + { + if (!set_native_breakpoint(brpt)) + return false; + } + } + + brpt->next = m_brpt_list; + m_brpt_list = brpt; + + return true; +} + +bool VmBreakpoints::remove_breakpoint(VmBrkpt* brpt) +{ + assert(brpt); + assert(!brpt->method || find_breakpoint(brpt->method, brpt->location)); + assert(brpt->method || find_breakpoint(brpt->addr)); + + for (VmBrkpt** cur_ptr = &m_brpt_list; + *cur_ptr; cur_ptr = &(*cur_ptr)->next) + { + if (*cur_ptr == brpt) + { + *cur_ptr = (*cur_ptr)->next; + break; + } + } + + if (brpt->is_interp) + { + assert(interpreter_enabled()); + interpreter.interpreter_ti_clear_breakpoint(brpt->method, + brpt->location, brpt->saved_byte); + } + else + { + if (brpt->addr) + { + assert(!brpt->method || (((Method*)brpt->method)->get_state() == Method::ST_Compiled)); + return clear_native_breakpoint(brpt); + } + else + { + assert(brpt->method && (((Method*)brpt->method)->get_state() != Method::ST_Compiled)); + Method *m = (Method *)brpt->method; + m->remove_pending_breakpoint(); + } + } + + return true; +} + +VmBrkpt* VmBreakpoints::find_breakpoint(jmethodID method, jlocation location) +{ + for (VmBrkpt* brpt = m_brpt_list; brpt; brpt = brpt->next) + { + if (brpt->method == method && + brpt->location == location) + return brpt; + } + + return NULL; +} + +VmBrkpt* VmBreakpoints::find_breakpoint(NativeCodePtr addr) +{ + assert(addr); + + for (VmBrkpt* brpt = m_brpt_list; brpt; brpt = brpt->next) + { + if (brpt->addr == addr) + return brpt; + } + + return NULL; +} + +VmBrkpt* VmBreakpoints::find_next_breakpoint(VmBrkpt* prev, NativeCodePtr addr) +{ + assert(addr && prev); + + for (VmBrkpt* brpt = prev->next; brpt; brpt = brpt->next) + { + if (brpt->addr == addr) + return brpt; + } + + return NULL; +} + +bool VmBreakpoints::has_breakpoint(jmethodID method) +{ + return (find_first(method) != NULL); +} + +VmBrkpt* VmBreakpoints::find_first(jmethodID method) +{ + assert(method); + + for (VmBrkpt* brpt = m_brpt_list; brpt; brpt = brpt->next) + { + if (brpt->method && + brpt->method == method) + return brpt; + } + + return NULL; +} + +VmBrkpt* VmBreakpoints::find_next(VmBrkpt* prev, jmethodID method) +{ + assert(prev); + + for (VmBrkpt* brpt = prev->next; brpt; brpt = brpt->next) + { + if (brpt->method == method) + return brpt; + } + + return NULL; +} + +VmBrkptRef* VmBreakpoints::find_other(VmBrkptIntf* intf, + jmethodID method, jlocation location) +{ + assert(intf); + + for (VmBrkptIntf* cur = m_intf_list; cur; cur = cur->m_next) + { + if (cur == intf) + continue; + + VmBrkptRef* ref = cur->find(method, location); + + if (ref) + return ref; + } + + return NULL; +} + +VmBrkptRef* VmBreakpoints::find_other(VmBrkptIntf* intf, NativeCodePtr addr) +{ + assert(intf); + + for (VmBrkptIntf* cur = m_intf_list; cur; cur = cur->m_next) + { + if (cur == intf) + continue; + + VmBrkptRef* ref = cur->find(addr); + + if (ref) + return ref; + } + + return NULL; +} + +VmBrkptRef* VmBreakpoints::find_other(VmBrkptIntf* intf, VmBrkpt* brpt) +{ + assert(intf); + + for (VmBrkptIntf* cur = m_intf_list; cur; cur = cur->m_next) + { + if (cur == intf) + continue; + + VmBrkptRef* ref = cur->find(brpt); + + if (ref) + return ref; + } + + return NULL; +} + + +#define INSTRUMENTATION_BYTE_HLT 0xf4 // HLT instruction +#define INSTRUMENTATION_BYTE_CLI 0xfa // CLI instruction +#define INSTRUMENTATION_BYTE_INT3 0xcc // INT 3 instruction +#define INSTRUMENTATION_BYTE INSTRUMENTATION_BYTE_INT3 + +void VmBreakpoints::process_native_breakpoint() +{ + // When we get here we know already that breakpoint occurred in JITted code, + // JVMTI handles it, and registers context is saved for us in TLS + VM_thread *vm_thread = p_TLS_vmthread; + Registers regs = vm_thread->jvmti_saved_exception_registers; + +#if PLATFORM_POSIX && INSTRUMENTATION_BYTE == INSTRUMENTATION_BYTE_INT3 + // Int3 exception address points to the instruction after it + NativeCodePtr addr = + (NativeCodePtr)(((POINTER_SIZE_INT)regs.get_ip()) - 1); +#else + NativeCodePtr addr = (NativeCodePtr)regs.get_ip(); +#endif + + TRACE2("jvmti.break.intf", "Native breakpoint occured, location = " << addr); + + VmBrkpt* bp = this->find_breakpoint(addr); + assert(bp); + + bool push_frame = (vm_identify_eip(addr) == VM_TYPE_JAVA); + M2nFrame* m2nf; + + if (push_frame) + { + m2nf = m2n_push_suspended_frame(®s); + } + else + m2nf = m2n_get_last_frame(); + + jbyte *instruction_buffer; + BEGIN_RAISE_AREA; + + // need to be able to pop the frame + frame_type m2nf_type = m2n_get_frame_type(m2nf); + m2nf_type = (frame_type) (m2nf_type | FRAME_POPABLE); + m2n_set_frame_type(m2nf, m2nf_type); + + jbyte orig_byte = bp->saved_byte; + // Copy disassembler instance in case a breakpoint is deleted + // inside of callbacks + InstructionDisassembler idisasm(*bp->disasm); + + while (bp) + { + VmBrkpt* next_bp = this->find_next_breakpoint(bp, addr); + + for (int priority = BREAK_MAX_PRIORITY; + priority <= BREAK_MIN_PRIORITY; priority++) + { + VmBrkptIntf* intf; + + while (NULL != (intf = get_intf_to_process(priority))) + { + VmBrkptRef* ref = intf->find(bp); + + if (ref && bp->addr != addr) + break; // It's another breakpoint now... + + if (ref && intf->m_callback != NULL) + { + TRACE2("jvmti.break.intf", + "Calling native breakpoint callback function, location = " << addr); + + intf->m_callback(intf, ref); + + TRACE2("jvmti.break.intf", + "Finished native breakpoint callback function, location = " << addr); + } + + intf->set_processed(true); + } + } + + clear_processed_flags(); + bp = next_bp; + } + + // Now we need to return back to normal code execution, it is + // necessary to execute the original instruction The idea is to + // recreate the original instriction in a special thread local + // buffer followed by a jump to an instruction next after it. In + // case the instruction was a relative jump or call it requires + // special handling. + InstructionDisassembler::Type type = idisasm.get_type(); + + instruction_buffer = vm_thread->jvmti_jit_breakpoints_handling_buffer; + jbyte *interrupted_instruction = (jbyte *)addr; + jint instruction_length = idisasm.get_length_with_prefix(); + + switch(type) + { + case InstructionDisassembler::UNKNOWN: + { + char *next_instruction = (char *)interrupted_instruction + + instruction_length; + + // Copy original instruction to the exeuction buffer + *instruction_buffer = orig_byte; + memcpy(instruction_buffer + 1, interrupted_instruction + 1, + instruction_length - 1); + + // Create JMP $next_instruction instruction in the execution buffer + jump((char *)instruction_buffer + instruction_length, + next_instruction); + break; + } + case InstructionDisassembler::RELATIVE_JUMP: + { + jint instruction_length = idisasm.get_length_with_prefix(); + char *jump_target = (char *)idisasm.get_jump_target_address(); + + // Create JMP to the absolute address which conditional jump + // had in the execution buffer + jump((char *)instruction_buffer, jump_target); + break; + } + case InstructionDisassembler::RELATIVE_COND_JUMP: + { + char *code = (char *)instruction_buffer; + InstructionDisassembler::CondJumpType jump_type = + idisasm.get_cond_jump_type(); + char *next_instruction = (char *)interrupted_instruction + + instruction_length; + char *jump_target = (char *)idisasm.get_jump_target_address(); + + // Create a conditional JMP of the same type over 1 + // instruction forward, the next instruction is JMP to the + // $next_instruction + code = branch8(code, get_condition_code(jump_type), Imm_Opnd(size_8, 0)); + char *branch_address = code - 1; + + code = jump(code, next_instruction); + jint offset = code - branch_address - 1; + *branch_address = offset; + + jump(code, jump_target); + break; + } + case InstructionDisassembler::ABSOLUTE_CALL: + case InstructionDisassembler::RELATIVE_CALL: + { + jbyte *next_instruction = interrupted_instruction + instruction_length; + char *jump_target = (char *)idisasm.get_jump_target_address(); + char *code = (char *)instruction_buffer; + + // Push "return address" to the $next_instruction + code = push(code, Imm_Opnd((POINTER_SIZE_INT)next_instruction)); + + // Jump to the target address of the call instruction + jump(code, jump_target); + break; + } + } + + unlock(); + + END_RAISE_AREA; + + // This function does not return. It restores register context and + // transfers execution control to the instruction buffer to + // execute the original instruction with the registers which it + // had before breakpoint happened + StackIterator *si = + si_create_from_registers(®s, false, m2n_get_previous_frame(m2nf)); + + if (push_frame) + { + m2n_set_last_frame(m2n_get_previous_frame(m2nf)); + STD_FREE(m2nf); + } + + si_set_ip(si, instruction_buffer, false); + si_transfer_control(si); +} + +jbyte VmBreakpoints::process_interpreter_breakpoint(jmethodID method, jlocation location) +{ + TRACE2("jvmti.break.intf", "Interpreter breakpoint occured, method = " << + method << " location = " << location); + + assert(interpreter_enabled()); + + lock(); + + VmBrkpt* bp = find_breakpoint(method, location); + assert(bp); + + jbyte orig_byte = bp->saved_byte; + + for (int priority = BREAK_MAX_PRIORITY; + priority <= BREAK_MIN_PRIORITY; priority++) + { + VmBrkptIntf* intf; + + while (NULL != (intf = get_intf_to_process(priority))) + { + VmBrkptRef* ref = intf->find(bp); + + if (ref && + ( bp->method != method || + bp->location != location )) + break; // It's another breakpoint now... + + if (ref && intf->m_callback != NULL) + { + JNIEnv *jni_env = (JNIEnv *)jni_native_intf; + TRACE2("jvmti.break.intf", + "Calling interpreter breakpoint callback function, location = " + << location); + + intf->m_callback(intf, ref); + + TRACE2("jvmti.break.intf", + "Finished interpreter breakpoint callback function, location = " + << location); + } + + intf->set_processed(true); + } + } + + clear_processed_flags(); + unlock(); + + return orig_byte; +} + +void VmBreakpoints::clear_processed_flags() +{ + // Must be executed under lock + for (VmBrkptIntf* intf = m_intf_list; intf; intf = intf->m_next) + { + intf->set_processed(false); + } +} + +VmBrkptIntf* VmBreakpoints::get_intf_to_process(int priority) +{ + // Must be executed under lock + for (VmBrkptIntf* intf = m_intf_list; intf; intf = intf->m_next) + { + if (!intf->is_processed() && + intf->get_priority() == priority) + return intf; + } + + return NULL; +} + + +////////////////////////////////////////////////////////////////////////////// +// VmBrkptIntf implementation +VmBrkptIntf::VmBrkptIntf(fn_interface_callback callback, bool is_interp) + : m_list(NULL), m_callback(callback), m_priority(BREAK_MIN_PRIORITY), + m_next(NULL), m_processed(false), m_is_interp(is_interp) +{} + +VmBrkptIntf::~VmBrkptIntf() +{ + remove_all(); +} + +void VmBrkptIntf::lock() +{ + VM_Global_State::loader_env->TI->vm_brpt->lock(); +} + +void VmBrkptIntf::unlock() +{ + VM_Global_State::loader_env->TI->vm_brpt->unlock(); +} + +Lock_Manager* VmBrkptIntf::get_lock() +{ + return VM_Global_State::loader_env->TI->vm_brpt->get_lock(); +} + +bool VmBrkptIntf::set_priority(int priority) +{ + if (priority < BREAK_MAX_PRIORITY || priority > BREAK_MIN_PRIORITY) + return false; + + m_priority = priority; + return true; +} + +VmBrkptRef* VmBrkptIntf::get_first() +{ + return m_list; +} + +VmBrkptRef* VmBrkptIntf::get_next(VmBrkptRef* prev) +{ + assert(prev); + return prev->next; +} + +VmBrkptRef* VmBrkptIntf::add(jmethodID method, jlocation location, void* data) +{ + assert(method); + assert(!this->find(method, location)); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + VmBrkpt* brpt = vm_brpt->find_breakpoint(method, location); + + if (!brpt) + { + brpt = (VmBrkpt*)STD_MALLOC(sizeof(VmBrkpt)); + assert(brpt); + + brpt->is_interp = m_is_interp; + brpt->addr = NULL; + brpt->method = method; + brpt->location = location; + brpt->saved_byte = 0; + brpt->disasm = NULL; + + // Insert breakpoint, possibly to the same native address + if (!vm_brpt->insert_breakpoint(brpt)) + { + STD_FREE(brpt); + return false; + } + } + + VmBrkptRef* brpt_ref = + (VmBrkptRef*)STD_MALLOC(sizeof(VmBrkptRef)); + assert(brpt_ref); + + brpt_ref->brpt = brpt; + brpt_ref->data = data; + brpt_ref->next = m_list; + m_list = brpt_ref; + + return brpt_ref; +} + +VmBrkptRef* VmBrkptIntf::add(jmethodID method, jlocation location, + NativeCodePtr addr, void* data) +{ + assert(method); + assert(!this->find(method, location)); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + VmBrkpt* brpt = vm_brpt->find_breakpoint(method, location); + assert(!brpt || !brpt->addr || !addr || brpt->addr == addr); + + if (!brpt) + { + brpt = (VmBrkpt*)STD_MALLOC(sizeof(VmBrkpt)); + assert(brpt); + + brpt->is_interp = m_is_interp; + brpt->addr = addr; + brpt->method = method; + brpt->location = location; + brpt->saved_byte = 0; + brpt->disasm = NULL; + + if (!vm_brpt->insert_breakpoint(brpt)) + { + STD_FREE(brpt); + return false; + } + } + + VmBrkptRef* brpt_ref = + (VmBrkptRef*)STD_MALLOC(sizeof(VmBrkptRef)); + assert(brpt_ref); + + brpt_ref->brpt = brpt; + brpt_ref->data = data; + brpt_ref->next = m_list; + m_list = brpt_ref; + + return brpt_ref; +} + +VmBrkptRef* VmBrkptIntf::add(NativeCodePtr addr, void* data) +{ + assert(addr); + assert(!this->m_is_interp); + assert(!this->find(addr)); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + VmBrkpt* brpt; + // Look for native breakpoint (with method == NULL) + for (brpt = vm_brpt->find_breakpoint(addr); + brpt && brpt->method; + brpt = vm_brpt->find_next_breakpoint(brpt, addr)); + + if (!brpt) + { + brpt = (VmBrkpt*)STD_MALLOC(sizeof(VmBrkpt)); + assert(brpt); + + brpt->is_interp = m_is_interp; // false + brpt->addr = addr; + brpt->method = NULL; + brpt->location = 0; + brpt->saved_byte = 0; + brpt->disasm = NULL; + + // Insert breakpoint, possibly duplicating breakpoint with method != NULL + if (!vm_brpt->insert_breakpoint(brpt)) + { + STD_FREE(brpt); + return false; + } + } + + VmBrkptRef* brpt_ref = + (VmBrkptRef*)STD_MALLOC(sizeof(VmBrkptRef)); + assert(brpt_ref); + + brpt_ref->brpt = brpt; + brpt_ref->data = data; + brpt_ref->next = m_list; + m_list = brpt_ref; + + return brpt_ref; +} + +bool VmBrkptIntf::remove(VmBrkptRef* ref) +{ + assert(ref); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + VmBrkptRef* found = NULL; + + for (VmBrkptRef** cur_ptr = &m_list; + *cur_ptr; cur_ptr = &(*cur_ptr)->next) + { + if (*cur_ptr == ref) + { + found = *cur_ptr; + *cur_ptr = (*cur_ptr)->next; + break; + } + } + + assert(found); + + VmBrkpt* brpt = found->brpt; + assert(brpt); + + if (found->data) + _deallocate((unsigned char*)found->data); + + STD_FREE(found); + + if (vm_brpt->find_other(this, brpt)) + return true; // There are some other references to the same breakpoint + + if (!vm_brpt->remove_breakpoint(brpt)) + return false; + + STD_FREE(brpt); + return true; +} + +VmBrkptRef* VmBrkptIntf::find(jmethodID method, jlocation location) +{ + assert(method); + + for (VmBrkptRef* ref = m_list; ref; ref = ref->next) + { + if (ref->brpt->method && + ref->brpt->method == method && + ref->brpt->location == location) + { + return ref; + } + } + + return NULL; +} + +VmBrkptRef* VmBrkptIntf::find(NativeCodePtr addr) +{ + assert(addr); + + for (VmBrkptRef* ref = m_list; ref; ref = ref->next) + { + if (ref->brpt->addr == addr) + { + return ref; + } + } + + return NULL; +} + +VmBrkptRef* VmBrkptIntf::find(VmBrkpt* brpt) +{ + assert(brpt); + + for (VmBrkptRef* ref = m_list; ref; ref = ref->next) + { + if (ref->brpt == brpt) + { + return ref; + } + } + + return NULL; +} + +void VmBrkptIntf::remove_all() +{ + while (m_list) + remove(m_list); +} + +////////////////////////////////////////////////////////////////////////////// +// Helper functions + +static inline ConditionCode +get_condition_code(InstructionDisassembler::CondJumpType jump_type) +{ + // Right now InstructionDisassembler::CondJumpType enum values are + // equal to enums in ia32/em64t encoder, so this statement is ok + return (ConditionCode)jump_type; +} + +static bool set_jit_mode_breakpoint(VmBreakpoints* vm_brpt, VmBrkpt* bp) +{ + assert(bp); + + // Find native location in the method code + Method *m = (Method *)bp->method; + assert( m->get_state() == Method::ST_Compiled ); + + NativeCodePtr np = bp->addr; + if (!np) + { + OpenExeJpdaError res = EXE_ERROR_NONE; + for (CodeChunkInfo* cci = m->get_first_JIT_specific_info(); + cci; cci = cci->_next) + { + JIT *jit = cci->get_jit(); + res = jit->get_native_location_for_bc(m, (uint16)bp->location, &np); + if (res == EXE_ERROR_NONE) + break; + } + + if (NULL == np) + return false; + + bp->addr = np; + } + + TRACE2("jvmti.break.intf", "Instrumenting JIT location " << np << + " in method " << m->get_name()->bytes << + " location " << bp->location); + + return set_native_breakpoint(bp); +} + +static bool set_native_breakpoint(VmBrkpt* bp) +{ + assert(bp); + assert(bp->addr); + + TRACE2("jvmti.break.intf", "Instrumenting native location " << + bp->addr); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + + // Look for breakpoint with identical addr + VmBrkpt* same_brpt; + for (same_brpt = vm_brpt->find_breakpoint(bp->addr); same_brpt; + same_brpt = vm_brpt->find_next_breakpoint(same_brpt, bp->addr)); + + if (same_brpt) + { + bp->disasm = new InstructionDisassembler(*same_brpt->disasm); + bp->saved_byte = same_brpt->saved_byte; + return true; + } + + bp->disasm = new InstructionDisassembler(bp->addr); + assert(bp->disasm); + + jbyte* target_instruction = (jbyte*)bp->addr; + bp->saved_byte = *target_instruction; + *target_instruction = (jbyte)INSTRUMENTATION_BYTE; + + return true; +} + +static bool clear_native_breakpoint(VmBrkpt* bp) +{ + assert(bp); + assert(bp->addr); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + + // Look for breakpoint with same addr + if (!vm_brpt->find_breakpoint(bp->addr)) + { + TRACE2("jvmti.break.intf", "Deinstrumenting native location " << bp->addr); + + jbyte* target_instruction = (jbyte*)bp->addr; + *target_instruction = bp->saved_byte; + } + + delete bp->disasm; + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +// Native breakpoints +////////////////////////////////////////////////////////////////////////////// + +static void process_native_breakpoint_event() +{ + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + ti->vm_brpt->process_native_breakpoint(); +} + +bool jvmti_jit_breakpoint_handler(Registers *regs) +{ +#if PLATFORM_POSIX && INSTRUMENTATION_BYTE == INSTRUMENTATION_BYTE_INT3 + // Int3 exception address points to the instruction after it + NativeCodePtr native_location = (NativeCodePtr)(((POINTER_SIZE_INT)regs->get_ip()) - 1); +#else + NativeCodePtr native_location = (NativeCodePtr)regs->get_ip(); +#endif + + TRACE2("jvmti.break", "BREAKPOINT occured: " << native_location); + + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + if (!ti->isEnabled() || ti->getPhase() != JVMTI_PHASE_LIVE) + return false; + + VmBreakpoints* vm_brpt = ti->vm_brpt; + vm_brpt->lock(); + VmBrkpt* bp = vm_brpt->find_breakpoint(native_location); + if (NULL == bp) + { + vm_brpt->unlock(); + return false; + } + + assert(!interpreter_enabled()); + + // Now it is necessary to set up a transition to + // process_native_breakpoint_event from the exception/signal + // handler + VM_thread *vm_thread = p_TLS_vmthread; + // Copy original registers to TLS + vm_thread->jvmti_saved_exception_registers = *regs; +#ifndef _EM64T_ + regs->eip = (POINTER_SIZE_INT)process_native_breakpoint_event; +#else + regs->rip = (POINTER_SIZE_INT)process_native_breakpoint_event; +#endif + + // Breakpoints list lock is not released until it is unlocked + // inside of process_native_breakpoint_event + return true; +} + +// Called when method compilation has completed +void jvmti_set_pending_breakpoints(Method *method) +{ + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + if (!ti->isEnabled()) + return; + + VmBreakpoints* vm_brpt = ti->vm_brpt; + + assert(method->get_pending_breakpoints() > 0); + + LMAutoUnlock lock(vm_brpt->get_lock()); + + jmethodID mid = (jmethodID)method; + VmBrkpt* bp = vm_brpt->find_first(mid); + assert(bp); + + jlocation *locations = (jlocation *)STD_MALLOC(sizeof(jlocation) * + method->get_pending_breakpoints()); + assert(locations); + uint32 location_count = 0; + + do + { + // It is necessary to set breakpoints only once for each + // location, so we need to filter out duplicate breakpoints + for (uint32 iii = 0; iii < location_count; iii++) + if (bp->location == locations[iii]) + continue; + + set_jit_mode_breakpoint(vm_brpt, bp); + locations[location_count++] = bp->location; + + method->remove_pending_breakpoint(); + bp = vm_brpt->find_next(bp, mid); + } + while (NULL != bp); + + assert(method->get_pending_breakpoints() == 0); + STD_FREE(locations); +} + +////////////////////////////////////////////////////////////////////////////// +// Interpreter breakpoints +////////////////////////////////////////////////////////////////////////////// + +VMEXPORT jbyte +jvmti_process_interpreter_breakpoint_event(jmethodID method, jlocation location) +{ + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + if (!ti->isEnabled()) + return false; + + return ti->vm_brpt->process_interpreter_breakpoint(method, location); +} diff --git a/vm/vmcore/src/jvmti/jvmti_event.cpp b/vm/vmcore/src/jvmti/jvmti_event.cpp index f567256..4f4ed36 100644 --- a/vm/vmcore/src/jvmti/jvmti_event.cpp +++ b/vm/vmcore/src/jvmti/jvmti_event.cpp @@ -36,6 +36,7 @@ #include "suspend_checker.h" #include "jit_intf_cpp.h" #include "vm_log.h" #include "compile.h" +#include "jvmti_break_intf.h" /* * Set Event Callbacks @@ -1601,7 +1602,6 @@ void jvmti_send_thread_start_end_event(i assert(JVMTI_ERROR_NONE == errorCode); vm_thread->ss_state->predicted_breakpoints = NULL; - vm_thread->ss_state->predicted_bp_count = 0; } } else @@ -1612,9 +1612,10 @@ void jvmti_send_thread_start_end_event(i { // 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); + LMAutoUnlock lock(ti->vm_brpt->get_lock()); + jvmti_remove_single_step_breakpoints(ti, vm_thread); + ti->vm_brpt->release_intf(vm_thread->ss_state->predicted_breakpoints); _deallocate((unsigned char *)vm_thread->ss_state); vm_thread->ss_state = NULL; } diff --git a/vm/vmcore/src/jvmti/jvmti_general.cpp b/vm/vmcore/src/jvmti/jvmti_general.cpp index 408c32d..324e7ef 100644 --- a/vm/vmcore/src/jvmti/jvmti_general.cpp +++ b/vm/vmcore/src/jvmti/jvmti_general.cpp @@ -27,6 +27,7 @@ #include "jvmti_internal.h" #include "environment.h" #include "cxxlog.h" #include "suspend_checker.h" +#include "jvmti_break_intf.h" /* * Get Phase @@ -106,10 +107,8 @@ jvmtiDisposeEnvironment(jvmtiEnv* env) _deallocate((unsigned char *)threads); } - // Remove all breakpoints set by this environment - ti->brkpntlst_lock._lock(); - ti->remove_all_breakpoints_env(p_env); - ti->brkpntlst_lock._unlock(); + // Remove all breakpoints set by this environment and release interface + ti->vm_brpt->release_intf(p_env->brpt_intf); // Remove all capabilities for this environment jvmtiRelinquishCapabilities(env, &p_env->posessed_capabilities); diff --git a/vm/vmcore/src/jvmti/jvmti_method.cpp b/vm/vmcore/src/jvmti/jvmti_method.cpp index 2a03ed8..4b2d78e 100644 --- a/vm/vmcore/src/jvmti/jvmti_method.cpp +++ b/vm/vmcore/src/jvmti/jvmti_method.cpp @@ -36,6 +36,7 @@ #include "jvmti_internal.h" #include "environment.h" #include "exceptions.h" #include "interpreter_exports.h" +#include "jvmti_break_intf.h" /* * Get Method Name (and Signature) @@ -529,18 +530,18 @@ jvmtiGetBytecodes(jvmtiEnv* env, if (interpreter_enabled()) { TIEnv *p_env = (TIEnv *)env; - DebugUtilsTI *ti = p_env->vm->vm_env->TI; + VmBreakpoints* vm_brpt = p_env->vm->vm_env->TI->vm_brpt; - LMAutoUnlock lock(&ti->brkpntlst_lock); + LMAutoUnlock lock(vm_brpt->get_lock()); - if (!ti->have_breakpoint(method)) + if (!vm_brpt->has_breakpoint(method)) return JVMTI_ERROR_NONE; - for (BreakPoint* bpt = ti->find_first_bpt(method); bpt; - bpt = ti->find_next_bpt(bpt, method)) + for (VmBrkpt* bpt = vm_brpt->find_first(method); bpt; + bpt = vm_brpt->find_next(bpt, method)) { (*bytecodes_ptr)[bpt->location] = - (unsigned char)(POINTER_SIZE_INT) bpt->id; + (unsigned char)bpt->saved_byte; } } diff --git a/vm/vmcore/src/jvmti/jvmti_pop_frame.cpp b/vm/vmcore/src/jvmti/jvmti_pop_frame.cpp index e05dacf..dec8359 100644 --- a/vm/vmcore/src/jvmti/jvmti_pop_frame.cpp +++ b/vm/vmcore/src/jvmti/jvmti_pop_frame.cpp @@ -30,6 +30,7 @@ #include "jit_intf_cpp.h" #include "m2n.h" #include "mon_enter_exit.h" #include "stack_iterator.h" +#include "jvmti_break_intf.h" #include "clog.h" static void jvmti_pop_frame_callback() @@ -328,7 +329,7 @@ jvmti_relocate_single_step_breakpoints( VM_thread *vm_thread = p_TLS_vmthread; if (NULL != vm_thread->ss_state) { // remove old single step breakpoints - LMAutoUnlock lock(&ti->brkpntlst_lock); + LMAutoUnlock lock(ti->vm_brpt->get_lock()); jvmti_remove_single_step_breakpoints(ti, vm_thread); // set new single step breakpoints @@ -342,9 +343,7 @@ jvmti_relocate_single_step_breakpoints( assert(EXE_ERROR_NONE == result); jvmti_StepLocation locations = {method, bc, ip}; - jvmtiError UNREF error = jvmti_set_single_step_breakpoints(ti, vm_thread, - &locations, 1); - assert( error == JVMTI_ERROR_NONE); + jvmti_set_single_step_breakpoints(ti, vm_thread, &locations, 1); } } return; diff --git a/vm/vmcore/src/jvmti/jvmti_step.cpp b/vm/vmcore/src/jvmti/jvmti_step.cpp index 582ac71..d967194 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 "method_lookup.h" #include "open/bytecodes.h" +#include "open/jthread.h" +#include "jvmti_break_intf.h" static JNIEnv * jvmti_test_jenv = jni_native_intf; @@ -379,65 +381,176 @@ 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) +static void +jvmti_setup_jit_single_step(DebugUtilsTI *ti, VmBrkptIntf* intf, + Method* m, jlocation location) { - // Function is always executed under global TI breakpoints lock - BreakPoint **thread_breakpoints; - jvmtiError errorCode = _allocate(sizeof(BreakPoint*) * locations_number, - (unsigned char **)&thread_breakpoints); - if (JVMTI_ERROR_NONE != errorCode) - return errorCode; + VM_thread* vm_thread = p_TLS_vmthread; + jvmti_StepLocation *locations; + unsigned locations_count; - for (unsigned iii = 0; iii < locations_number; iii++) + jvmti_SingleStepLocation(vm_thread, m, (unsigned)location, + &locations, &locations_count); + + jvmti_remove_single_step_breakpoints(ti, vm_thread); + + jvmti_set_single_step_breakpoints(ti, vm_thread, locations, locations_count); +} + +// Callback function for JVMTI single step processing +static bool jvmti_process_jit_single_step_event(VmBrkptIntf* intf, VmBrkptRef* bp_ref) +{ + VmBrkpt* bp = bp_ref->brpt; + assert(bp); + + TRACE2("jvmti.break.ss", "Single Step occured: location=" << bp->location); + + DebugUtilsTI *ti = VM_Global_State::loader_env->TI; + if (!ti->isEnabled() || ti->getPhase() != JVMTI_PHASE_LIVE) + return false; + + JVMTISingleStepState* sss = p_TLS_vmthread->ss_state; + + if (!sss || !ti->is_single_step_enabled()) + return false; + + jlocation location = bp->location; + jmethodID method = bp->method; + Method* m = (Method*)method; + NativeCodePtr addr = bp->addr; + assert(addr); + assert(bp_ref->data == NULL); + + hythread_t h_thread = hythread_self(); + jthread j_thread = jthread_get_java_thread(h_thread); + ObjectHandle hThread = oh_allocate_local_handle(); + hThread->object = (Java_java_lang_Thread *)j_thread->object; + tmn_suspend_enable(); + + JNIEnv *jni_env = (JNIEnv *)jni_native_intf; + TIEnv *env = ti->getEnvironments(); + TIEnv *next_env; + + while (NULL != env) { - BreakPoint *bp; - errorCode = _allocate(sizeof(BreakPoint), (unsigned char **)&bp); - if (JVMTI_ERROR_NONE != errorCode) - return errorCode; + next_env = env->next; - memset( bp, 0, sizeof(BreakPoint)); - bp->method = (jmethodID)locations[iii].method; - bp->location = locations[iii].location; - bp->native_location = locations[iii].native_location; + jvmtiEventSingleStep func = + (jvmtiEventSingleStep)env->get_event_callback(JVMTI_EVENT_SINGLE_STEP); - TRACE2("jvmti.break.ss", "Set single step breakpoint: " - << class_get_name(method_get_class((Method *)bp->method)) << "." - << method_get_name((Method *)bp->method) - << method_get_descriptor((Method *)bp->method) - << " :" << bp->location << " :" << bp->native_location); + if (NULL != func) + { + if (env->global_events[JVMTI_EVENT_SINGLE_STEP - JVMTI_MIN_EVENT_TYPE_VAL]) + { + TRACE2("jvmti.break.ss", + "Calling JIT global SingleStep breakpoint callback: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location << " :" << addr); + // fire global event + intf->unlock(); + func((jvmtiEnv*)env, jni_env, (jthread)hThread, method, location); + intf->lock(); + TRACE2("jvmti.break.ss", + "Finished JIT global SingleStep breakpoint callback: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location << " :" << addr); + + env = next_env; + continue; + } - errorCode = jvmti_set_breakpoint_for_jit(ti, bp); - if (JVMTI_ERROR_NONE != errorCode) - return errorCode; + TIEventThread* next_et; + bool found = false; + // fire local events + for (TIEventThread* et = env->event_threads[JVMTI_EVENT_SINGLE_STEP - JVMTI_MIN_EVENT_TYPE_VAL]; + et != NULL; et = next_et) + { + next_et = et->next; + + if (et->thread == hythread_self()) + { + TRACE2("jvmti.break.ss", + "Calling JIT local SingleStep breakpoint callback: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location << " :" << addr); + found = true; + intf->unlock(); + func((jvmtiEnv*)env, jni_env, + (jthread)hThread, method, location); + intf->lock(); + TRACE2("jvmti.break.ss", + "Finished JIT local SingleStep breakpoint callback: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location << " :" << addr); + } + } - thread_breakpoints[iii] = bp; + env = next_env; + } } - JVMTISingleStepState *ss_state = vm_thread->ss_state; - ss_state->predicted_breakpoints = thread_breakpoints; - ss_state->predicted_bp_count = locations_number; + // Set breakpoints on bytecodes after the current one + if (ti->is_single_step_enabled()) + jvmti_setup_jit_single_step(ti, intf, m, location); - return JVMTI_ERROR_NONE; + tmn_suspend_disable(); + oh_discard_local_handle(hThread); + + return true; } -void jvmti_remove_single_step_breakpoints(DebugUtilsTI *ti, VM_thread *vm_thread) +void 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 + ASSERT_NO_INTERPRETER; + JVMTISingleStepState *ss_state = vm_thread->ss_state; - if(!ss_state->predicted_bp_count) { - // nothing to do - return; + if (NULL == ss_state->predicted_breakpoints) + { // Create SS breakpoints list + ss_state->predicted_breakpoints = + ti->vm_brpt->query_intf(jvmti_process_jit_single_step_event, false); + assert(ss_state->predicted_breakpoints); + // Single Step must be processed earlier then Breakpoints + assert(BREAK_MIN_PRIORITY > BREAK_MAX_PRIORITY); + ss_state->predicted_breakpoints->set_priority(BREAK_MIN_PRIORITY - 1); } - for (unsigned iii = 0; iii < ss_state->predicted_bp_count; iii++) + + for (unsigned iii = 0; iii < locations_number; iii++) { - BreakPoint *bp = ss_state->predicted_breakpoints[iii]; - jvmti_remove_breakpoint_for_jit(ti, bp); + VmBrkptRef* ref = + ss_state->predicted_breakpoints->add((jmethodID)locations[iii].method, + locations[iii].location, + locations[iii].native_location, + NULL); + + TRACE2("jvmti.break.ss", "Set single step breakpoint: " + << class_get_name(method_get_class(locations[iii].method)) << "." + << method_get_name(locations[iii].method) + << method_get_descriptor(locations[iii].method) + << " :" << locations[iii].location + << " :" << locations[iii].native_location); + + assert(ref); } +} - _deallocate((unsigned char *)ss_state->predicted_breakpoints); - ss_state->predicted_bp_count = 0; +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; + + if (ss_state && ss_state->predicted_breakpoints) + ss_state->predicted_breakpoints->remove_all(); } jvmtiError jvmti_get_next_bytecodes_stack_from_native(VM_thread *thread, @@ -499,7 +612,9 @@ jvmtiError jvmti_get_next_bytecodes_stac jvmtiError DebugUtilsTI::jvmti_single_step_start(void) { assert(hythread_is_suspend_enabled()); - LMAutoUnlock lock(&brkpntlst_lock); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + LMAutoUnlock lock(vm_brpt->get_lock()); hythread_iterator_t threads_iterator; @@ -532,7 +647,6 @@ jvmtiError DebugUtilsTI::jvmti_single_st } vm_thread->ss_state->predicted_breakpoints = NULL; - vm_thread->ss_state->predicted_bp_count = 0; jvmti_StepLocation *locations; unsigned locations_number; @@ -546,14 +660,7 @@ jvmtiError DebugUtilsTI::jvmti_single_st 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; - } + jvmti_set_single_step_breakpoints(this, vm_thread, locations, locations_number); } single_step_enabled = true; @@ -568,7 +675,9 @@ jvmtiError DebugUtilsTI::jvmti_single_st jvmtiError DebugUtilsTI::jvmti_single_step_stop(void) { assert(hythread_is_suspend_enabled()); - LMAutoUnlock lock(&brkpntlst_lock); + + VmBreakpoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + LMAutoUnlock lock(vm_brpt->get_lock()); hythread_iterator_t threads_iterator; @@ -588,7 +697,9 @@ jvmtiError DebugUtilsTI::jvmti_single_st // SingleStep state for it continue; } + jvmti_remove_single_step_breakpoints(this, vm_thread); + vm_brpt->release_intf(vm_thread->ss_state->predicted_breakpoints); _deallocate((unsigned char *)vm_thread->ss_state); vm_thread->ss_state = NULL; } diff --git a/vm/vmcore/src/util/linux/signals_ia32.cpp b/vm/vmcore/src/util/linux/signals_ia32.cpp index 0162451..6662d26 100644 --- a/vm/vmcore/src/util/linux/signals_ia32.cpp +++ b/vm/vmcore/src/util/linux/signals_ia32.cpp @@ -71,6 +71,7 @@ #include "exception_filter.h" #include "interpreter.h" #include "crash_handler.h" #include "stack_dump.h" +#include "jvmti_break_intf.h" // Variables used to locate the context from the signal handler static int sc_nest = -1; diff --git a/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp b/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp index ad1fa94..8630ffd 100644 --- a/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp +++ b/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp @@ -25,6 +25,7 @@ #include "exceptions.h" #include "exceptions_jit.h" #include "interpreter_exports.h" #include "stack_dump.h" +#include "jvmti_break_intf.h" // Windows specific #include