# HG changeset patch # User mdoerr # Date 1536931322 -7200 # Fri Sep 14 15:22:02 2018 +0200 # Node ID 8c30b8bfcc033270210b4a42d7479b707288ff3c # Parent 7bed934d439e608b21da36471b35019b8eed9dc4 8210754: print_location is not reliable enough (printing register info) Reviewed-by: diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -1384,6 +1384,18 @@ } #endif // PRODUCT +bool ClassLoaderDataGraph::is_valid(ClassLoaderData* loader_data) { + if (loader_data == ClassLoaderData::the_null_class_loader_data()) { + return true; + } + for (ClassLoaderData* data = _head; data != NULL; data = data->next()) { + if (loader_data == data) { + return true; + } + } + return false; +} + // Move class loader data from main list to the unloaded list for unloading // and deallocation later. bool ClassLoaderDataGraph::do_unloading(bool do_cleaning) { diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -177,6 +177,9 @@ #ifndef PRODUCT static bool contains_loader_data(ClassLoaderData* loader_data); #endif + + // Check if ClassLoaderData is part of the ClassLoaderDataGraph (not unloaded) + static bool is_valid(ClassLoaderData* loader_data); }; class LockedClassesDo : public KlassClosure { diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -863,6 +863,42 @@ #endif } +// Utils to check if a pointer or range is part of a committed metaspace region. +metaspace::VirtualSpaceNode* MetaspaceUtils::find_enclosing_virtual_space(const void* p) { + VirtualSpaceNode* vsn = Metaspace::space_list()->find_enclosing_space(p); + if (Metaspace::using_class_space() && vsn == NULL) { + vsn = Metaspace::class_space_list()->find_enclosing_space(p); + } + return vsn; +} + +bool MetaspaceUtils::is_in_committed(const void* p) { +#if INCLUDE_CDS + if (UseSharedSpaces) { + for (int idx = MetaspaceShared::ro; idx <= MetaspaceShared::mc; idx++) { + if (FileMapInfo::current_info()->is_in_shared_region(p, idx)) { + return true; + } + } + } +#endif + return find_enclosing_virtual_space(p) != NULL; +} + +bool MetaspaceUtils::is_range_in_committed(const void* from, const void* to) { +#if INCLUDE_CDS + if (UseSharedSpaces) { + for (int idx = MetaspaceShared::ro; idx <= MetaspaceShared::mc; idx++) { + if (FileMapInfo::current_info()->is_in_shared_region(from, idx)) { + return FileMapInfo::current_info()->is_in_shared_region(to, idx); + } + } + } +#endif + VirtualSpaceNode* vsn = find_enclosing_virtual_space(from); + return (vsn != NULL) && vsn->contains(to); +} + // Metaspace methods diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp --- a/src/hotspot/share/memory/metaspace.hpp +++ b/src/hotspot/share/memory/metaspace.hpp @@ -26,6 +26,7 @@ #include "memory/allocation.hpp" #include "memory/memRegion.hpp" +#include "memory/metaspace/virtualSpaceNode.hpp" #include "memory/metaspaceChunkFreeListSummary.hpp" #include "memory/virtualspace.hpp" #include "utilities/exceptions.hpp" @@ -297,6 +298,9 @@ // Spacemanager updates running counters. friend class metaspace::SpaceManager; + // Special access for error reporting (checks without locks). + friend class vmErrorHelper; + // Running counters for statistics concerning in-use chunks. // Note: capacity = used + free + waste + overhead. Note that we do not // count free and waste. Their sum can be deduces from the three other values. @@ -324,6 +328,12 @@ // Helper for print_xx_report. static void print_vs(outputStream* out, size_t scale); + // Utils to check if a pointer or range is part of a committed metaspace region + // without acquiring any locks. + static metaspace::VirtualSpaceNode* find_enclosing_virtual_space(const void* p); + static bool is_in_committed(const void* p); + static bool is_range_in_committed(const void* from, const void* to); + public: // Collect used metaspace statistics. This involves walking the CLDG. The resulting diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp --- a/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp @@ -377,6 +377,17 @@ return next; } +VirtualSpaceNode* VirtualSpaceList::find_enclosing_space(const void* p) { + VirtualSpaceListIterator iter(virtual_space_list()); + while (iter.repeat()) { + VirtualSpaceNode* vsn = iter.get_next(); + if (vsn->contains(p)) { + return vsn; + } + } + return NULL; +} + void VirtualSpaceList::print_on(outputStream* st, size_t scale) const { st->print_cr(SIZE_FORMAT " nodes, current node: " PTR_FORMAT, _virtual_space_count, p2i(_current_virtual_space)); diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp b/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp --- a/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp @@ -121,6 +121,8 @@ // Unlink empty VirtualSpaceNodes and free it. void purge(ChunkManager* chunk_manager); + VirtualSpaceNode* find_enclosing_space(const void* p); + void print_on(outputStream* st) const { print_on(st, K); } void print_on(outputStream* st, size_t scale) const; void print_map(outputStream* st) const; diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -66,6 +66,7 @@ #include "utilities/align.hpp" #include "utilities/defaultStream.hpp" #include "utilities/events.hpp" +#include "utilities/vmErrorHelper.hpp" # include # include @@ -1020,99 +1021,48 @@ st->print_cr("0x0 is NULL"); return; } + + // Check if addr points into a code blob. CodeBlob* b = CodeCache::find_blob_unsafe(addr); if (b != NULL) { - if (b->is_buffer_blob()) { - // the interpreter is generated into a buffer blob - InterpreterCodelet* i = Interpreter::codelet_containing(addr); - if (i != NULL) { - st->print_cr(INTPTR_FORMAT " is at code_begin+%d in an Interpreter codelet", p2i(addr), (int)(addr - i->code_begin())); - i->print_on(st); - return; - } - if (Interpreter::contains(addr)) { - st->print_cr(INTPTR_FORMAT " is pointing into interpreter code" - " (not bytecode specific)", p2i(addr)); - return; - } - // - if (AdapterHandlerLibrary::contains(b)) { - st->print_cr(INTPTR_FORMAT " is at code_begin+%d in an AdapterHandler", p2i(addr), (int)(addr - b->code_begin())); - AdapterHandlerLibrary::print_handler_on(st, b); - } - // the stubroutines are generated into a buffer blob - StubCodeDesc* d = StubCodeDesc::desc_for(addr); - if (d != NULL) { - st->print_cr(INTPTR_FORMAT " is at begin+%d in a stub", p2i(addr), (int)(addr - d->begin())); - d->print_on(st); - st->cr(); - return; - } - if (StubRoutines::contains(addr)) { - st->print_cr(INTPTR_FORMAT " is pointing to an (unnamed) stub routine", p2i(addr)); - return; - } - // the InlineCacheBuffer is using stubs generated into a buffer blob - if (InlineCacheBuffer::contains(addr)) { - st->print_cr(INTPTR_FORMAT " is pointing into InlineCacheBuffer", p2i(addr)); - return; - } - VtableStub* v = VtableStubs::stub_containing(addr); - if (v != NULL) { - st->print_cr(INTPTR_FORMAT " is at entry_point+%d in a vtable stub", p2i(addr), (int)(addr - v->entry_point())); - v->print_on(st); - st->cr(); - return; - } - } - nmethod* nm = b->as_nmethod_or_null(); - if (nm != NULL) { - ResourceMark rm; - st->print(INTPTR_FORMAT " is at entry_point+%d in (nmethod*)" INTPTR_FORMAT, - p2i(addr), (int)(addr - nm->entry_point()), p2i(nm)); - if (verbose) { - st->print(" for "); - nm->method()->print_value_on(st); - } - st->cr(); - nm->print_nmethod(verbose); - return; - } - st->print_cr(INTPTR_FORMAT " is at code_begin+%d in ", p2i(addr), (int)(addr - b->code_begin())); - b->print_on(st); + vmErrorHelper::dump_code_blob(addr, b, st, verbose); return; } + // Check if addr points into Java heap. if (Universe::heap()->is_in(addr)) { - HeapWord* p = Universe::heap()->block_start(addr); - bool print = false; - // If we couldn't find it it just may mean that heap wasn't parsable - // See if we were just given an oop directly - if (p != NULL && Universe::heap()->block_is_obj(p)) { - print = true; - } else if (p == NULL && oopDesc::is_oop(oop(addr))) { - p = (HeapWord*) addr; - print = true; - } - if (print) { - if (p == (HeapWord*) addr) { - st->print_cr(INTPTR_FORMAT " is an oop", p2i(addr)); + oop o = vmErrorHelper::oop_or_null(addr); + if (o != NULL) { + if ((HeapWord*)o == (HeapWord*)addr) { + st->print(INTPTR_FORMAT " is an oop: ", p2i(addr)); } else { - st->print_cr(INTPTR_FORMAT " is pointing into object: " INTPTR_FORMAT, p2i(addr), p2i(p)); + st->print(INTPTR_FORMAT " is pointing into object: " , p2i(addr)); } - oop(p)->print_on(st); + o->print_on(st); return; } - } else { - if (Universe::heap()->is_in_reserved(addr)) { - st->print_cr(INTPTR_FORMAT " is an unallocated location " - "in the heap", p2i(addr)); + } else if (Universe::heap()->is_in_reserved(addr)) { + st->print_cr(INTPTR_FORMAT " is an unallocated location in the heap", p2i(addr)); + return; + } + + // Compressed oop needs to be decoded first. +#ifdef _LP64 + if (UseCompressedOops && ((uintptr_t)addr &~ (uintptr_t)max_juint) == 0) { + narrowOop narrow_oop = (narrowOop)(uintptr_t)addr; + oop o = vmErrorHelper::decode_oop_raw(narrow_oop); + + if (vmErrorHelper::is_valid_oop(o)) { + st->print(UINT32_FORMAT " is a compressed pointer to object: ", narrow_oop); + o->print_on(st); return; } } +#endif bool accessible = is_readable_pointer(addr); + // Check if addr is a JNI handle. if (align_down((intptr_t)addr, sizeof(intptr_t)) != 0 && accessible) { if (JNIHandles::is_global_handle((jobject) addr)) { st->print_cr(INTPTR_FORMAT " is a global jni handle", p2i(addr)); @@ -1131,6 +1081,7 @@ #endif } + // Check if addr belongs to a Java thread. for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { // Check for privilege stack if (thread->privileged_stack_top() != NULL && @@ -1171,6 +1122,20 @@ return; } + // Compressed klass needs to be decoded first. +#ifdef _LP64 + if (UseCompressedClassPointers && ((uintptr_t)addr &~ (uintptr_t)max_juint) == 0) { + narrowKlass narrow_klass = (narrowKlass)(uintptr_t)addr; + Klass* k = vmErrorHelper::decode_klass_raw(narrow_klass); + + if (vmErrorHelper::is_valid_klass(k)) { + st->print_cr(UINT32_FORMAT " is a compressed pointer to class: " INTPTR_FORMAT, narrow_klass, p2i((HeapWord*)k)); + k->print_on(st); + return; + } + } +#endif + // Try an OS specific find if (os::find(addr, st)) { return; diff --git a/src/hotspot/share/utilities/vmErrorHelper.cpp b/src/hotspot/share/utilities/vmErrorHelper.cpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/utilities/vmErrorHelper.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "code/codeCache.hpp" +#include "code/icBuffer.hpp" +#include "code/vtableStubs.hpp" +#include "interpreter/interpreter.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/sharedRuntime.hpp" +#include "utilities/vmErrorHelper.hpp" + + +bool vmErrorHelper::is_readable_range(const void* from, const void* to) { + for (address p = (address) from; p < to; p += 4*K) { + if (!os::is_readable_pointer(p)) { + return false; + } + } + return true; +} + + +bool vmErrorHelper::is_range_in_committed_java_heap(const void* from, const void* to) { + for (uintptr_t i = (uintptr_t)from; i != (uintptr_t)to; ++i) { + if (!Universe::heap()->is_in((const void*) i)) { + return false; + } + } + + return true; +} + +bool vmErrorHelper::is_range_in_committed_c_heap(const void* from, const void* to) { + for (uintptr_t i = (uintptr_t)from; i != (uintptr_t)to; ++i) { + // Should not be in other known regions. + if (Universe::heap()->is_in_reserved((const void*) i) || + CodeCache::contains((void*) i) || + MetaspaceUtils::is_in_committed((const void*) i)) { + return false; + } + } + + // And should be in committed memory. + return is_readable_range(from, to); +} + + +void* vmErrorHelper::load_klass_raw(oop obj) { + if (UseCompressedClassPointers) { + narrowKlass narrow_klass = *(obj->compressed_klass_addr()); + if (narrow_klass == 0) return NULL; + return (void*)decode_klass_raw(narrow_klass); + } else { + return *(void**)(obj->klass_addr()); + } +} + +void* vmErrorHelper::load_oop_raw(oop obj, int offset) { + uintptr_t addr = (uintptr_t)(void*)obj + (uint)offset; + if (UseCompressedOops) { + narrowOop narrow_oop = *(narrowOop*)addr; + if (narrow_oop == 0) return NULL; + return (void*)decode_oop_raw(narrow_oop); + } else { + return *(void**)addr; + } +} + + +bool vmErrorHelper::is_valid_symbol(Symbol* s) { + if (s == NULL) { + return false; + } + + if (!is_ptr_aligned((intptr_t) s)) { + return false; + } + + if (!is_range_in_committed_c_heap(s, s + 1)) { + return false; + } + + int len = s->utf8_length(); + + if (len < 0) { + return false; + } + + jbyte* bytes = (jbyte*) s->bytes(); + + return is_readable_range(bytes, bytes + len); +} + +bool vmErrorHelper::is_valid_klass(Klass* k) { + if (!is_ptr_aligned((intptr_t) k)) { + return false; + } + + if (!MetaspaceUtils::is_range_in_committed(k, k + 1)) { + return false; + } + + if (!is_valid_symbol(k->name())) { + return false; + } + + return ClassLoaderDataGraph::is_valid(k->class_loader_data()); +} + +bool vmErrorHelper::is_valid_oop(oop obj) { + if (!is_oop_aligned(obj)) { + return false; + } + + // We need at least the mark and the klass word in the committed region. + if (!is_range_in_committed_java_heap((oopDesc*) obj, (oopDesc*) obj + 1)) { + return false; + } + + Klass* k = (Klass*)load_klass_raw(obj); + + if (!is_ptr_aligned((intptr_t) k)) { + return false; + } + + return MetaspaceUtils::is_range_in_committed(k, k + 1); +} + +oop vmErrorHelper::oop_or_null(address addr) { + HeapWord* p = Universe::heap()->block_start(addr); + // If we couldn't find it it just may mean that heap wasn't parsable + // See if we were just given an oop directly + if (p != NULL && Universe::heap()->block_is_obj(p)) { + return oop(p); + } else if (p == NULL && is_valid_oop(oop(addr))) { + return oop(addr); + } + return NULL; +} + + + +void vmErrorHelper::dump_code_blob(address addr, CodeBlob* b, outputStream* st, bool verbose) { + if (b->is_buffer_blob()) { + // the interpreter is generated into a buffer blob + InterpreterCodelet* i = Interpreter::codelet_containing(addr); + if (i != NULL) { + st->print_cr(INTPTR_FORMAT " is at code_begin+%d in an Interpreter codelet", p2i(addr), (int)(addr - i->code_begin())); + i->print_on(st); + return; + } + if (Interpreter::contains(addr)) { + st->print_cr(INTPTR_FORMAT " is pointing into interpreter code" + " (not bytecode specific)", p2i(addr)); + return; + } + // + if (AdapterHandlerLibrary::contains(b)) { + st->print_cr(INTPTR_FORMAT " is at code_begin+%d in an AdapterHandler", p2i(addr), (int)(addr - b->code_begin())); + AdapterHandlerLibrary::print_handler_on(st, b); + } + // the stubroutines are generated into a buffer blob + StubCodeDesc* d = StubCodeDesc::desc_for(addr); + if (d != NULL) { + st->print_cr(INTPTR_FORMAT " is at begin+%d in a stub", p2i(addr), (int)(addr - d->begin())); + d->print_on(st); + st->cr(); + return; + } + if (StubRoutines::contains(addr)) { + st->print_cr(INTPTR_FORMAT " is pointing to an (unnamed) stub routine", p2i(addr)); + return; + } + // the InlineCacheBuffer is using stubs generated into a buffer blob + if (InlineCacheBuffer::contains(addr)) { + st->print_cr(INTPTR_FORMAT " is pointing into InlineCacheBuffer", p2i(addr)); + return; + } + VtableStub* v = VtableStubs::stub_containing(addr); + if (v != NULL) { + st->print_cr(INTPTR_FORMAT " is at entry_point+%d in a vtable stub", p2i(addr), (int)(addr - v->entry_point())); + v->print_on(st); + st->cr(); + return; + } + } + nmethod* nm = b->as_nmethod_or_null(); + if (nm != NULL) { + ResourceMark rm; + st->print(INTPTR_FORMAT " is at entry_point+%d in (nmethod*)" INTPTR_FORMAT, + p2i(addr), (int)(addr - nm->entry_point()), p2i(nm)); + if (verbose) { + st->print(" for "); + nm->method()->print_value_on(st); + } + st->cr(); + nm->print_nmethod(verbose); + return; + } + st->print_cr(INTPTR_FORMAT " is at code_begin+%d in ", p2i(addr), (int)(addr - b->code_begin())); + b->print_on(st); +} diff --git a/src/hotspot/share/utilities/vmErrorHelper.hpp b/src/hotspot/share/utilities/vmErrorHelper.hpp new file mode 100644 --- /dev/null +++ b/src/hotspot/share/utilities/vmErrorHelper.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +/* + * This class provides tools to analyze the JVM's data structures carefully, + * especially for the purpose of creating detailed error reports + * (when VMError::is_error_reported()). + * Attention: They don't acquire locks! Use with care! + */ + +class vmErrorHelper { + static bool is_oop_aligned(oop obj) { return is_object_aligned(obj); } + static bool is_ptr_aligned(intptr_t ptr) { return is_aligned(ptr, sizeof(void*)); } + + static bool is_range_in_committed_java_heap(const void* from, const void* to); + static bool is_range_in_committed_c_heap(const void* from, const void* to); + + static void* load_klass_raw(oop obj); + static void* load_oop_raw(oop obj, int offset); + + public: + static oop decode_oop_raw(narrowOop narrow_oop) { + return (oop)(void*)( (uintptr_t)Universe::narrow_oop_base() + + ((uintptr_t)narrow_oop << Universe::narrow_oop_shift())); + } + static Klass* decode_klass_raw(narrowKlass narrow_klass) { + return (Klass*)(void*)( (uintptr_t)Universe::narrow_klass_base() + + ((uintptr_t)narrow_klass << Universe::narrow_klass_shift())); + } + + static bool is_readable_range(const void* from, const void* to); + + static bool is_valid_symbol(Symbol* s); + static bool is_valid_klass(Klass* k); + static bool is_valid_oop(oop obj); + static oop oop_or_null(address addr); + + static void dump_code_blob(address addr, CodeBlob* cb, outputStream* st, bool verbose); +};