8221539: [metaspace] Improve MetaspaceObj::is_metaspace_obj() and friends

Reviewed-by: adinn, coleenp, mdoerr
This commit is contained in:
Thomas Stuefe 2019-03-27 14:13:34 +01:00
parent f65be307bf
commit 1baf5289c5
13 changed files with 222 additions and 28 deletions

View File

@ -559,7 +559,7 @@ bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
// validate constantPoolCache*
ConstantPoolCache* cp = *interpreter_frame_cache_addr();
if (cp == NULL || !cp->is_metaspace_object()) return false;
if (MetaspaceObj::is_valid(cp) == false) return false;
// validate locals

View File

@ -494,7 +494,7 @@ bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
// validate ConstantPoolCache*
ConstantPoolCache* cp = *interpreter_frame_cache_addr();
if (cp == NULL || !cp->is_metaspace_object()) return false;
if (MetaspaceObj::is_valid(cp) == false) return false;
// validate locals

View File

@ -665,7 +665,7 @@ bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
// validate ConstantPoolCache*
ConstantPoolCache* cp = *interpreter_frame_cache_addr();
if (cp == NULL || !cp->is_metaspace_object()) return false;
if (MetaspaceObj::is_valid(cp) == false) return false;
// validate locals

View File

@ -546,7 +546,7 @@ bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
// validate ConstantPoolCache*
ConstantPoolCache* cp = *interpreter_frame_cache_addr();
if (cp == NULL || !cp->is_metaspace_object()) return false;
if (MetaspaceObj::is_valid(cp) == false) return false;
// validate locals

View File

