8220366: Optimize Symbol handling in ClassVerifier and SignatureStream

Reviewed-by: hseigel, coleenp, lfoltan
This commit is contained in:
Claes Redestad 2019-03-14 18:56:25 +01:00
parent 9635954e6e
commit 4bfd3db2e0
8 changed files with 132 additions and 80 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -103,13 +103,16 @@ VerificationType StackMapFrame::set_locals_from_arg(
case T_ARRAY:
{
Symbol* sig = ss.as_symbol(CHECK_(VerificationType::bogus_type()));
// Create another symbol to save as signature stream unreferences
// this symbol.
Symbol* sig_copy =
verifier()->create_temporary_symbol(sig, 0, sig->utf8_length(),
CHECK_(VerificationType::bogus_type()));
assert(sig_copy == sig, "symbols don't match");
return VerificationType::reference_type(sig_copy);
if (!sig->is_permanent()) {
// Create another symbol to save as signature stream unreferences
// this symbol.
Symbol *sig_copy =
verifier()->create_temporary_symbol(sig, 0, sig->utf8_length(),
CHECK_(VerificationType::bogus_type()));
assert(sig_copy == sig, "symbols don't match");
sig = sig_copy;
}
return VerificationType::reference_type(sig);
}
case T_INT: return VerificationType::integer_type();
case T_BYTE: return VerificationType::byte_type();

View File

@ -487,8 +487,8 @@ Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) {
if (sym == NULL) {
sym = SymbolTable::the_table()->do_add_if_needed(name, len, hash, false, CHECK_NULL);
}
if (sym->refcount() != PERM_REFCOUNT) {
sym->increment_refcount();
if (!sym->is_permanent()) {
sym->make_permanent();
log_trace_symboltable_helper(sym, "Asked for a permanent symbol, but got a regular one");
}
return sym;

View File

@ -164,17 +164,15 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
// If the class should be verified, first see if we can use the split
// verifier. If not, or if verification fails and FailOverToOldVerifier
// is set, then call the inference verifier.
Symbol* exception_name = NULL;
const size_t message_buffer_len = klass->name()->utf8_length() + 1024;
char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
char* exception_message = message_buffer;
char* message_buffer = NULL;
char* exception_message = NULL;
const char* klassName = klass->external_name();
bool can_failover = FailOverToOldVerifier &&
klass->major_version() < NOFAILOVER_MAJOR_VERSION;
log_info(class, init)("Start class verification for: %s", klassName);
log_info(class, init)("Start class verification for: %s", klass->external_name());
if (klass->major_version() >= STACKMAP_ATTRIBUTE_MAJOR_VERSION) {
ClassVerifier split_verifier(klass, THREAD);
split_verifier.verify_class(THREAD);
@ -182,8 +180,10 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
if (can_failover && !HAS_PENDING_EXCEPTION &&
(exception_name == vmSymbols::java_lang_VerifyError() ||
exception_name == vmSymbols::java_lang_ClassFormatError())) {
log_info(verification)("Fail over class verification to old verifier for: %s", klassName);
log_info(class, init)("Fail over class verification to old verifier for: %s", klassName);
log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name());
log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name());
message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
exception_message = message_buffer;
exception_name = inference_verify(
klass, message_buffer, message_buffer_len, THREAD);
}
@ -191,6 +191,8 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
exception_message = split_verifier.exception_message();
}
} else {
message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
exception_message = message_buffer;
exception_name = inference_verify(
klass, message_buffer, message_buffer_len, THREAD);
}
@ -198,20 +200,19 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
LogTarget(Info, class, init) lt1;
if (lt1.is_enabled()) {
LogStream ls(lt1);
log_end_verification(&ls, klassName, exception_name, THREAD);
log_end_verification(&ls, klass->external_name(), exception_name, THREAD);
}
LogTarget(Info, verification) lt2;
if (lt2.is_enabled()) {
LogStream ls(lt2);
log_end_verification(&ls, klassName, exception_name, THREAD);
log_end_verification(&ls, klass->external_name(), exception_name, THREAD);
}
if (HAS_PENDING_EXCEPTION) {
return false; // use the existing exception
} else if (exception_name == NULL) {
return true; // verifcation succeeded
return true; // verification succeeded
} else { // VerifyError or ClassFormatError to be created and thrown
ResourceMark rm(THREAD);
Klass* kls =
SystemDictionary::resolve_or_fail(exception_name, true, CHECK_false);
if (log_is_enabled(Debug, class, resolve)) {
@ -228,7 +229,10 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
}
kls = kls->super();
}
message_buffer[message_buffer_len - 1] = '\0'; // just to be sure
if (message_buffer != NULL) {
message_buffer[message_buffer_len - 1] = '\0'; // just to be sure
}
assert(exception_message != NULL, "");
THROW_MSG_(exception_name, exception_message, false);
}
}
@ -569,17 +573,18 @@ void ErrorContext::stackmap_details(outputStream* ss, const Method* method) cons
ClassVerifier::ClassVerifier(
InstanceKlass* klass, TRAPS)
: _thread(THREAD), _exception_type(NULL), _message(NULL), _klass(klass) {
: _thread(THREAD), _previous_symbol(NULL), _symbols(NULL), _exception_type(NULL),
_message(NULL), _klass(klass) {
_this_type = VerificationType::reference_type(klass->name());
// Create list to hold symbols in reference area.
_symbols = new GrowableArray<Symbol*>(100, 0, NULL);
}
ClassVerifier::~ClassVerifier() {
// Decrement the reference count for any symbols created.
for (int i = 0; i < _symbols->length(); i++) {
Symbol* s = _symbols->at(i);
s->decrement_refcount();
if (_symbols != NULL) {
for (int i = 0; i < _symbols->length(); i++) {
Symbol* s = _symbols->at(i);
s->decrement_refcount();
}
}
}
@ -3092,13 +3097,23 @@ void ClassVerifier::verify_return_value(
// they can be reference counted.
Symbol* ClassVerifier::create_temporary_symbol(const Symbol *s, int begin,
int end, TRAPS) {
Symbol* sym = SymbolTable::new_symbol(s, begin, end, CHECK_NULL);
_symbols->push(sym);
return sym;
const char* name = (const char*)s->base() + begin;
int length = end - begin;
return create_temporary_symbol(name, length, CHECK_NULL);
}
Symbol* ClassVerifier::create_temporary_symbol(const char *s, int length, TRAPS) {
Symbol* sym = SymbolTable::new_symbol(s, length, CHECK_NULL);
_symbols->push(sym);
Symbol* ClassVerifier::create_temporary_symbol(const char *name, int length, TRAPS) {
// Quick deduplication check
if (_previous_symbol != NULL && _previous_symbol->equals(name, length)) {
return _previous_symbol;
}
Symbol* sym = SymbolTable::new_symbol(name, length, CHECK_NULL);
if (!sym->is_permanent()) {
if (_symbols == NULL) {
_symbols = new GrowableArray<Symbol*>(50, 0, NULL);
}
_symbols->push(sym);
}
_previous_symbol = sym;
return sym;
}

View File

@ -250,6 +250,8 @@ class ErrorContext {
class ClassVerifier : public StackObj {
private:
Thread* _thread;
Symbol* _previous_symbol; // cache of the previously looked up symbol
GrowableArray<Symbol*>* _symbols; // keep a list of symbols created
Symbol* _exception_type;
@ -411,12 +413,18 @@ class ClassVerifier : public StackObj {
// created, we can't use a TempNewSymbol.
Symbol* create_temporary_symbol(const Symbol* s, int begin, int end, TRAPS);
Symbol* create_temporary_symbol(const char *s, int length, TRAPS);
Symbol* create_temporary_symbol(Symbol* s) {
// This version just updates the reference count and saves the symbol to be
// dereferenced later.
s->increment_refcount();
_symbols->push(s);
if (s == _previous_symbol) {
return s;
}
if (!s->is_permanent()) {
s->increment_refcount();
if (_symbols == NULL) {
_symbols = new GrowableArray<Symbol*>(50, 0, NULL);
}
_symbols->push(s);
}
_previous_symbol = s;
return s;
}

View File

@ -264,6 +264,30 @@ void Symbol::decrement_refcount() {
}
}
void Symbol::make_permanent() {
uint32_t found = _length_and_refcount;
while (true) {
uint32_t old_value = found;
int refc = extract_refcount(old_value);
if (refc == PERM_REFCOUNT) {
return; // refcount is permanent, permanent is sticky
} else if (refc == 0) {
#ifdef ASSERT
print();
fatal("refcount underflow");
#endif
return;
} else {
int len = extract_length(old_value);
found = Atomic::cmpxchg(pack_length_and_refcount(len, PERM_REFCOUNT), &_length_and_refcount, old_value);
if (found == old_value) {
return; // successfully updated.
}
// refcount changed, try again.
}
}
}
void Symbol::metaspace_pointers_do(MetaspaceClosure* it) {
if (log_is_enabled(Trace, cds)) {
LogStream trace_stream(Log(cds)::trace());

View File

@ -170,6 +170,7 @@ class Symbol : public MetaspaceObj {
bool is_permanent() {
return (refcount() == PERM_REFCOUNT);
}
void make_permanent();
// Function char_at() returns the Symbol's selected u1 byte as a char type.
//

View File

@ -123,15 +123,6 @@ void SignatureIterator::check_signature_end() {
}
void SignatureIterator::dispatch_field() {
// no '(', just one (field) type
_index = 0;
_parameter_index = 0;
parse_type();
check_signature_end();
}
void SignatureIterator::iterate_parameters() {
// Parse parameters
_index = 0;
@ -196,7 +187,6 @@ void SignatureIterator::iterate_parameters( uint64_t fingerprint ) {
break;
case done_parm:
return;
break;
default:
tty->print_cr("*** parameter is " UINT64_FORMAT, fingerprint & parameter_feature_mask);
tty->print_cr("*** fingerprint is " PTR64_FORMAT, saved_fingerprint);
@ -205,7 +195,6 @@ void SignatureIterator::iterate_parameters( uint64_t fingerprint ) {
}
fingerprint >>= parameter_feature_size;
}
_parameter_index = 0;
}
@ -239,10 +228,7 @@ void SignatureIterator::iterate_returntype() {
break;
case '[':
{
int begin = ++_index;
while (sig->char_at(_index) == '[') {
_index++;
}
while (sig->char_at(++_index) == '[') ;
if (sig->char_at(_index) == 'L') {
while (sig->char_at(_index++) != ';') ;
} else {
@ -281,16 +267,17 @@ void SignatureIterator::iterate() {
// Implementation of SignatureStream
SignatureStream::SignatureStream(Symbol* signature, bool is_method) :
_signature(signature), _at_return_type(false) {
_signature(signature), _at_return_type(false), _previous_name(NULL), _names(NULL) {
_begin = _end = (is_method ? 1 : 0); // skip first '(' in method signatures
_names = new GrowableArray<Symbol*>(10);
next();
}
SignatureStream::~SignatureStream() {
// decrement refcount for names created during signature parsing
for (int i = 0; i < _names->length(); i++) {
_names->at(i)->decrement_refcount();
if (_names != NULL) {
for (int i = 0; i < _names->length(); i++) {
_names->at(i)->decrement_refcount();
}
}
}
@ -359,10 +346,35 @@ Symbol* SignatureStream::as_symbol(TRAPS) {
end--;
}
const char* symbol_chars = (const char*)_signature->base() + begin;
int len = end - begin;
// Quick check for common symbols in signatures
assert((vmSymbols::java_lang_String()->utf8_length() == 16 && vmSymbols::java_lang_Object()->utf8_length() == 16), "sanity");
if (len == 16 &&
strncmp(symbol_chars, "java/lang/", 10) == 0) {
if (strncmp("String", symbol_chars + 10, 6) == 0) {
return vmSymbols::java_lang_String();
} else if (strncmp("Object", symbol_chars + 10, 6) == 0) {
return vmSymbols::java_lang_Object();
}
}
Symbol* name = _previous_name;
if (name != NULL && name->equals(symbol_chars, len)) {
return name;
}
// Save names for cleaning up reference count at the end of
// SignatureStream scope.
Symbol* name = SymbolTable::new_symbol(_signature, begin, end, CHECK_NULL);
_names->push(name); // save new symbol for decrementing later
name = SymbolTable::new_symbol(symbol_chars, len, CHECK_NULL);
if (!name->is_permanent()) {
if (_names == NULL) {
_names = new GrowableArray<Symbol*>(10);
}
_names->push(name); // save new symbol for decrementing later
}
_previous_name = name;
return name;
}
@ -467,11 +479,12 @@ ssize_t SignatureVerifier::is_valid_type(const char* type, ssize_t limit) {
case 'L':
for (index = index + 1; index < limit; ++index) {
char c = type[index];
if (c == ';') {
return index + 1;
}
if (invalid_name_char(c)) {
return -1;
switch (c) {
case ';':
return index + 1;
case '\0': case '.': case '[':
return -1;
default: ; // fall through
}
}
// fall through
@ -479,13 +492,4 @@ ssize_t SignatureVerifier::is_valid_type(const char* type, ssize_t limit) {
}
return -1;
}
bool SignatureVerifier::invalid_name_char(char c) {
switch (c) {
case '\0': case '.': case ';': case '[':
return true;
default:
return false;
}
}
#endif // ASSERT

View File

@ -90,7 +90,6 @@ class SignatureIterator: public ResourceObj {
SignatureIterator(Symbol* signature);
// Iteration
void dispatch_field(); // dispatches once for field signatures
void iterate_parameters(); // iterates over parameters only
void iterate_parameters( uint64_t fingerprint );
void iterate_returntype(); // iterates over returntype only
@ -363,8 +362,8 @@ class SignatureStream : public StackObj {
int _end;
BasicType _type;
bool _at_return_type;
GrowableArray<Symbol*>* _names; // symbols created while parsing signature
Symbol* _previous_name; // cache the previously looked up symbol to avoid lookups
GrowableArray<Symbol*>* _names; // symbols created while parsing that need to be dereferenced
public:
bool at_return_type() const { return _at_return_type; }
bool is_done() const;
@ -402,7 +401,7 @@ class SignatureStream : public StackObj {
bool is_array() const; // True if this argument is an array
BasicType type() const { return _type; }
Symbol* as_symbol(TRAPS);
enum FailureMode { ReturnNull, CNFException, NCDFError };
enum FailureMode { ReturnNull, NCDFError };
Klass* as_klass(Handle class_loader, Handle protection_domain, FailureMode failure_mode, TRAPS);
oop as_java_mirror(Handle class_loader, Handle protection_domain, FailureMode failure_mode, TRAPS);
const u1* raw_bytes() { return _signature->bytes() + _begin; }
@ -421,9 +420,7 @@ class SignatureVerifier : public StackObj {
static bool is_valid_method_signature(Symbol* sig);
static bool is_valid_type_signature(Symbol* sig);
private:
static ssize_t is_valid_type(const char*, ssize_t);
static bool invalid_name_char(char);
};
#endif
#endif // SHARE_RUNTIME_SIGNATURE_HPP