Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 68383 invoked from network); 23 Sep 2006 15:02:09 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 23 Sep 2006 15:02:09 -0000 Received: (qmail 82794 invoked by uid 500); 23 Sep 2006 15:02:09 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 82683 invoked by uid 500); 23 Sep 2006 15:02:08 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 82672 invoked by uid 99); 23 Sep 2006 15:02:08 -0000 Received: from idunn.apache.osuosl.org (HELO idunn.apache.osuosl.org) (140.211.166.84) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 23 Sep 2006 08:02:08 -0700 Authentication-Results: idunn.apache.osuosl.org smtp.mail=geirm@apache.org; spf=permerror X-ASF-Spam-Status: No, hits=-9.4 required=5.0 tests=ALL_TRUSTED,NO_REAL_NAME Received-SPF: error (idunn.apache.osuosl.org: domain apache.org from 140.211.166.113 cause and error) Received: from [140.211.166.113] ([140.211.166.113:52772] helo=eris.apache.org) by idunn.apache.osuosl.org (ecelerity 2.1.1.8 r(12930)) with ESMTP id FB/9D-27820-D6C45154 for ; Sat, 23 Sep 2006 08:02:06 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id B8DAF1A981A; Sat, 23 Sep 2006 08:02:03 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r449255 - in /incubator/harmony/enhanced/drlvm/trunk/vm/vmcore: include/jvmti_break_intf.h src/jvmti/jvmti_break_intf.cpp Date: Sat, 23 Sep 2006 15:02:03 -0000 To: harmony-commits@incubator.apache.org From: geirm@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20060923150203.B8DAF1A981A@eris.apache.org> X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: geirm Date: Sat Sep 23 08:02:02 2006 New Revision: 449255 URL: http://svn.apache.org/viewvc?view=rev&rev=449255 Log: additional for HARMONY-1545 missed these when committing... Added: incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/include/jvmti_break_intf.h (with props) incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/src/jvmti/jvmti_break_intf.cpp (with props) Added: incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/include/jvmti_break_intf.h URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/include/jvmti_break_intf.h?view=auto&rev=449255 ============================================================================== --- incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/include/jvmti_break_intf.h (added) +++ incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/include/jvmti_break_intf.h Sat Sep 23 08:02:02 2006 @@ -0,0 +1,214 @@ +/* + * 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" + +#define INSTRUMENTATION_BYTE_HLT 0xf4 // HLT instruction +#define INSTRUMENTATION_BYTE_CLI 0xfa // CLI instruction +#define INSTRUMENTATION_BYTE_INT3 0xcc // INT 3 instruction + +#ifdef PLATFORM_NT +#define INSTRUMENTATION_BYTE INSTRUMENTATION_BYTE_CLI +#else +#define INSTRUMENTATION_BYTE INSTRUMENTATION_BYTE_INT3 +#endif + +// Callbacks are called for interfaces according to its priority +typedef enum { + PRIORITY_SINGLE_STEP_BREAKPOINT = 0, + PRIORITY_SIMPLE_BREAKPOINT, + PRIORITY_NUMBER +} jvmti_BreakPriority; + +class VMBreakInterface; + +struct VMBreakPoint +{ + InstructionDisassembler* disasm; + VMBreakPoint* next; + NativeCodePtr addr; + jlocation location; + jmethodID method; + jbyte saved_byte; + bool is_interp; + bool is_processed; +}; + +// Breakpoint reference +struct VMBreakPointRef +{ + VMBreakPoint* brpt; + void* data; + VMBreakPointRef* next; +}; + +// Pointer to interface callback function +typedef bool (*BPInterfaceCallBack)(VMBreakInterface* intf, VMBreakPointRef* bp_ref); + +class VMBreakPoints +{ +public: + VMBreakPoints() : m_break(NULL) + { + for(unsigned index = 0; index < PRIORITY_NUMBER; index++ ) { + m_intf[index] = NULL; + } + } + + ~VMBreakPoints(); + + void lock() {m_lock._lock();} + void unlock() {m_lock._unlock();} + // Is used for LMAutoUnlock + Lock_Manager* get_lock() {return &m_lock;} + + // Returns interface for breakpoint handling + VMBreakInterface* new_intf(BPInterfaceCallBack callback, + unsigned priority, bool is_interp); + // Destroys interface and deletes all its breakpoints + void release_intf(VMBreakInterface* intf); + + // Checks breakpoint before inserting + bool check_insert_breakpoint(VMBreakPoint* bp); + // Inserts breakpoint into global list and performs instrumentation + bool insert_breakpoint(VMBreakPoint* brpt); + // Removes breakpoint from global list and restores instrumented area + bool remove_breakpoint(VMBreakPoint* brpt); + + // Search operations + VMBreakPoint* find_breakpoint(jmethodID method, jlocation location); + VMBreakPoint* find_breakpoint(NativeCodePtr addr); + VMBreakPoint* find_other_breakpoint_with_same_addr(VMBreakPoint* bp); + VMBreakPoint* find_next_breakpoint(VMBreakPoint* prev, NativeCodePtr addr); + + // Search breakpoints for given method + bool has_breakpoint(jmethodID method); + VMBreakPoint* find_first(jmethodID method); + VMBreakPoint* find_next(VMBreakPoint* prev, jmethodID method); + + // Checks if given breakpoint is set by other interfaces + VMBreakPointRef* find_other_reference(VMBreakInterface* intf, + jmethodID method, jlocation location); + VMBreakPointRef* find_other_reference(VMBreakInterface* intf, + NativeCodePtr addr); + VMBreakPointRef* find_other_reference(VMBreakInterface* intf, + VMBreakPoint* brpt); + + // General callback functions + void process_native_breakpoint(); + jbyte process_interpreter_breakpoint(jmethodID method, jlocation location); + +protected: + void clear_breakpoints_processed_flags(); + VMBreakPoint* get_none_processed_breakpoint(NativeCodePtr addr); + void set_breakpoint_processed( VMBreakPoint *bp, bool flag ) + { + bp->is_processed = flag; + } + bool breakpoint_is_processed( VMBreakPoint *bp ) + { + return bp->is_processed == true; + } + + void clear_intfs_processed_flags(); + VMBreakInterface* get_none_processed_intf(unsigned priority); + +private: + VMBreakInterface* m_intf[PRIORITY_NUMBER]; + VMBreakPoint* m_break; + Lock_Manager m_lock; +}; + +class VMBreakInterface +{ + friend class VMBreakPoints; + +public: + void lock() {VM_Global_State::loader_env->TI->vm_brpt->lock();} + void unlock() {VM_Global_State::loader_env->TI->vm_brpt->unlock();} + Lock_Manager* get_lock() + {return VM_Global_State::loader_env->TI->vm_brpt->get_lock();} + + int get_priority() const { return m_priority; } + + // Iteration + VMBreakPointRef* get_first() { return m_list; } + VMBreakPointRef* get_next(VMBreakPointRef* prev) + { + assert(prev); + return prev->next; + } + + // Basic operations + + // 'data' must be allocated with JVMTI Allocate (or internal _allocate) + // Users must not deallocate 'data', it will be deallocated by 'remove' + VMBreakPointRef* add(jmethodID method, jlocation location, void* data); + // To specify address explicitly + VMBreakPointRef* add(jmethodID method, jlocation location, + NativeCodePtr addr, void* data); + VMBreakPointRef* add(NativeCodePtr addr, void* data); + + void remove_all(); + bool remove(VMBreakPointRef* ref); + + VMBreakPointRef* find(jmethodID method, jlocation location); + VMBreakPointRef* find(NativeCodePtr addr); + VMBreakPointRef* find(VMBreakPoint* brpt); + +protected: + VMBreakInterface(BPInterfaceCallBack callback, unsigned priority, bool is_interp) + : m_list(NULL), m_callback(callback), m_priority(priority), + m_next(NULL), m_processed(false), m_is_interp(is_interp) {} + ~VMBreakInterface() { remove_all(); } + + void set_processed(bool processed) { m_processed = processed; } + bool is_processed() const { return (m_processed == true); } + VMBreakInterface* m_next; + +private: + VMBreakPointRef* m_list; + BPInterfaceCallBack m_callback; + bool m_is_interp; + unsigned m_priority; + bool m_processed; +}; + +// 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__ Propchange: incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/include/jvmti_break_intf.h ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/src/jvmti/jvmti_break_intf.cpp URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/src/jvmti/jvmti_break_intf.cpp?view=auto&rev=449255 ============================================================================== --- incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/src/jvmti/jvmti_break_intf.cpp (added) +++ incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/src/jvmti/jvmti_break_intf.cpp Sat Sep 23 08:02:02 2006 @@ -0,0 +1,1183 @@ +/* + * 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, VMBreakPoint* bp); +static bool set_native_breakpoint(VMBreakPoint* bp); +static bool clear_native_breakpoint(VMBreakPoint* bp); + + +////////////////////////////////////////////////////////////////////////////// +// VMBreakPoints implementation +VMBreakPoints::~VMBreakPoints() +{ + lock(); + for(unsigned index = 0; index < PRIORITY_NUMBER; index++ ) { + while (m_intf[index]) { + VMBreakInterface* intf = m_intf[index]; + m_intf[index] = intf->m_next; + delete intf; + } + } + assert(m_break == NULL); + unlock(); +} + +VMBreakInterface* +VMBreakPoints::new_intf(BPInterfaceCallBack callback, + unsigned priority, + bool is_interp) +{ + assert(callback); + assert(priority < PRIORITY_NUMBER); + VMBreakInterface* intf = new VMBreakInterface(callback, priority, is_interp); + assert(intf); + + lock(); + intf->m_next = m_intf[priority]; + m_intf[priority] = intf; + unlock(); + + return intf; +} + +void +VMBreakPoints::release_intf(VMBreakInterface* intf) +{ + assert(intf); + assert(intf->get_priority() < PRIORITY_NUMBER); + LMAutoUnlock lock(get_lock()); + + for (VMBreakInterface** cur_ptr = &m_intf[intf->get_priority()]; + *cur_ptr; cur_ptr = &((*cur_ptr)->m_next)) + { + if (*cur_ptr == intf) + { + *cur_ptr = (*cur_ptr)->m_next; + + delete intf; + return; + } + } + DIE2("jvmti.break", "VMBreakPoints::release_intf: try to release unknown interface"); +} + +inline bool +VMBreakPoints::check_insert_breakpoint(VMBreakPoint* bp) +{ +#ifndef NDEBUG + if( !bp ) { + return false; + } else if( bp->method ) { + TRACE2("jvmti.break", "Try to insert 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->addr); + + VMBreakPoint* another = find_breakpoint(bp->method, bp->location); + if( !another ) { + return true; + } + + TRACE2("jvmti.break", "Before inserting found another breakpoint: " + << (another->method + ? class_get_name(method_get_class((Method*)another->method)): "(nil)") + << "." + << (another->method ? method_get_name((Method*)another->method) : "(nil)") + << (another->method ? method_get_descriptor((Method*)another->method) : "(nil)") + << " :" << another->location << " :" << another->addr); + + if( bp->addr == another->addr) { + return false; + } + } else if( bp->addr ) { + TRACE2("jvmti.break", "Try to insert breakpoint: native address:" + << bp->addr); + + VMBreakPoint* another = find_breakpoint(bp->addr); + if( !another ) { + return true; + } + + TRACE2("jvmti.break", "Before inserting found another breakpoint: " + << (another->method + ? class_get_name(method_get_class((Method*)another->method)) :"(nil)") + << "." + << (another->method ? method_get_name((Method*)another->method) :"(nil)") + << (another->method ? method_get_descriptor((Method*)another->method) :"(nil)") + << " :" << another->location << " :" << another->addr); + + if(another->method) { + return false; + } + } else { + return false; + } +#endif // !NDEBUG + return true; +} + +bool +VMBreakPoints::insert_breakpoint(VMBreakPoint* brpt) +{ + check_insert_breakpoint(brpt); + 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 " + << class_get_name(method_get_class(m)) << "." + << method_get_name(m) + << method_get_descriptor(m) + << " because it is not compiled yet"); + m->insert_pending_breakpoint(); + } + } + else + { + if (!set_native_breakpoint(brpt)) + return false; + } + } + TRACE2("jvmti.break.intf", "Insert breakpoint: " + << class_get_name(method_get_class((Method*)brpt->method)) << "." + << method_get_name((Method*)brpt->method) + << method_get_descriptor((Method*)brpt->method) + << " :" << brpt->location << " :" << brpt->addr); + + brpt->next = m_break; + m_break = brpt; + + return true; +} + +bool +VMBreakPoints::remove_breakpoint(VMBreakPoint* brpt) +{ + assert(brpt); + assert(!brpt->method || find_breakpoint(brpt->method, brpt->location)); + assert(brpt->method || find_breakpoint(brpt->addr)); + + TRACE2("jvmti.break.intf", "Remove breakpoint: " + << class_get_name(method_get_class((Method*)brpt->method)) << "." + << method_get_name((Method*)brpt->method) + << method_get_descriptor((Method*)brpt->method) + << " :" << brpt->location << " :" << brpt->addr); + + for (VMBreakPoint** cur_ptr = &m_break; + *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; +} + +VMBreakPoint* +VMBreakPoints::find_breakpoint(jmethodID method, jlocation location) +{ + for (VMBreakPoint* brpt = m_break; brpt; brpt = brpt->next) + { + if (brpt->method == method && + brpt->location == location) + return brpt; + } + + return NULL; +} + +VMBreakPoint* +VMBreakPoints::find_breakpoint(NativeCodePtr addr) +{ + assert(addr); + + for (VMBreakPoint* brpt = m_break; brpt; brpt = brpt->next) + { + if (brpt->addr == addr) + return brpt; + } + + return NULL; +} + + +VMBreakPoint* +VMBreakPoints::find_other_breakpoint_with_same_addr(VMBreakPoint* bp) +{ + assert(bp); + + for (VMBreakPoint* other = m_break; other; other = other->next) + { + if (other != bp && other->addr == bp->addr) + return other; + } + + return NULL; +} + +VMBreakPoint* +VMBreakPoints::find_next_breakpoint(VMBreakPoint* prev, NativeCodePtr addr) +{ + assert(addr && prev); + + for (VMBreakPoint* 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); +} + +VMBreakPoint* +VMBreakPoints::find_first(jmethodID method) +{ + assert(method); + + for (VMBreakPoint* brpt = m_break; brpt; brpt = brpt->next) + { + if (brpt->method && + brpt->method == method) + return brpt; + } + + return NULL; +} + +VMBreakPoint* +VMBreakPoints::find_next(VMBreakPoint* prev, jmethodID method) +{ + assert(prev); + + for (VMBreakPoint* brpt = prev->next; brpt; brpt = brpt->next) + { + if (brpt->method == method) + return brpt; + } + + return NULL; +} + +VMBreakPointRef* +VMBreakPoints::find_other_reference(VMBreakInterface* intf, + jmethodID method, + jlocation location) +{ + assert(intf); + + for( unsigned index = 0; index < PRIORITY_NUMBER; index++ ) { + for (VMBreakInterface* cur = m_intf[index]; cur; cur = cur->m_next) { + if (cur == intf) + continue; + + VMBreakPointRef* ref = cur->find(method, location); + + if (ref) + return ref; + } + } + + return NULL; +} + +VMBreakPointRef* +VMBreakPoints::find_other_reference(VMBreakInterface* intf, + NativeCodePtr addr) +{ + assert(intf); + + for( unsigned index = 0; index < PRIORITY_NUMBER; index++ ) { + for (VMBreakInterface* cur = m_intf[index]; cur; cur = cur->m_next) { + if (cur == intf) + continue; + + VMBreakPointRef* ref = cur->find(addr); + + if (ref) + return ref; + } + } + + return NULL; +} + +VMBreakPointRef* +VMBreakPoints::find_other_reference(VMBreakInterface* intf, + VMBreakPoint* brpt) +{ + assert(intf); + + for( unsigned index = 0; index < PRIORITY_NUMBER; index++ ) { + for (VMBreakInterface* cur = m_intf[index]; cur; cur = cur->m_next) { + if (cur == intf) + continue; + + VMBreakPointRef* ref = cur->find(brpt); + + if (ref) + return ref; + } + } + + return NULL; +} + +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 _IA32_ && PLATFORM_POSIX && INSTRUMENTATION_BYTE == INSTRUMENTATION_BYTE_INT3 + // Int3 exception address points to the instruction after it + regs.eip -= 1; +#endif //_IA32_ && PLATFORM_POSIX && INSTRUMENTATION_BYTE == INSTRUMENTATION_BYTE_INT3 + NativeCodePtr addr = (NativeCodePtr)regs.get_ip(); + + TRACE2("jvmti.break.intf", "Native breakpoint occured: " << addr); + + VMBreakPoint* bp = 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); + + for (unsigned priority = 0; priority < PRIORITY_NUMBER; priority++) + { + while( bp = get_none_processed_breakpoint(addr) ) { + set_breakpoint_processed(bp, true); + + VMBreakInterface* intf; + while( intf = get_none_processed_intf(priority) ) + { + intf->set_processed(true); + VMBreakPointRef* 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: " + << class_get_name(method_get_class((Method*)bp->method)) << "." + << method_get_name((Method*)bp->method) + << method_get_descriptor((Method*)bp->method) + << " :" << bp->location << " :" << bp->addr ); + + intf->m_callback(intf, ref); + + TRACE2("jvmti.break.intf", + "Finished native breakpoint callback function: " << addr ); + } + } + clear_intfs_processed_flags(); + } + clear_breakpoints_processed_flags(); + } + + // 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::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; + } + case InstructionDisassembler::INDIRECT_JUMP: + { + jint instruction_length = idisasm.get_length_with_prefix(); + char *jump_target = (char *)idisasm.get_target_address_from_context(®s); + + // Create JMP to the absolute address which conditional jump + // had in the execution buffer + jump((char *)instruction_buffer, jump_target); + break; + } + case InstructionDisassembler::INDIRECT_CALL: + { + jbyte *next_instruction = interrupted_instruction + instruction_length; + char *jump_target = (char *)idisasm.get_target_address_from_context(®s); + 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: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location ); + + assert(interpreter_enabled()); + + lock(); + VMBreakPoint* bp = find_breakpoint(method, location); + assert(bp); + + jbyte orig_byte = bp->saved_byte; + for (unsigned priority = 0; priority < PRIORITY_NUMBER; priority++) + { + VMBreakInterface* intf; + + while (intf = get_none_processed_intf(priority) ) + { + intf->set_processed(true); + VMBreakPointRef* 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: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location ); + + intf->m_callback(intf, ref); + + TRACE2("jvmti.break.intf", + "Finished interpreter breakpoint callback function: " + << class_get_name(method_get_class((Method*)method)) << "." + << method_get_name((Method*)method) + << method_get_descriptor((Method*)method) + << " :" << location ); + } + } + } + + clear_intfs_processed_flags(); + unlock(); + + return orig_byte; +} + +void +VMBreakPoints::clear_breakpoints_processed_flags() +{ + LMAutoUnlock lock(get_lock()); + + for(VMBreakPoint *bp = m_break; bp; bp = bp->next ) { + set_breakpoint_processed( bp, false ); + } +} + +VMBreakPoint* +VMBreakPoints::get_none_processed_breakpoint(NativeCodePtr addr) +{ + LMAutoUnlock lock(get_lock()); + + for(VMBreakPoint *bp = find_breakpoint(addr); + bp; + bp = find_next_breakpoint(bp, addr) ) + { + if(!breakpoint_is_processed(bp)) { + return bp; + } + } + + return NULL; +} + +void +VMBreakPoints::clear_intfs_processed_flags() +{ + LMAutoUnlock lock(get_lock()); + + for(unsigned index = 0; index < PRIORITY_NUMBER; index++ ) { + for (VMBreakInterface* intf = m_intf[index]; intf; intf = intf->m_next) { + intf->set_processed(false); + } + } +} + +VMBreakInterface* +VMBreakPoints::get_none_processed_intf(unsigned priority) +{ + LMAutoUnlock lock(get_lock()); + + for (VMBreakInterface* intf = m_intf[priority]; intf; intf = intf->m_next) { + if (!intf->is_processed()) { + assert(intf->get_priority() == priority); + return intf; + } + } + + return NULL; +} + + +////////////////////////////////////////////////////////////////////////////// +// VMBreakInterface implementation + +VMBreakPointRef* VMBreakInterface::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; + VMBreakPoint* brpt = vm_brpt->find_breakpoint(method, location); + + if (!brpt) + { + brpt = (VMBreakPoint*)STD_MALLOC(sizeof(VMBreakPoint)); + assert(brpt); + + brpt->is_interp = m_is_interp; + brpt->addr = NULL; + brpt->method = method; + brpt->location = location; + brpt->saved_byte = 0; + brpt->disasm = NULL; + brpt->is_processed = false; + + // Insert breakpoint, possibly to the same native address + if (!vm_brpt->insert_breakpoint(brpt)) + { + STD_FREE(brpt); + return false; + } + } + + VMBreakPointRef* brpt_ref = + (VMBreakPointRef*)STD_MALLOC(sizeof(VMBreakPointRef)); + assert(brpt_ref); + + brpt_ref->brpt = brpt; + brpt_ref->data = data; + brpt_ref->next = m_list; + m_list = brpt_ref; + + TRACE2("jvmti.break.intf", "Added ref on breakpoint: " + << class_get_name(method_get_class((Method*)brpt_ref->brpt->method)) << "." + << method_get_name((Method*)brpt_ref->brpt->method) + << method_get_descriptor((Method*)brpt_ref->brpt->method) + << " :" << brpt_ref->brpt->location << " :" << brpt_ref->brpt->addr); + + return brpt_ref; +} + +VMBreakPointRef* VMBreakInterface::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; + VMBreakPoint* brpt = vm_brpt->find_breakpoint(method, location); + + // If breakpoint with the same method location is not found or + // given native address is differ with obtained breapoint. + // The last case cound be if the same method location points + // to different native address. + if ( !brpt || !brpt->addr || brpt->addr != addr ) + { + brpt = (VMBreakPoint*)STD_MALLOC(sizeof(VMBreakPoint)); + assert(brpt); + + brpt->is_interp = m_is_interp; + brpt->addr = addr; + brpt->method = method; + brpt->location = location; + brpt->saved_byte = 0; + brpt->disasm = NULL; + brpt->is_processed = false; + + if (!vm_brpt->insert_breakpoint(brpt)) + { + STD_FREE(brpt); + return false; + } + } + + VMBreakPointRef* brpt_ref = + (VMBreakPointRef*)STD_MALLOC(sizeof(VMBreakPointRef)); + assert(brpt_ref); + + brpt_ref->brpt = brpt; + brpt_ref->data = data; + brpt_ref->next = m_list; + m_list = brpt_ref; + + TRACE2("jvmti.break.intf", "Added ref on breakpoint: " + << class_get_name(method_get_class((Method*)brpt_ref->brpt->method)) << "." + << method_get_name((Method*)brpt_ref->brpt->method) + << method_get_descriptor((Method*)brpt_ref->brpt->method) + << " :" << brpt_ref->brpt->location << " :" << brpt_ref->brpt->addr); + + return brpt_ref; +} + +VMBreakPointRef* VMBreakInterface::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; + VMBreakPoint* brpt = vm_brpt->find_breakpoint(addr); + + if (!brpt) + { + brpt = (VMBreakPoint*)STD_MALLOC(sizeof(VMBreakPoint)); + 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; + brpt->is_processed = false; + + // Insert breakpoint, possibly duplicating breakpoint with method != NULL + if (!vm_brpt->insert_breakpoint(brpt)) + { + STD_FREE(brpt); + return false; + } + } + + VMBreakPointRef* brpt_ref = + (VMBreakPointRef*)STD_MALLOC(sizeof(VMBreakPointRef)); + assert(brpt_ref); + + brpt_ref->brpt = brpt; + brpt_ref->data = data; + brpt_ref->next = m_list; + m_list = brpt_ref; + + TRACE2("jvmti.break.intf", "Added ref on breakpoint: " + << class_get_name(method_get_class((Method*)brpt_ref->brpt->method)) << "." + << method_get_name((Method*)brpt_ref->brpt->method) + << method_get_descriptor((Method*)brpt_ref->brpt->method) + << " :" << brpt_ref->brpt->location << " :" << brpt_ref->brpt->addr); + + return brpt_ref; +} + +bool VMBreakInterface::remove(VMBreakPointRef* ref) +{ + assert(ref); + + TRACE2("jvmti.break.intf", "Remove ref on breakpoint: " + << class_get_name(method_get_class((Method*)ref->brpt->method)) << "." + << method_get_name((Method*)ref->brpt->method) + << method_get_descriptor((Method*)ref->brpt->method) + << " :" << ref->brpt->location << " :" << ref->brpt->addr); + + VMBreakPoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + VMBreakPointRef* found = NULL; + + for (VMBreakPointRef** 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); + + VMBreakPoint* brpt = found->brpt; + assert(brpt); + + if (found->data) + _deallocate((unsigned char*)found->data); + + STD_FREE(found); + + if (vm_brpt->find_other_reference(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; +} + +VMBreakPointRef* VMBreakInterface::find(jmethodID method, jlocation location) +{ + assert(method); + + for (VMBreakPointRef* ref = m_list; ref; ref = ref->next) + { + if (ref->brpt->method && + ref->brpt->method == method && + ref->brpt->location == location) + { + return ref; + } + } + + return NULL; +} + +VMBreakPointRef* VMBreakInterface::find(NativeCodePtr addr) +{ + assert(addr); + + for (VMBreakPointRef* ref = m_list; ref; ref = ref->next) + { + if (ref->brpt->addr == addr) + { + return ref; + } + } + + return NULL; +} + +VMBreakPointRef* VMBreakInterface::find(VMBreakPoint* brpt) +{ + assert(brpt); + + for (VMBreakPointRef* ref = m_list; ref; ref = ref->next) + { + if (ref->brpt == brpt) + { + return ref; + } + } + + return NULL; +} + +void VMBreakInterface::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, VMBreakPoint* 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", "Set JIT 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->addr); + return set_native_breakpoint(bp); +} + +static bool set_native_breakpoint(VMBreakPoint* bp) +{ + assert(bp); + assert(bp->addr); + + TRACE2("jvmti.break.intf", "Instrumenting native: " + << (bp->method ? class_get_name(method_get_class((Method*)bp->method)) : "" ) + << "." + << (bp->method ? method_get_name((Method*)bp->method) : "" ) + << (bp->method ? method_get_descriptor((Method*)bp->method) : "" ) + << " :" << bp->location << " :" << bp->addr); + + VMBreakPoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + + // Look for breakpoint with identical addr + VMBreakPoint* other_bp = vm_brpt->find_other_breakpoint_with_same_addr(bp); + if (other_bp) + { + assert(other_bp->disasm); + bp->disasm = new InstructionDisassembler(*other_bp->disasm); + assert(bp->disasm); + bp->saved_byte = other_bp->saved_byte; + } + else + { + bp->disasm = new InstructionDisassembler(bp->addr); + assert(bp->disasm); + + // instrumening code + jbyte* target_instruction = (jbyte*)bp->addr; + bp->saved_byte = *target_instruction; + *target_instruction = (jbyte)INSTRUMENTATION_BYTE; + } + + return true; +} + +static bool clear_native_breakpoint(VMBreakPoint* bp) +{ + assert(bp); + assert(bp->addr); + + VMBreakPoints* vm_brpt = VM_Global_State::loader_env->TI->vm_brpt; + + // Looking for another breakpoint with the same address, + // currect breakpoint is already removed from breakpoint list. + if (!vm_brpt->find_breakpoint(bp->addr)) + { + TRACE2("jvmti.break.intf", "Deinstrumenting native: " + << (bp->method ? class_get_name(method_get_class((Method*)bp->method)) : "" ) + << "." + << (bp->method ? method_get_name((Method*)bp->method) : "" ) + << (bp->method ? method_get_descriptor((Method*)bp->method) : "" ) + << " :" << bp->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(); + VMBreakPoint* 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; + + if( !method->get_pending_breakpoints() ) + return; + + VMBreakPoints* vm_brpt = ti->vm_brpt; + LMAutoUnlock lock(vm_brpt->get_lock()); + + if( !method->get_pending_breakpoints() ) + return; + + VMBreakPoint* bp = vm_brpt->find_first((jmethodID)method); + 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, (jmethodID)method); + } + while (NULL != bp); + + assert(method->get_pending_breakpoints() == 0); + STD_FREE(locations); + return; +} + +////////////////////////////////////////////////////////////////////////////// +// 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); +} Propchange: incubator/harmony/enhanced/drlvm/trunk/vm/vmcore/src/jvmti/jvmti_break_intf.cpp ------------------------------------------------------------------------------ svn:eol-style = native