@ -66,7 +66,7 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext,
if (ret_frame.is_interpreted_frame()) {
frame::ijava_state* istate = ret_frame.get_ijava_state();
if (!((Method*)(istate->method))->is_metaspace_object()) {
if (MetaspaceObj::is_valid((Method*)(istate->method)) == false) {
return false;
}
uint64_t reg_bcp = uc->uc_mcontext.regs->gpr[14/*R14_bcp*/];

View File

@ -63,7 +63,8 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext,
if (ret_frame.is_interpreted_frame()) {
frame::z_ijava_state* istate = ret_frame.ijava_state_unchecked();
if ((stack_base() >= (address)istate && (address)istate > stack_end()) || !((Method*)(istate->method))->is_metaspace_object()) {
if ((stack_base() >= (address)istate && (address)istate > stack_end()) ||
MetaspaceObj::is_valid((Method*)(istate->method)) == false) {
return false;
}
uint64_t reg_bcp = uc->uc_mcontext.gregs[13/*Z_BCP*/];

View File

@ -84,8 +84,14 @@ void* MetaspaceObj::operator new(size_t size, ClassLoaderData* loader_data,
return Metaspace::allocate(loader_data, word_size, type, THREAD);
}
bool MetaspaceObj::is_metaspace_object() const {
return Metaspace::contains((void*)this);
bool MetaspaceObj::is_valid(const MetaspaceObj* p) {
// Weed out obvious bogus values first without traversing metaspace
if ((size_t)p < os::min_page_size()) {
return false;
} else if (!is_aligned((address)p, sizeof(MetaWord))) {
return false;
}
return Metaspace::contains((void*)p);
}
void MetaspaceObj::print_address_on(outputStream* st) const {

View File

@ -258,12 +258,19 @@ class MetaspaceObj {
static void* _shared_metaspace_top; // (exclusive) high address
public:
bool is_metaspace_object() const;
bool is_shared() const {
// Returns true if the pointer points to a valid MetaspaceObj. A valid
// MetaspaceObj is MetaWord-aligned and contained within either
// non-shared or shared metaspace.
static bool is_valid(const MetaspaceObj* p);
static bool is_shared(const MetaspaceObj* p) {
// If no shared metaspace regions are mapped, _shared_metaspace_{base,top} will
// both be NULL and all values of p will be rejected quickly.
return (((void*)this) < _shared_metaspace_top && ((void*)this) >= _shared_metaspace_base);
return (((void*)p) < _shared_metaspace_top && ((void*)p) >= _shared_metaspace_base);
}
bool is_shared() const { return MetaspaceObj::is_shared(this); }
void print_address_on(outputStream* st) const; // nonvirtual address printing
static void set_shared_metaspace_range(void* base, void* top) {

View File

@ -92,9 +92,9 @@ void VirtualSpaceList::purge(ChunkManager* chunk_manager) {
assert_lock_strong(MetaspaceExpand_lock);
// Don't use a VirtualSpaceListIterator because this
// list is being changed and a straightforward use of an iterator is not safe.
VirtualSpaceNode* purged_vsl = NULL;
VirtualSpaceNode* prev_vsl = virtual_space_list();
VirtualSpaceNode* next_vsl = prev_vsl;
int num_purged_nodes = 0;
while (next_vsl != NULL) {
VirtualSpaceNode* vsl = next_vsl;
DEBUG_ONLY(vsl->verify(false);)
@ -118,20 +118,17 @@ void VirtualSpaceList::purge(ChunkManager* chunk_manager) {
dec_reserved_words(vsl->reserved_words());
dec_committed_words(vsl->committed_words());
dec_virtual_space_count();
purged_vsl = vsl;
delete vsl;
num_purged_nodes ++;
} else {
prev_vsl = vsl;
}
}
// Verify list
#ifdef ASSERT
if (purged_vsl != NULL) {
// List should be stable enough to use an iterator here.
VirtualSpaceListIterator iter(virtual_space_list());
while (iter.repeat()) {
VirtualSpaceNode* vsl = iter.get_next();
assert(vsl != purged_vsl, "Purge of vsl failed");
}
if (num_purged_nodes > 0) {
verify(false);
}
#endif
}
@ -143,11 +140,13 @@ void VirtualSpaceList::purge(ChunkManager* chunk_manager) {
VirtualSpaceNode* VirtualSpaceList::find_enclosing_space(const void* ptr) {
// List should be stable enough to use an iterator here because removing virtual
// space nodes is only allowed at a safepoint.
VirtualSpaceListIterator iter(virtual_space_list());
while (iter.repeat()) {
VirtualSpaceNode* vsn = iter.get_next();
if (vsn->contains(ptr)) {
return vsn;
if (is_within_envelope((address)ptr)) {
VirtualSpaceListIterator iter(virtual_space_list());
while (iter.repeat()) {
VirtualSpaceNode* vsn = iter.get_next();
if (vsn->contains(ptr)) {
return vsn;
}
}
}
return NULL;
@ -170,7 +169,9 @@ VirtualSpaceList::VirtualSpaceList(size_t word_size) :
_is_class(false),
_reserved_words(0),
_committed_words(0),
_virtual_space_count(0) {
_virtual_space_count(0),
_envelope_lo((address)max_uintx),
_envelope_hi(NULL) {
MutexLockerEx cl(MetaspaceExpand_lock,
Mutex::_no_safepoint_check_flag);
create_new_virtual_space(word_size);
@ -182,12 +183,17 @@ VirtualSpaceList::VirtualSpaceList(ReservedSpace rs) :
_is_class(true),
_reserved_words(0),
_committed_words(0),
_virtual_space_count(0) {
_virtual_space_count(0),
_envelope_lo((address)max_uintx),
_envelope_hi(NULL) {
MutexLockerEx cl(MetaspaceExpand_lock,
Mutex::_no_safepoint_check_flag);
VirtualSpaceNode* class_entry = new VirtualSpaceNode(is_class(), rs);
bool succeeded = class_entry->initialize();
if (succeeded) {
expand_envelope_to_include_node(class_entry);
// ensure lock-free iteration sees fully initialized node
OrderAccess::storestore();
link_vs(class_entry);
}
}
@ -224,12 +230,16 @@ bool VirtualSpaceList::create_new_virtual_space(size_t vs_word_size) {
} else {
assert(new_entry->reserved_words() == vs_word_size,
"Reserved memory size differs from requested memory size");
expand_envelope_to_include_node(new_entry);
// ensure lock-free iteration sees fully initialized node
OrderAccess::storestore();
link_vs(new_entry);
DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_created));
return true;
}
DEBUG_ONLY(verify(false);)
}
void VirtualSpaceList::link_vs(VirtualSpaceNode* new_entry) {
@ -399,5 +409,41 @@ void VirtualSpaceList::print_map(outputStream* st) const {
}
}
// Given a node, expand range such that it includes the node.
void VirtualSpaceList::expand_envelope_to_include_node(const VirtualSpaceNode* node) {
_envelope_lo = MIN2(_envelope_lo, (address)node->low_boundary());
_envelope_hi = MAX2(_envelope_hi, (address)node->high_boundary());
}
#ifdef ASSERT
void VirtualSpaceList::verify(bool slow) {
VirtualSpaceNode* list = virtual_space_list();
VirtualSpaceListIterator iter(list);
size_t reserved = 0;
size_t committed = 0;
size_t node_count = 0;
while (iter.repeat()) {
VirtualSpaceNode* node = iter.get_next();
if (slow) {
node->verify(true);
}
// Check that the node resides fully within our envelope.
assert((address)node->low_boundary() >= _envelope_lo && (address)node->high_boundary() <= _envelope_hi,
"Node " SIZE_FORMAT " [" PTR_FORMAT ", " PTR_FORMAT ") outside envelope [" PTR_FORMAT ", " PTR_FORMAT ").",
node_count, p2i(node->low_boundary()), p2i(node->high_boundary()), p2i(_envelope_lo), p2i(_envelope_hi));
reserved += node->reserved_words();
committed += node->committed_words();
node_count ++;
}
assert(reserved == reserved_words() && committed == committed_words() && node_count == _virtual_space_count,
"Mismatch: reserved real: " SIZE_FORMAT " expected: " SIZE_FORMAT
", committed real: " SIZE_FORMAT " expected: " SIZE_FORMAT
", node count real: " SIZE_FORMAT " expected: " SIZE_FORMAT ".",
reserved, reserved_words(), committed, committed_words(),
node_count, _virtual_space_count);
}
#endif // ASSERT
} // namespace metaspace

View File

@ -58,6 +58,19 @@ class VirtualSpaceList : public CHeapObj<mtClass> {
// Number of virtual spaces
size_t _virtual_space_count;
// Optimization: we keep an address range to quickly exclude pointers
// which are clearly not pointing into metaspace. This is an optimization for
// VirtualSpaceList::contains().
address _envelope_lo;
address _envelope_hi;
bool is_within_envelope(address p) const {
return p >= _envelope_lo && p < _envelope_hi;
}
// Given a node, expand range such that it includes the node.
void expand_envelope_to_include_node(const VirtualSpaceNode* node);
~VirtualSpaceList();
VirtualSpaceNode* virtual_space_list() const { return _virtual_space_list; }
@ -80,6 +93,8 @@ class VirtualSpaceList : public CHeapObj<mtClass> {
// virtual space and add the chunks to the free list.
void retire_current_virtual_space();
DEBUG_ONLY(bool contains_node(const VirtualSpaceNode* node) const;)
public:
VirtualSpaceList(size_t word_size);
VirtualSpaceList(ReservedSpace rs);
@ -126,6 +141,8 @@ class VirtualSpaceList : public CHeapObj<mtClass> {
void print_on(outputStream* st, size_t scale) const;
void print_map(outputStream* st) const;
DEBUG_ONLY(void verify(bool slow);)
class VirtualSpaceListIterator : public StackObj {
VirtualSpaceNode* _virtual_spaces;
public:

View File

@ -60,6 +60,8 @@ class VirtualSpaceNode : public CHeapObj<mtClass> {
// Convenience functions to access the _virtual_space
char* low() const { return virtual_space()->low(); }
char* high() const { return virtual_space()->high(); }
char* low_boundary() const { return virtual_space()->low_boundary(); }
char* high_boundary() const { return virtual_space()->high_boundary(); }
// The first Metachunk will be allocated at the bottom of the
// VirtualSpace

View File

@ -3104,7 +3104,7 @@ static void print_vtable(intptr_t* start, int len, outputStream* st) {
for (int i = 0; i < len; i++) {
intptr_t e = start[i];
st->print("%d : " INTPTR_FORMAT, i, e);
if (e != 0 && ((Metadata*)e)->is_metaspace_object()) {
if (MetaspaceObj::is_valid((Metadata*)e)) {
st->print(" ");
((Metadata*)e)->print_value_on(st);
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2016, 2018 Oracle and/or its affiliates. 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 "memory/allocation.inline.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspace/virtualSpaceList.hpp"
#include "runtime/mutex.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
using namespace metaspace;
// Test the cheerful multitude of metaspace-contains-functions.
class MetaspaceIsMetaspaceObjTest : public ::testing::Test {
Mutex* _lock;
ClassLoaderMetaspace* _ms;
public:
MetaspaceIsMetaspaceObjTest() : _lock(NULL), _ms(NULL) {}
virtual void SetUp() {
}
virtual void TearDown() {
delete _ms;
delete _lock;
}
void do_test(Metaspace::MetadataType mdType) {
_lock = new Mutex(Monitor::native, "gtest-IsMetaspaceObjTest-lock", false, Monitor::_safepoint_check_never);
{
MutexLockerEx ml(_lock, Mutex::_no_safepoint_check_flag);
_ms = new ClassLoaderMetaspace(_lock, Metaspace::StandardMetaspaceType);
}
const MetaspaceObj* p = (MetaspaceObj*) _ms->allocate(42, mdType);
// Test MetaspaceObj::is_metaspace_object
ASSERT_TRUE(MetaspaceObj::is_valid(p));
// A misaligned object shall not be recognized
const MetaspaceObj* p_misaligned = (MetaspaceObj*)((address)p) + 1;
ASSERT_FALSE(MetaspaceObj::is_valid(p_misaligned));
// Test VirtualSpaceList::contains and find_enclosing_space
VirtualSpaceList* list = Metaspace::space_list();
if (mdType == Metaspace::ClassType && Metaspace::using_class_space()) {
list = Metaspace::class_space_list();
}
ASSERT_TRUE(list->contains(p));
VirtualSpaceNode* const n = list->find_enclosing_space(p);
ASSERT_TRUE(n != NULL);
ASSERT_TRUE(n->contains(p));
// A misaligned pointer shall be recognized by list::contains
ASSERT_TRUE(list->contains((address)p) + 1);
// Now for some bogus values
ASSERT_FALSE(MetaspaceObj::is_valid((MetaspaceObj*)NULL));
// Should exercise various paths in MetaspaceObj::is_valid()
ASSERT_FALSE(MetaspaceObj::is_valid((MetaspaceObj*)1024));
ASSERT_FALSE(MetaspaceObj::is_valid((MetaspaceObj*)8192));
MetaspaceObj* p_stack = (MetaspaceObj*) &_lock;
ASSERT_FALSE(MetaspaceObj::is_valid(p_stack));
MetaspaceObj* p_heap = (MetaspaceObj*) os::malloc(41, mtInternal);
ASSERT_FALSE(MetaspaceObj::is_valid(p_heap));
os::free(p_heap);
// Test Metaspace::contains_xxx
ASSERT_TRUE(Metaspace::contains(p));
ASSERT_TRUE(Metaspace::contains_non_shared(p));
delete _ms;
_ms = NULL;
delete _lock;
_lock = NULL;
}
};
TEST_VM_F(MetaspaceIsMetaspaceObjTest, non_class_space) {
do_test(Metaspace::NonClassType);
}
TEST_VM_F(MetaspaceIsMetaspaceObjTest, class_space) {
do_test(Metaspace::ClassType);
}