aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: * aoqi@0: */ aoqi@0: aoqi@0: #ifndef SHARE_VM_RUNTIME_VFRAME_HPP aoqi@0: #define SHARE_VM_RUNTIME_VFRAME_HPP aoqi@0: aoqi@0: #include "code/debugInfo.hpp" aoqi@0: #include "code/debugInfoRec.hpp" aoqi@0: #include "code/location.hpp" aoqi@0: #include "oops/oop.hpp" aoqi@0: #include "runtime/frame.hpp" aoqi@0: #include "runtime/frame.inline.hpp" aoqi@0: #include "runtime/stackValue.hpp" aoqi@0: #include "runtime/stackValueCollection.hpp" aoqi@0: #include "utilities/growableArray.hpp" aoqi@0: aoqi@0: // vframes are virtual stack frames representing source level activations. aoqi@0: // A single frame may hold several source level activations in the case of aoqi@0: // optimized code. The debugging stored with the optimized code enables aoqi@0: // us to unfold a frame as a stack of vframes. aoqi@0: // A cVFrame represents an activation of a non-java method. aoqi@0: aoqi@0: // The vframe inheritance hierarchy: aoqi@0: // - vframe aoqi@0: // - javaVFrame aoqi@0: // - interpretedVFrame aoqi@0: // - compiledVFrame ; (used for both compiled Java methods and native stubs) aoqi@0: // - externalVFrame aoqi@0: // - entryVFrame ; special frame created when calling Java from C aoqi@0: aoqi@0: // - BasicLock aoqi@0: aoqi@0: class vframe: public ResourceObj { aoqi@0: protected: aoqi@0: frame _fr; // Raw frame behind the virtual frame. aoqi@0: RegisterMap _reg_map; // Register map for the raw frame (used to handle callee-saved registers). aoqi@0: JavaThread* _thread; // The thread owning the raw frame. aoqi@0: aoqi@0: vframe(const frame* fr, const RegisterMap* reg_map, JavaThread* thread); aoqi@0: vframe(const frame* fr, JavaThread* thread); aoqi@0: public: aoqi@0: // Factory method for creating vframes aoqi@0: static vframe* new_vframe(const frame* f, const RegisterMap *reg_map, JavaThread* thread); aoqi@0: aoqi@0: // Accessors aoqi@0: frame fr() const { return _fr; } aoqi@0: CodeBlob* cb() const { return _fr.cb(); } aoqi@0: nmethod* nm() const { aoqi@0: assert( cb() != NULL && cb()->is_nmethod(), "usage"); aoqi@0: return (nmethod*) cb(); aoqi@0: } aoqi@0: aoqi@0: // ???? Does this need to be a copy? aoqi@0: frame* frame_pointer() { return &_fr; } aoqi@0: const RegisterMap* register_map() const { return &_reg_map; } aoqi@0: JavaThread* thread() const { return _thread; } aoqi@0: aoqi@0: // Returns the sender vframe aoqi@0: virtual vframe* sender() const; aoqi@0: aoqi@0: // Returns the next javaVFrame on the stack (skipping all other kinds of frame) aoqi@0: javaVFrame *java_sender() const; aoqi@0: aoqi@0: // Answers if the this is the top vframe in the frame, i.e., if the sender vframe aoqi@0: // is in the caller frame aoqi@0: virtual bool is_top() const { return true; } aoqi@0: aoqi@0: // Returns top vframe within same frame (see is_top()) aoqi@0: virtual vframe* top() const; aoqi@0: aoqi@0: // Type testing operations aoqi@0: virtual bool is_entry_frame() const { return false; } aoqi@0: virtual bool is_java_frame() const { return false; } aoqi@0: virtual bool is_interpreted_frame() const { return false; } aoqi@0: virtual bool is_compiled_frame() const { return false; } aoqi@0: aoqi@0: #ifndef PRODUCT aoqi@0: // printing operations aoqi@0: virtual void print_value() const; aoqi@0: virtual void print(); aoqi@0: #endif aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: class javaVFrame: public vframe { aoqi@0: public: aoqi@0: // JVM state aoqi@0: virtual Method* method() const = 0; aoqi@0: virtual int bci() const = 0; aoqi@0: virtual StackValueCollection* locals() const = 0; aoqi@0: virtual StackValueCollection* expressions() const = 0; aoqi@0: // the order returned by monitors() is from oldest -> youngest#4418568 aoqi@0: virtual GrowableArray* monitors() const = 0; aoqi@0: aoqi@0: // Debugging support via JVMTI. aoqi@0: // NOTE that this is not guaranteed to give correct results for compiled vframes. aoqi@0: // Deoptimize first if necessary. aoqi@0: virtual void set_locals(StackValueCollection* values) const = 0; aoqi@0: aoqi@0: // Test operation aoqi@0: bool is_java_frame() const { return true; } aoqi@0: aoqi@0: protected: aoqi@0: javaVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) : vframe(fr, reg_map, thread) {} aoqi@0: javaVFrame(const frame* fr, JavaThread* thread) : vframe(fr, thread) {} aoqi@0: aoqi@0: public: aoqi@0: // casting aoqi@0: static javaVFrame* cast(vframe* vf) { aoqi@0: assert(vf == NULL || vf->is_java_frame(), "must be java frame"); aoqi@0: return (javaVFrame*) vf; aoqi@0: } aoqi@0: aoqi@0: // Return an array of monitors locked by this frame in the youngest to oldest order aoqi@0: GrowableArray* locked_monitors(); aoqi@0: aoqi@0: // printing used during stack dumps aoqi@0: void print_lock_info_on(outputStream* st, int frame_count); aoqi@0: void print_lock_info(int frame_count) { print_lock_info_on(tty, frame_count); } aoqi@0: aoqi@0: #ifndef PRODUCT aoqi@0: public: aoqi@0: // printing operations aoqi@0: void print(); aoqi@0: void print_value() const; aoqi@0: void print_activation(int index) const; aoqi@0: aoqi@0: // verify operations aoqi@0: virtual void verify() const; aoqi@0: aoqi@0: // Structural compare aoqi@0: bool structural_compare(javaVFrame* other); aoqi@0: #endif aoqi@0: friend class vframe; aoqi@0: }; aoqi@0: aoqi@0: class interpretedVFrame: public javaVFrame { aoqi@0: public: aoqi@0: // JVM state aoqi@0: Method* method() const; aoqi@0: int bci() const; aoqi@0: StackValueCollection* locals() const; aoqi@0: StackValueCollection* expressions() const; aoqi@0: GrowableArray* monitors() const; aoqi@0: aoqi@0: void set_locals(StackValueCollection* values) const; aoqi@0: aoqi@0: // Test operation aoqi@0: bool is_interpreted_frame() const { return true; } aoqi@0: aoqi@0: protected: aoqi@0: interpretedVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) : javaVFrame(fr, reg_map, thread) {}; aoqi@0: aoqi@0: public: aoqi@0: // Accessors for Byte Code Pointer aoqi@0: u_char* bcp() const; aoqi@0: void set_bcp(u_char* bcp); aoqi@0: aoqi@0: // casting aoqi@0: static interpretedVFrame* cast(vframe* vf) { aoqi@0: assert(vf == NULL || vf->is_interpreted_frame(), "must be interpreted frame"); aoqi@0: return (interpretedVFrame*) vf; aoqi@0: } aoqi@0: aoqi@0: private: aoqi@0: static const int bcp_offset; aoqi@0: intptr_t* locals_addr_at(int offset) const; mgronlun@7215: StackValueCollection* stack_data(bool expressions) const; aoqi@0: // returns where the parameters starts relative to the frame pointer aoqi@0: int start_of_parameters() const; aoqi@0: aoqi@0: #ifndef PRODUCT aoqi@0: public: aoqi@0: // verify operations aoqi@0: void verify() const; aoqi@0: #endif aoqi@0: friend class vframe; aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: class externalVFrame: public vframe { aoqi@0: protected: aoqi@0: externalVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) : vframe(fr, reg_map, thread) {} aoqi@0: aoqi@0: #ifndef PRODUCT aoqi@0: public: aoqi@0: // printing operations aoqi@0: void print_value() const; aoqi@0: void print(); aoqi@0: #endif aoqi@0: friend class vframe; aoqi@0: }; aoqi@0: aoqi@0: class entryVFrame: public externalVFrame { aoqi@0: public: aoqi@0: bool is_entry_frame() const { return true; } aoqi@0: aoqi@0: protected: aoqi@0: entryVFrame(const frame* fr, const RegisterMap* reg_map, JavaThread* thread); aoqi@0: aoqi@0: public: aoqi@0: // casting aoqi@0: static entryVFrame* cast(vframe* vf) { aoqi@0: assert(vf == NULL || vf->is_entry_frame(), "must be entry frame"); aoqi@0: return (entryVFrame*) vf; aoqi@0: } aoqi@0: aoqi@0: #ifndef PRODUCT aoqi@0: public: aoqi@0: // printing aoqi@0: void print_value() const; aoqi@0: void print(); aoqi@0: #endif aoqi@0: friend class vframe; aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: // A MonitorInfo is a ResourceObject that describes a the pair: aoqi@0: // 1) the owner of the monitor aoqi@0: // 2) the monitor lock aoqi@0: class MonitorInfo : public ResourceObj { aoqi@0: private: aoqi@0: oop _owner; // the object owning the monitor aoqi@0: BasicLock* _lock; aoqi@0: oop _owner_klass; // klass (mirror) if owner was scalar replaced aoqi@0: bool _eliminated; aoqi@0: bool _owner_is_scalar_replaced; aoqi@0: public: aoqi@0: // Constructor aoqi@0: MonitorInfo(oop owner, BasicLock* lock, bool eliminated, bool owner_is_scalar_replaced) { aoqi@0: if (!owner_is_scalar_replaced) { aoqi@0: _owner = owner; aoqi@0: _owner_klass = NULL; aoqi@0: } else { aoqi@0: assert(eliminated, "monitor should be eliminated for scalar replaced object"); aoqi@0: _owner = NULL; aoqi@0: _owner_klass = owner; aoqi@0: } aoqi@0: _lock = lock; aoqi@0: _eliminated = eliminated; aoqi@0: _owner_is_scalar_replaced = owner_is_scalar_replaced; aoqi@0: } aoqi@0: // Accessors aoqi@0: oop owner() const { aoqi@0: assert(!_owner_is_scalar_replaced, "should not be called for scalar replaced object"); aoqi@0: return _owner; aoqi@0: } aoqi@0: oop owner_klass() const { aoqi@0: assert(_owner_is_scalar_replaced, "should not be called for not scalar replaced object"); aoqi@0: return _owner_klass; aoqi@0: } aoqi@0: BasicLock* lock() const { return _lock; } aoqi@0: bool eliminated() const { return _eliminated; } aoqi@0: bool owner_is_scalar_replaced() const { return _owner_is_scalar_replaced; } aoqi@0: }; aoqi@0: aoqi@0: class vframeStreamCommon : StackObj { aoqi@0: protected: aoqi@0: // common aoqi@0: frame _frame; aoqi@0: JavaThread* _thread; aoqi@0: RegisterMap _reg_map; aoqi@0: enum { interpreted_mode, compiled_mode, at_end_mode } _mode; aoqi@0: aoqi@0: int _sender_decode_offset; aoqi@0: aoqi@0: // Cached information aoqi@0: Method* _method; aoqi@0: int _bci; aoqi@0: aoqi@0: // Should VM activations be ignored or not aoqi@0: bool _stop_at_java_call_stub; aoqi@0: aoqi@0: bool fill_in_compiled_inlined_sender(); aoqi@0: void fill_from_compiled_frame(int decode_offset); aoqi@0: void fill_from_compiled_native_frame(); aoqi@0: aoqi@0: void found_bad_method_frame(); aoqi@0: aoqi@0: void fill_from_interpreter_frame(); aoqi@0: bool fill_from_frame(); aoqi@0: aoqi@0: // Helper routine for security_get_caller_frame aoqi@0: void skip_prefixed_method_and_wrappers(); aoqi@0: aoqi@0: public: aoqi@0: // Constructor aoqi@0: vframeStreamCommon(JavaThread* thread) : _reg_map(thread, false) { aoqi@0: _thread = thread; aoqi@0: } aoqi@0: aoqi@0: // Accessors aoqi@0: Method* method() const { return _method; } aoqi@0: int bci() const { return _bci; } aoqi@0: intptr_t* frame_id() const { return _frame.id(); } aoqi@0: address frame_pc() const { return _frame.pc(); } aoqi@0: aoqi@0: CodeBlob* cb() const { return _frame.cb(); } aoqi@0: nmethod* nm() const { aoqi@0: assert( cb() != NULL && cb()->is_nmethod(), "usage"); aoqi@0: return (nmethod*) cb(); aoqi@0: } aoqi@0: aoqi@0: // Frame type aoqi@0: bool is_interpreted_frame() const { return _frame.is_interpreted_frame(); } aoqi@0: bool is_entry_frame() const { return _frame.is_entry_frame(); } aoqi@0: aoqi@0: // Iteration aoqi@0: void next() { aoqi@0: // handle frames with inlining aoqi@0: if (_mode == compiled_mode && fill_in_compiled_inlined_sender()) return; aoqi@0: aoqi@0: // handle general case aoqi@0: do { aoqi@0: _frame = _frame.sender(&_reg_map); aoqi@0: } while (!fill_from_frame()); aoqi@0: } aoqi@0: void security_next(); aoqi@0: aoqi@0: bool at_end() const { return _mode == at_end_mode; } aoqi@0: aoqi@0: // Implements security traversal. Skips depth no. of frame including aoqi@0: // special security frames and prefixed native methods aoqi@0: void security_get_caller_frame(int depth); aoqi@0: aoqi@0: // Helper routine for JVM_LatestUserDefinedLoader -- needed for 1.4 aoqi@0: // reflection implementation aoqi@0: void skip_reflection_related_frames(); aoqi@0: }; aoqi@0: aoqi@0: class vframeStream : public vframeStreamCommon { aoqi@0: public: aoqi@0: // Constructors aoqi@0: vframeStream(JavaThread* thread, bool stop_at_java_call_stub = false) aoqi@0: : vframeStreamCommon(thread) { aoqi@0: _stop_at_java_call_stub = stop_at_java_call_stub; aoqi@0: aoqi@0: if (!thread->has_last_Java_frame()) { aoqi@0: _mode = at_end_mode; aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: _frame = _thread->last_frame(); aoqi@0: while (!fill_from_frame()) { aoqi@0: _frame = _frame.sender(&_reg_map); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // top_frame may not be at safepoint, start with sender aoqi@0: vframeStream(JavaThread* thread, frame top_frame, bool stop_at_java_call_stub = false); aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: inline bool vframeStreamCommon::fill_in_compiled_inlined_sender() { aoqi@0: if (_sender_decode_offset == DebugInformationRecorder::serialized_null) { aoqi@0: return false; aoqi@0: } aoqi@0: fill_from_compiled_frame(_sender_decode_offset); aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: inline void vframeStreamCommon::fill_from_compiled_frame(int decode_offset) { aoqi@0: _mode = compiled_mode; aoqi@0: aoqi@0: // Range check to detect ridiculous offsets. aoqi@0: if (decode_offset == DebugInformationRecorder::serialized_null || aoqi@0: decode_offset < 0 || aoqi@0: decode_offset >= nm()->scopes_data_size()) { aoqi@0: // 6379830 AsyncGetCallTrace sometimes feeds us wild frames. zmajo@7854: // If we read nmethod::scopes_data at serialized_null (== 0) zmajo@7854: // or if read some at other invalid offset, invalid values will be decoded. zmajo@7854: // Based on these values, invalid heap locations could be referenced zmajo@7854: // that could lead to crashes in product mode. zmajo@7854: // Therefore, do not use the decode offset if invalid, but fill the frame zmajo@7854: // as it were a native compiled frame (no Java-level assumptions). aoqi@0: #ifdef ASSERT aoqi@0: if (WizardMode) { aoqi@0: tty->print_cr("Error in fill_from_frame: pc_desc for " aoqi@0: INTPTR_FORMAT " not found or invalid at %d", aoqi@0: p2i(_frame.pc()), decode_offset); aoqi@0: nm()->print(); aoqi@0: nm()->method()->print_codes(); aoqi@0: nm()->print_code(); aoqi@0: nm()->print_pcs(); aoqi@0: } aoqi@0: #endif aoqi@0: // Provide a cheap fallback in product mode. (See comment above.) aoqi@0: found_bad_method_frame(); aoqi@0: fill_from_compiled_native_frame(); aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: // Decode first part of scopeDesc aoqi@0: DebugInfoReadStream buffer(nm(), decode_offset); aoqi@0: _sender_decode_offset = buffer.read_int(); aoqi@0: _method = buffer.read_method(); aoqi@0: _bci = buffer.read_bci(); aoqi@0: aoqi@0: assert(_method->is_method(), "checking type of decoded method"); aoqi@0: } aoqi@0: aoqi@0: // The native frames are handled specially. We do not rely on ScopeDesc info aoqi@0: // since the pc might not be exact due to the _last_native_pc trick. aoqi@0: inline void vframeStreamCommon::fill_from_compiled_native_frame() { aoqi@0: _mode = compiled_mode; aoqi@0: _sender_decode_offset = DebugInformationRecorder::serialized_null; aoqi@0: _method = nm()->method(); aoqi@0: _bci = 0; aoqi@0: } aoqi@0: aoqi@0: inline bool vframeStreamCommon::fill_from_frame() { aoqi@0: // Interpreted frame aoqi@0: if (_frame.is_interpreted_frame()) { aoqi@0: fill_from_interpreter_frame(); aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: // Compiled frame aoqi@0: aoqi@0: if (cb() != NULL && cb()->is_nmethod()) { aoqi@0: if (nm()->is_native_method()) { aoqi@0: // Do not rely on scopeDesc since the pc might be unprecise due to the _last_native_pc trick. aoqi@0: fill_from_compiled_native_frame(); aoqi@0: } else { aoqi@0: PcDesc* pc_desc = nm()->pc_desc_at(_frame.pc()); aoqi@0: int decode_offset; aoqi@0: if (pc_desc == NULL) { aoqi@0: // Should not happen, but let fill_from_compiled_frame handle it. aoqi@0: aoqi@0: // If we are trying to walk the stack of a thread that is not aoqi@0: // at a safepoint (like AsyncGetCallTrace would do) then this is an aoqi@0: // acceptable result. [ This is assuming that safe_for_sender aoqi@0: // is so bullet proof that we can trust the frames it produced. ] aoqi@0: // aoqi@0: // So if we see that the thread is not safepoint safe aoqi@0: // then simply produce the method and a bci of zero aoqi@0: // and skip the possibility of decoding any inlining that aoqi@0: // may be present. That is far better than simply stopping (or aoqi@0: // asserting. If however the thread is safepoint safe this aoqi@0: // is the sign of a compiler bug and we'll let aoqi@0: // fill_from_compiled_frame handle it. aoqi@0: aoqi@0: aoqi@0: JavaThreadState state = _thread->thread_state(); aoqi@0: aoqi@0: // in_Java should be good enough to test safepoint safety aoqi@0: // if state were say in_Java_trans then we'd expect that aoqi@0: // the pc would have already been slightly adjusted to aoqi@0: // one that would produce a pcDesc since the trans state aoqi@0: // would be one that might in fact anticipate a safepoint aoqi@0: aoqi@0: if (state == _thread_in_Java ) { aoqi@0: // This will get a method a zero bci and no inlining. aoqi@0: // Might be nice to have a unique bci to signify this aoqi@0: // particular case but for now zero will do. aoqi@0: aoqi@0: fill_from_compiled_native_frame(); aoqi@0: aoqi@0: // There is something to be said for setting the mode to aoqi@0: // at_end_mode to prevent trying to walk further up the aoqi@0: // stack. There is evidence that if we walk any further aoqi@0: // that we could produce a bad stack chain. However until aoqi@0: // we see evidence that allowing this causes us to find aoqi@0: // frames bad enough to cause segv's or assertion failures aoqi@0: // we don't do it as while we may get a bad call chain the aoqi@0: // probability is much higher (several magnitudes) that we aoqi@0: // get good data. aoqi@0: aoqi@0: return true; aoqi@0: } aoqi@0: decode_offset = DebugInformationRecorder::serialized_null; aoqi@0: } else { aoqi@0: decode_offset = pc_desc->scope_decode_offset(); aoqi@0: } aoqi@0: fill_from_compiled_frame(decode_offset); aoqi@0: } aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: // End of stack? aoqi@0: if (_frame.is_first_frame() || (_stop_at_java_call_stub && _frame.is_entry_frame())) { aoqi@0: _mode = at_end_mode; aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: inline void vframeStreamCommon::fill_from_interpreter_frame() { aoqi@0: Method* method = _frame.interpreter_frame_method(); aoqi@0: intptr_t bcx = _frame.interpreter_frame_bcx(); aoqi@0: int bci = method->validate_bci_from_bcx(bcx); aoqi@0: // 6379830 AsyncGetCallTrace sometimes feeds us wild frames. zmajo@7854: // AsyncGetCallTrace interrupts the VM asynchronously. As a result zmajo@7854: // it is possible to access an interpreter frame for which zmajo@7854: // no Java-level information is yet available (e.g., becasue zmajo@7854: // the frame was being created when the VM interrupted it). zmajo@7854: // In this scenario, pretend that the interpreter is at the point zmajo@7854: // of entering the method. aoqi@0: if (bci < 0) { aoqi@0: found_bad_method_frame(); zmajo@7854: bci = 0; aoqi@0: } aoqi@0: _mode = interpreted_mode; aoqi@0: _method = method; aoqi@0: _bci = bci; aoqi@0: } aoqi@0: aoqi@0: #endif // SHARE_VM_RUNTIME_VFRAME_HPP