duke@435: /* stefank@2314: * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * trims@1907: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA trims@1907: * or visit www.oracle.com if you need additional information or have any trims@1907: * questions. duke@435: * duke@435: */ duke@435: stefank@2314: #include "precompiled.hpp" stefank@2314: #include "classfile/systemDictionary.hpp" stefank@2314: #include "interpreter/interpreter.hpp" stefank@2314: #include "jvmtifiles/jvmtiEnv.hpp" stefank@2314: #include "memory/resourceArea.hpp" stefank@2314: #include "oops/instanceKlass.hpp" stefank@2314: #include "prims/jvmtiAgentThread.hpp" stefank@2314: #include "prims/jvmtiEventController.inline.hpp" stefank@2314: #include "prims/jvmtiImpl.hpp" stefank@2314: #include "prims/jvmtiRedefineClasses.hpp" stefank@2314: #include "runtime/deoptimization.hpp" stefank@2314: #include "runtime/handles.hpp" stefank@2314: #include "runtime/handles.inline.hpp" stefank@2314: #include "runtime/interfaceSupport.hpp" stefank@2314: #include "runtime/javaCalls.hpp" stefank@2314: #include "runtime/signature.hpp" stefank@2314: #include "runtime/vframe.hpp" stefank@2314: #include "runtime/vframe_hp.hpp" stefank@2314: #include "runtime/vm_operations.hpp" stefank@2314: #include "utilities/exceptions.hpp" stefank@2314: #ifdef TARGET_OS_FAMILY_linux stefank@2314: # include "thread_linux.inline.hpp" stefank@2314: #endif stefank@2314: #ifdef TARGET_OS_FAMILY_solaris stefank@2314: # include "thread_solaris.inline.hpp" stefank@2314: #endif stefank@2314: #ifdef TARGET_OS_FAMILY_windows stefank@2314: # include "thread_windows.inline.hpp" stefank@2314: #endif duke@435: duke@435: // duke@435: // class JvmtiAgentThread duke@435: // duke@435: // JavaThread used to wrap a thread started by an agent duke@435: // using the JVMTI method RunAgentThread. duke@435: // duke@435: duke@435: JvmtiAgentThread::JvmtiAgentThread(JvmtiEnv* env, jvmtiStartFunction start_fn, const void *start_arg) duke@435: : JavaThread(start_function_wrapper) { duke@435: _env = env; duke@435: _start_fn = start_fn; duke@435: _start_arg = start_arg; duke@435: } duke@435: duke@435: void duke@435: JvmtiAgentThread::start_function_wrapper(JavaThread *thread, TRAPS) { duke@435: // It is expected that any Agent threads will be created as duke@435: // Java Threads. If this is the case, notification of the creation duke@435: // of the thread is given in JavaThread::thread_main(). duke@435: assert(thread->is_Java_thread(), "debugger thread should be a Java Thread"); duke@435: assert(thread == JavaThread::current(), "sanity check"); duke@435: duke@435: JvmtiAgentThread *dthread = (JvmtiAgentThread *)thread; duke@435: dthread->call_start_function(); duke@435: } duke@435: duke@435: void duke@435: JvmtiAgentThread::call_start_function() { duke@435: ThreadToNativeFromVM transition(this); duke@435: _start_fn(_env->jvmti_external(), jni_environment(), (void*)_start_arg); duke@435: } duke@435: duke@435: duke@435: // duke@435: // class GrowableCache - private methods duke@435: // duke@435: duke@435: void GrowableCache::recache() { duke@435: int len = _elements->length(); duke@435: duke@435: FREE_C_HEAP_ARRAY(address, _cache); duke@435: _cache = NEW_C_HEAP_ARRAY(address,len+1); duke@435: duke@435: for (int i=0; iat(i)->getCacheValue(); duke@435: // duke@435: // The cache entry has gone bad. Without a valid frame pointer duke@435: // value, the entry is useless so we simply delete it in product duke@435: // mode. The call to remove() will rebuild the cache again duke@435: // without the bad entry. duke@435: // duke@435: if (_cache[i] == NULL) { duke@435: assert(false, "cannot recache NULL elements"); duke@435: remove(i); duke@435: return; duke@435: } duke@435: } duke@435: _cache[len] = NULL; duke@435: duke@435: _listener_fun(_this_obj,_cache); duke@435: } duke@435: duke@435: bool GrowableCache::equals(void* v, GrowableElement *e2) { duke@435: GrowableElement *e1 = (GrowableElement *) v; duke@435: assert(e1 != NULL, "e1 != NULL"); duke@435: assert(e2 != NULL, "e2 != NULL"); duke@435: duke@435: return e1->equals(e2); duke@435: } duke@435: duke@435: // duke@435: // class GrowableCache - public methods duke@435: // duke@435: duke@435: GrowableCache::GrowableCache() { duke@435: _this_obj = NULL; duke@435: _listener_fun = NULL; duke@435: _elements = NULL; duke@435: _cache = NULL; duke@435: } duke@435: duke@435: GrowableCache::~GrowableCache() { duke@435: clear(); duke@435: delete _elements; duke@435: FREE_C_HEAP_ARRAY(address, _cache); duke@435: } duke@435: duke@435: void GrowableCache::initialize(void *this_obj, void listener_fun(void *, address*) ) { duke@435: _this_obj = this_obj; duke@435: _listener_fun = listener_fun; duke@435: _elements = new (ResourceObj::C_HEAP) GrowableArray(5,true); duke@435: recache(); duke@435: } duke@435: duke@435: // number of elements in the collection duke@435: int GrowableCache::length() { duke@435: return _elements->length(); duke@435: } duke@435: duke@435: // get the value of the index element in the collection duke@435: GrowableElement* GrowableCache::at(int index) { duke@435: GrowableElement *e = (GrowableElement *) _elements->at(index); duke@435: assert(e != NULL, "e != NULL"); duke@435: return e; duke@435: } duke@435: duke@435: int GrowableCache::find(GrowableElement* e) { duke@435: return _elements->find(e, GrowableCache::equals); duke@435: } duke@435: duke@435: // append a copy of the element to the end of the collection duke@435: void GrowableCache::append(GrowableElement* e) { duke@435: GrowableElement *new_e = e->clone(); duke@435: _elements->append(new_e); duke@435: recache(); duke@435: } duke@435: duke@435: // insert a copy of the element using lessthan() duke@435: void GrowableCache::insert(GrowableElement* e) { duke@435: GrowableElement *new_e = e->clone(); duke@435: _elements->append(new_e); duke@435: duke@435: int n = length()-2; duke@435: for (int i=n; i>=0; i--) { duke@435: GrowableElement *e1 = _elements->at(i); duke@435: GrowableElement *e2 = _elements->at(i+1); duke@435: if (e2->lessThan(e1)) { duke@435: _elements->at_put(i+1, e1); duke@435: _elements->at_put(i, e2); duke@435: } duke@435: } duke@435: duke@435: recache(); duke@435: } duke@435: duke@435: // remove the element at index duke@435: void GrowableCache::remove (int index) { duke@435: GrowableElement *e = _elements->at(index); duke@435: assert(e != NULL, "e != NULL"); duke@435: _elements->remove(e); duke@435: delete e; duke@435: recache(); duke@435: } duke@435: duke@435: // clear out all elements, release all heap space and duke@435: // let our listener know that things have changed. duke@435: void GrowableCache::clear() { duke@435: int len = _elements->length(); duke@435: for (int i=0; iat(i); duke@435: } duke@435: _elements->clear(); duke@435: recache(); duke@435: } duke@435: duke@435: void GrowableCache::oops_do(OopClosure* f) { duke@435: int len = _elements->length(); duke@435: for (int i=0; iat(i); duke@435: e->oops_do(f); duke@435: } duke@435: } duke@435: duke@435: void GrowableCache::gc_epilogue() { duke@435: int len = _elements->length(); duke@435: // recompute the new cache value after GC duke@435: for (int i=0; iat(i)->getCacheValue(); duke@435: } duke@435: } duke@435: duke@435: // duke@435: // class JvmtiBreakpoint duke@435: // duke@435: duke@435: JvmtiBreakpoint::JvmtiBreakpoint() { duke@435: _method = NULL; duke@435: _bci = 0; duke@435: #ifdef CHECK_UNHANDLED_OOPS duke@435: // This one is always allocated with new, but check it just in case. duke@435: Thread *thread = Thread::current(); duke@435: if (thread->is_in_stack((address)&_method)) { duke@435: thread->allow_unhandled_oop((oop*)&_method); duke@435: } duke@435: #endif // CHECK_UNHANDLED_OOPS duke@435: } duke@435: duke@435: JvmtiBreakpoint::JvmtiBreakpoint(methodOop m_method, jlocation location) { duke@435: _method = m_method; duke@435: assert(_method != NULL, "_method != NULL"); duke@435: _bci = (int) location; duke@435: #ifdef CHECK_UNHANDLED_OOPS duke@435: // Could be allocated with new and wouldn't be on the unhandled oop list. duke@435: Thread *thread = Thread::current(); duke@435: if (thread->is_in_stack((address)&_method)) { duke@435: thread->allow_unhandled_oop(&_method); duke@435: } duke@435: #endif // CHECK_UNHANDLED_OOPS duke@435: duke@435: assert(_bci >= 0, "_bci >= 0"); duke@435: } duke@435: duke@435: void JvmtiBreakpoint::copy(JvmtiBreakpoint& bp) { duke@435: _method = bp._method; duke@435: _bci = bp._bci; duke@435: } duke@435: duke@435: bool JvmtiBreakpoint::lessThan(JvmtiBreakpoint& bp) { duke@435: Unimplemented(); duke@435: return false; duke@435: } duke@435: duke@435: bool JvmtiBreakpoint::equals(JvmtiBreakpoint& bp) { duke@435: return _method == bp._method duke@435: && _bci == bp._bci; duke@435: } duke@435: duke@435: bool JvmtiBreakpoint::is_valid() { duke@435: return _method != NULL && duke@435: _bci >= 0; duke@435: } duke@435: duke@435: address JvmtiBreakpoint::getBcp() { duke@435: return _method->bcp_from(_bci); duke@435: } duke@435: duke@435: void JvmtiBreakpoint::each_method_version_do(method_action meth_act) { duke@435: ((methodOopDesc*)_method->*meth_act)(_bci); duke@435: duke@435: // add/remove breakpoint to/from versions of the method that duke@435: // are EMCP. Directly or transitively obsolete methods are duke@435: // not saved in the PreviousVersionInfo. duke@435: Thread *thread = Thread::current(); duke@435: instanceKlassHandle ikh = instanceKlassHandle(thread, _method->method_holder()); duke@435: symbolOop m_name = _method->name(); duke@435: symbolOop m_signature = _method->signature(); duke@435: duke@435: { duke@435: ResourceMark rm(thread); duke@435: // PreviousVersionInfo objects returned via PreviousVersionWalker duke@435: // contain a GrowableArray of handles. We have to clean up the duke@435: // GrowableArray _after_ the PreviousVersionWalker destructor duke@435: // has destroyed the handles. duke@435: { duke@435: // search previous versions if they exist duke@435: PreviousVersionWalker pvw((instanceKlass *)ikh()->klass_part()); duke@435: for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); duke@435: pv_info != NULL; pv_info = pvw.next_previous_version()) { duke@435: GrowableArray* methods = duke@435: pv_info->prev_EMCP_method_handles(); duke@435: duke@435: if (methods == NULL) { duke@435: // We have run into a PreviousVersion generation where duke@435: // all methods were made obsolete during that generation's duke@435: // RedefineClasses() operation. At the time of that duke@435: // operation, all EMCP methods were flushed so we don't duke@435: // have to go back any further. duke@435: // duke@435: // A NULL methods array is different than an empty methods duke@435: // array. We cannot infer any optimizations about older duke@435: // generations from an empty methods array for the current duke@435: // generation. duke@435: break; duke@435: } duke@435: duke@435: for (int i = methods->length() - 1; i >= 0; i--) { duke@435: methodHandle method = methods->at(i); duke@435: if (method->name() == m_name && method->signature() == m_signature) { duke@435: RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)", duke@435: meth_act == &methodOopDesc::set_breakpoint ? "sett" : "clear", duke@435: method->name()->as_C_string(), duke@435: method->signature()->as_C_string())); duke@435: assert(!method->is_obsolete(), "only EMCP methods here"); duke@435: duke@435: ((methodOopDesc*)method()->*meth_act)(_bci); duke@435: break; duke@435: } duke@435: } duke@435: } duke@435: } // pvw is cleaned up duke@435: } // rm is cleaned up duke@435: } duke@435: duke@435: void JvmtiBreakpoint::set() { duke@435: each_method_version_do(&methodOopDesc::set_breakpoint); duke@435: } duke@435: duke@435: void JvmtiBreakpoint::clear() { duke@435: each_method_version_do(&methodOopDesc::clear_breakpoint); duke@435: } duke@435: duke@435: void JvmtiBreakpoint::print() { duke@435: #ifndef PRODUCT duke@435: const char *class_name = (_method == NULL) ? "NULL" : _method->klass_name()->as_C_string(); duke@435: const char *method_name = (_method == NULL) ? "NULL" : _method->name()->as_C_string(); duke@435: duke@435: tty->print("Breakpoint(%s,%s,%d,%p)",class_name, method_name, _bci, getBcp()); duke@435: #endif duke@435: } duke@435: duke@435: duke@435: // duke@435: // class VM_ChangeBreakpoints duke@435: // duke@435: // Modify the Breakpoints data structure at a safepoint duke@435: // duke@435: duke@435: void VM_ChangeBreakpoints::doit() { duke@435: switch (_operation) { duke@435: case SET_BREAKPOINT: duke@435: _breakpoints->set_at_safepoint(*_bp); duke@435: break; duke@435: case CLEAR_BREAKPOINT: duke@435: _breakpoints->clear_at_safepoint(*_bp); duke@435: break; duke@435: case CLEAR_ALL_BREAKPOINT: duke@435: _breakpoints->clearall_at_safepoint(); duke@435: break; duke@435: default: duke@435: assert(false, "Unknown operation"); duke@435: } duke@435: } duke@435: duke@435: void VM_ChangeBreakpoints::oops_do(OopClosure* f) { duke@435: // This operation keeps breakpoints alive duke@435: if (_breakpoints != NULL) { duke@435: _breakpoints->oops_do(f); duke@435: } duke@435: if (_bp != NULL) { duke@435: _bp->oops_do(f); duke@435: } duke@435: } duke@435: duke@435: // duke@435: // class JvmtiBreakpoints duke@435: // duke@435: // a JVMTI internal collection of JvmtiBreakpoint duke@435: // duke@435: duke@435: JvmtiBreakpoints::JvmtiBreakpoints(void listener_fun(void *,address *)) { duke@435: _bps.initialize(this,listener_fun); duke@435: } duke@435: duke@435: JvmtiBreakpoints:: ~JvmtiBreakpoints() {} duke@435: duke@435: void JvmtiBreakpoints::oops_do(OopClosure* f) { duke@435: _bps.oops_do(f); duke@435: } duke@435: duke@435: void JvmtiBreakpoints::gc_epilogue() { duke@435: _bps.gc_epilogue(); duke@435: } duke@435: duke@435: void JvmtiBreakpoints::print() { duke@435: #ifndef PRODUCT duke@435: ResourceMark rm; duke@435: duke@435: int n = _bps.length(); duke@435: for (int i=0; iprint("%d: ", i); duke@435: bp.print(); duke@435: tty->print_cr(""); duke@435: } duke@435: #endif duke@435: } duke@435: duke@435: duke@435: void JvmtiBreakpoints::set_at_safepoint(JvmtiBreakpoint& bp) { duke@435: assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); duke@435: duke@435: int i = _bps.find(bp); duke@435: if (i == -1) { duke@435: _bps.append(bp); duke@435: bp.set(); duke@435: } duke@435: } duke@435: duke@435: void JvmtiBreakpoints::clear_at_safepoint(JvmtiBreakpoint& bp) { duke@435: assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); duke@435: duke@435: int i = _bps.find(bp); duke@435: if (i != -1) { duke@435: _bps.remove(i); duke@435: bp.clear(); duke@435: } duke@435: } duke@435: duke@435: void JvmtiBreakpoints::clearall_at_safepoint() { duke@435: assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); duke@435: duke@435: int len = _bps.length(); duke@435: for (int i=0; imethod_holder() == klass) { duke@435: bp.clear(); duke@435: _bps.remove(i); duke@435: // This changed 'i' so we have to start over. duke@435: changed = true; duke@435: break; duke@435: } duke@435: } duke@435: } duke@435: } duke@435: duke@435: void JvmtiBreakpoints::clearall() { duke@435: VM_ChangeBreakpoints clearall_breakpoint(this,VM_ChangeBreakpoints::CLEAR_ALL_BREAKPOINT); duke@435: VMThread::execute(&clearall_breakpoint); duke@435: } duke@435: duke@435: // duke@435: // class JvmtiCurrentBreakpoints duke@435: // duke@435: duke@435: JvmtiBreakpoints *JvmtiCurrentBreakpoints::_jvmti_breakpoints = NULL; duke@435: address * JvmtiCurrentBreakpoints::_breakpoint_list = NULL; duke@435: duke@435: duke@435: JvmtiBreakpoints& JvmtiCurrentBreakpoints::get_jvmti_breakpoints() { duke@435: if (_jvmti_breakpoints != NULL) return (*_jvmti_breakpoints); duke@435: _jvmti_breakpoints = new JvmtiBreakpoints(listener_fun); duke@435: assert(_jvmti_breakpoints != NULL, "_jvmti_breakpoints != NULL"); duke@435: return (*_jvmti_breakpoints); duke@435: } duke@435: duke@435: void JvmtiCurrentBreakpoints::listener_fun(void *this_obj, address *cache) { duke@435: JvmtiBreakpoints *this_jvmti = (JvmtiBreakpoints *) this_obj; duke@435: assert(this_jvmti != NULL, "this_jvmti != NULL"); duke@435: duke@435: debug_only(int n = this_jvmti->length();); duke@435: assert(cache[n] == NULL, "cache must be NULL terminated"); duke@435: duke@435: set_breakpoint_list(cache); duke@435: } duke@435: duke@435: duke@435: void JvmtiCurrentBreakpoints::oops_do(OopClosure* f) { duke@435: if (_jvmti_breakpoints != NULL) { duke@435: _jvmti_breakpoints->oops_do(f); duke@435: } duke@435: } duke@435: duke@435: void JvmtiCurrentBreakpoints::gc_epilogue() { duke@435: if (_jvmti_breakpoints != NULL) { duke@435: _jvmti_breakpoints->gc_epilogue(); duke@435: } duke@435: } duke@435: duke@435: duke@435: /////////////////////////////////////////////////////////////// duke@435: // duke@435: // class VM_GetOrSetLocal duke@435: // duke@435: duke@435: // Constructor for non-object getter duke@435: VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, int index, BasicType type) duke@435: : _thread(thread) duke@435: , _calling_thread(NULL) duke@435: , _depth(depth) duke@435: , _index(index) duke@435: , _type(type) duke@435: , _set(false) duke@435: , _jvf(NULL) duke@435: , _result(JVMTI_ERROR_NONE) duke@435: { duke@435: } duke@435: duke@435: // Constructor for object or non-object setter duke@435: VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, int index, BasicType type, jvalue value) duke@435: : _thread(thread) duke@435: , _calling_thread(NULL) duke@435: , _depth(depth) duke@435: , _index(index) duke@435: , _type(type) duke@435: , _value(value) duke@435: , _set(true) duke@435: , _jvf(NULL) duke@435: , _result(JVMTI_ERROR_NONE) duke@435: { duke@435: } duke@435: duke@435: // Constructor for object getter duke@435: VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, int index) duke@435: : _thread(thread) duke@435: , _calling_thread(calling_thread) duke@435: , _depth(depth) duke@435: , _index(index) duke@435: , _type(T_OBJECT) duke@435: , _set(false) duke@435: , _jvf(NULL) duke@435: , _result(JVMTI_ERROR_NONE) duke@435: { duke@435: } duke@435: duke@435: vframe *VM_GetOrSetLocal::get_vframe() { duke@435: if (!_thread->has_last_Java_frame()) { duke@435: return NULL; duke@435: } duke@435: RegisterMap reg_map(_thread); duke@435: vframe *vf = _thread->last_java_vframe(®_map); duke@435: int d = 0; duke@435: while ((vf != NULL) && (d < _depth)) { duke@435: vf = vf->java_sender(); duke@435: d++; duke@435: } duke@435: return vf; duke@435: } duke@435: duke@435: javaVFrame *VM_GetOrSetLocal::get_java_vframe() { duke@435: vframe* vf = get_vframe(); duke@435: if (vf == NULL) { duke@435: _result = JVMTI_ERROR_NO_MORE_FRAMES; duke@435: return NULL; duke@435: } duke@435: javaVFrame *jvf = (javaVFrame*)vf; duke@435: kamg@2361: if (!vf->is_java_frame()) { duke@435: _result = JVMTI_ERROR_OPAQUE_FRAME; duke@435: return NULL; duke@435: } duke@435: return jvf; duke@435: } duke@435: duke@435: // Check that the klass is assignable to a type with the given signature. duke@435: // Another solution could be to use the function Klass::is_subtype_of(type). duke@435: // But the type class can be forced to load/initialize eagerly in such a case. duke@435: // This may cause unexpected consequences like CFLH or class-init JVMTI events. duke@435: // It is better to avoid such a behavior. duke@435: bool VM_GetOrSetLocal::is_assignable(const char* ty_sign, Klass* klass, Thread* thread) { duke@435: assert(ty_sign != NULL, "type signature must not be NULL"); duke@435: assert(thread != NULL, "thread must not be NULL"); duke@435: assert(klass != NULL, "klass must not be NULL"); duke@435: duke@435: int len = (int) strlen(ty_sign); duke@435: if (ty_sign[0] == 'L' && ty_sign[len-1] == ';') { // Need pure class/interface name duke@435: ty_sign++; duke@435: len -= 2; duke@435: } duke@435: symbolHandle ty_sym = oopFactory::new_symbol_handle(ty_sign, len, thread); duke@435: if (klass->name() == ty_sym()) { duke@435: return true; duke@435: } duke@435: // Compare primary supers duke@435: int super_depth = klass->super_depth(); duke@435: int idx; duke@435: for (idx = 0; idx < super_depth; idx++) { duke@435: if (Klass::cast(klass->primary_super_of_depth(idx))->name() == ty_sym()) { duke@435: return true; duke@435: } duke@435: } duke@435: // Compare secondary supers duke@435: objArrayOop sec_supers = klass->secondary_supers(); duke@435: for (idx = 0; idx < sec_supers->length(); idx++) { duke@435: if (Klass::cast((klassOop) sec_supers->obj_at(idx))->name() == ty_sym()) { duke@435: return true; duke@435: } duke@435: } duke@435: return false; duke@435: } duke@435: duke@435: // Checks error conditions: duke@435: // JVMTI_ERROR_INVALID_SLOT duke@435: // JVMTI_ERROR_TYPE_MISMATCH duke@435: // Returns: 'true' - everything is Ok, 'false' - error code duke@435: duke@435: bool VM_GetOrSetLocal::check_slot_type(javaVFrame* jvf) { duke@435: methodOop method_oop = jvf->method(); duke@435: if (!method_oop->has_localvariable_table()) { duke@435: // Just to check index boundaries duke@435: jint extra_slot = (_type == T_LONG || _type == T_DOUBLE) ? 1 : 0; duke@435: if (_index < 0 || _index + extra_slot >= method_oop->max_locals()) { duke@435: _result = JVMTI_ERROR_INVALID_SLOT; duke@435: return false; duke@435: } duke@435: return true; duke@435: } duke@435: duke@435: jint num_entries = method_oop->localvariable_table_length(); duke@435: if (num_entries == 0) { duke@435: _result = JVMTI_ERROR_INVALID_SLOT; duke@435: return false; // There are no slots duke@435: } duke@435: int signature_idx = -1; duke@435: int vf_bci = jvf->bci(); duke@435: LocalVariableTableElement* table = method_oop->localvariable_table_start(); duke@435: for (int i = 0; i < num_entries; i++) { duke@435: int start_bci = table[i].start_bci; duke@435: int end_bci = start_bci + table[i].length; duke@435: duke@435: // Here we assume that locations of LVT entries duke@435: // with the same slot number cannot be overlapped duke@435: if (_index == (jint) table[i].slot && start_bci <= vf_bci && vf_bci <= end_bci) { duke@435: signature_idx = (int) table[i].descriptor_cp_index; duke@435: break; duke@435: } duke@435: } duke@435: if (signature_idx == -1) { duke@435: _result = JVMTI_ERROR_INVALID_SLOT; duke@435: return false; // Incorrect slot index duke@435: } duke@435: symbolOop sign_sym = method_oop->constants()->symbol_at(signature_idx); duke@435: const char* signature = (const char *) sign_sym->as_utf8(); duke@435: BasicType slot_type = char2type(signature[0]); duke@435: duke@435: switch (slot_type) { duke@435: case T_BYTE: duke@435: case T_SHORT: duke@435: case T_CHAR: duke@435: case T_BOOLEAN: duke@435: slot_type = T_INT; duke@435: break; duke@435: case T_ARRAY: duke@435: slot_type = T_OBJECT; duke@435: break; duke@435: }; duke@435: if (_type != slot_type) { duke@435: _result = JVMTI_ERROR_TYPE_MISMATCH; duke@435: return false; duke@435: } duke@435: duke@435: jobject jobj = _value.l; duke@435: if (_set && slot_type == T_OBJECT && jobj != NULL) { // NULL reference is allowed duke@435: // Check that the jobject class matches the return type signature. duke@435: JavaThread* cur_thread = JavaThread::current(); duke@435: HandleMark hm(cur_thread); duke@435: duke@435: Handle obj = Handle(cur_thread, JNIHandles::resolve_external_guard(jobj)); duke@435: NULL_CHECK(obj, (_result = JVMTI_ERROR_INVALID_OBJECT, false)); duke@435: KlassHandle ob_kh = KlassHandle(cur_thread, obj->klass()); duke@435: NULL_CHECK(ob_kh, (_result = JVMTI_ERROR_INVALID_OBJECT, false)); duke@435: duke@435: if (!is_assignable(signature, Klass::cast(ob_kh()), cur_thread)) { duke@435: _result = JVMTI_ERROR_TYPE_MISMATCH; duke@435: return false; duke@435: } duke@435: } duke@435: return true; duke@435: } duke@435: duke@435: static bool can_be_deoptimized(vframe* vf) { duke@435: return (vf->is_compiled_frame() && vf->fr().can_be_deoptimized()); duke@435: } duke@435: duke@435: bool VM_GetOrSetLocal::doit_prologue() { duke@435: _jvf = get_java_vframe(); duke@435: NULL_CHECK(_jvf, false); duke@435: kamg@2361: if (_jvf->method()->is_native()) { kamg@2361: if (getting_receiver() && !_jvf->method()->is_static()) { kamg@2361: return true; kamg@2361: } else { kamg@2361: _result = JVMTI_ERROR_OPAQUE_FRAME; kamg@2361: return false; kamg@2361: } kamg@2361: } kamg@2361: duke@435: if (!check_slot_type(_jvf)) { duke@435: return false; duke@435: } duke@435: return true; duke@435: } duke@435: duke@435: void VM_GetOrSetLocal::doit() { duke@435: if (_set) { duke@435: // Force deoptimization of frame if compiled because it's duke@435: // possible the compiler emitted some locals as constant values, duke@435: // meaning they are not mutable. duke@435: if (can_be_deoptimized(_jvf)) { duke@435: duke@435: // Schedule deoptimization so that eventually the local duke@435: // update will be written to an interpreter frame. never@2260: Deoptimization::deoptimize_frame(_jvf->thread(), _jvf->fr().id()); duke@435: duke@435: // Now store a new value for the local which will be applied duke@435: // once deoptimization occurs. Note however that while this duke@435: // write is deferred until deoptimization actually happens duke@435: // can vframe created after this point will have its locals duke@435: // reflecting this update so as far as anyone can see the duke@435: // write has already taken place. duke@435: duke@435: // If we are updating an oop then get the oop from the handle duke@435: // since the handle will be long gone by the time the deopt duke@435: // happens. The oop stored in the deferred local will be duke@435: // gc'd on its own. duke@435: if (_type == T_OBJECT) { duke@435: _value.l = (jobject) (JNIHandles::resolve_external_guard(_value.l)); duke@435: } duke@435: // Re-read the vframe so we can see that it is deoptimized duke@435: // [ Only need because of assert in update_local() ] duke@435: _jvf = get_java_vframe(); duke@435: ((compiledVFrame*)_jvf)->update_local(_type, _index, _value); duke@435: return; duke@435: } duke@435: StackValueCollection *locals = _jvf->locals(); duke@435: HandleMark hm; duke@435: duke@435: switch (_type) { kamg@2361: case T_INT: locals->set_int_at (_index, _value.i); break; kamg@2361: case T_LONG: locals->set_long_at (_index, _value.j); break; kamg@2361: case T_FLOAT: locals->set_float_at (_index, _value.f); break; kamg@2361: case T_DOUBLE: locals->set_double_at(_index, _value.d); break; kamg@2361: case T_OBJECT: { kamg@2361: Handle ob_h(JNIHandles::resolve_external_guard(_value.l)); kamg@2361: locals->set_obj_at (_index, ob_h); kamg@2361: break; kamg@2361: } kamg@2361: default: ShouldNotReachHere(); duke@435: } duke@435: _jvf->set_locals(locals); duke@435: } else { kamg@2361: if (_jvf->method()->is_native() && _jvf->is_compiled_frame()) { kamg@2361: assert(getting_receiver(), "Can only get here when getting receiver"); kamg@2361: oop receiver = _jvf->fr().get_native_receiver(); kamg@2361: _value.l = JNIHandles::make_local(_calling_thread, receiver); kamg@2361: } else { kamg@2361: StackValueCollection *locals = _jvf->locals(); duke@435: kamg@2361: if (locals->at(_index)->type() == T_CONFLICT) { kamg@2361: memset(&_value, 0, sizeof(_value)); kamg@2361: _value.l = NULL; kamg@2361: return; kamg@2361: } duke@435: kamg@2361: switch (_type) { kamg@2361: case T_INT: _value.i = locals->int_at (_index); break; kamg@2361: case T_LONG: _value.j = locals->long_at (_index); break; kamg@2361: case T_FLOAT: _value.f = locals->float_at (_index); break; kamg@2361: case T_DOUBLE: _value.d = locals->double_at(_index); break; kamg@2361: case T_OBJECT: { kamg@2361: // Wrap the oop to be returned in a local JNI handle since kamg@2361: // oops_do() no longer applies after doit() is finished. kamg@2361: oop obj = locals->obj_at(_index)(); kamg@2361: _value.l = JNIHandles::make_local(_calling_thread, obj); kamg@2361: break; kamg@2361: } kamg@2361: default: ShouldNotReachHere(); kamg@2361: } duke@435: } duke@435: } duke@435: } duke@435: duke@435: duke@435: bool VM_GetOrSetLocal::allow_nested_vm_operations() const { duke@435: return true; // May need to deoptimize duke@435: } duke@435: duke@435: kamg@2361: VM_GetReceiver::VM_GetReceiver( kamg@2361: JavaThread* thread, JavaThread* caller_thread, jint depth) kamg@2361: : VM_GetOrSetLocal(thread, caller_thread, depth, 0) {} kamg@2361: duke@435: ///////////////////////////////////////////////////////////////////////////////////////// duke@435: duke@435: // duke@435: // class JvmtiSuspendControl - see comments in jvmtiImpl.hpp duke@435: // duke@435: duke@435: bool JvmtiSuspendControl::suspend(JavaThread *java_thread) { duke@435: // external suspend should have caught suspending a thread twice duke@435: duke@435: // Immediate suspension required for JPDA back-end so JVMTI agent threads do duke@435: // not deadlock due to later suspension on transitions while holding duke@435: // raw monitors. Passing true causes the immediate suspension. duke@435: // java_suspend() will catch threads in the process of exiting duke@435: // and will ignore them. duke@435: java_thread->java_suspend(); duke@435: duke@435: // It would be nice to have the following assertion in all the time, duke@435: // but it is possible for a racing resume request to have resumed duke@435: // this thread right after we suspended it. Temporarily enable this duke@435: // assertion if you are chasing a different kind of bug. duke@435: // duke@435: // assert(java_lang_Thread::thread(java_thread->threadObj()) == NULL || duke@435: // java_thread->is_being_ext_suspended(), "thread is not suspended"); duke@435: duke@435: if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) { duke@435: // check again because we can get delayed in java_suspend(): duke@435: // the thread is in process of exiting. duke@435: return false; duke@435: } duke@435: duke@435: return true; duke@435: } duke@435: duke@435: bool JvmtiSuspendControl::resume(JavaThread *java_thread) { duke@435: // external suspend should have caught resuming a thread twice duke@435: assert(java_thread->is_being_ext_suspended(), "thread should be suspended"); duke@435: duke@435: // resume thread duke@435: { duke@435: // must always grab Threads_lock, see JVM_SuspendThread duke@435: MutexLocker ml(Threads_lock); duke@435: java_thread->java_resume(); duke@435: } duke@435: duke@435: return true; duke@435: } duke@435: duke@435: duke@435: void JvmtiSuspendControl::print() { duke@435: #ifndef PRODUCT duke@435: MutexLocker mu(Threads_lock); duke@435: ResourceMark rm; duke@435: duke@435: tty->print("Suspended Threads: ["); duke@435: for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { duke@435: #if JVMTI_TRACE duke@435: const char *name = JvmtiTrace::safe_get_thread_name(thread); duke@435: #else duke@435: const char *name = ""; duke@435: #endif /*JVMTI_TRACE */ duke@435: tty->print("%s(%c ", name, thread->is_being_ext_suspended() ? 'S' : '_'); duke@435: if (!thread->has_last_Java_frame()) { duke@435: tty->print("no stack"); duke@435: } duke@435: tty->print(") "); duke@435: } duke@435: tty->print_cr("]"); duke@435: #endif duke@435: }