/* * Copyright (c) 1998, 2022, 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. *
*/
TypedMethodOptionMatcher* TypedMethodOptionMatcher::clone() {
TypedMethodOptionMatcher* m = new TypedMethodOptionMatcher();
m->_class_mode = _class_mode;
m->_class_name = _class_name;
m->_method_mode = _method_mode;
m->_method_name = _method_name;
m->_signature = _signature; // Need to ref count the symbols if (_class_name != NULL) {
_class_name->increment_refcount();
} if (_method_name != NULL) {
_method_name->increment_refcount();
} if (_signature != NULL) {
_signature->increment_refcount();
} return m;
}
TypedMethodOptionMatcher::~TypedMethodOptionMatcher() { enum OptionType type = option2type(_option); if (type == OptionType::Ccstr || type == OptionType::Ccstrlist) {
ccstr v = value<ccstr>();
os::free((void*)v);
}
}
TypedMethodOptionMatcher* TypedMethodOptionMatcher::parse_method_pattern(char*& line, char* errorbuf, constint buf_size) {
assert(*errorbuf == '\0', "Dont call here with error_msg already set"); constchar* error_msg = NULL;
TypedMethodOptionMatcher* tom = new TypedMethodOptionMatcher();
MethodMatcher::parse_method_pattern(line, error_msg, tom); if (error_msg != NULL) {
jio_snprintf(errorbuf, buf_size, error_msg); delete tom; return NULL;
} return tom;
}
TypedMethodOptionMatcher* TypedMethodOptionMatcher::match(const methodHandle& method, enum CompileCommand option) {
TypedMethodOptionMatcher* current = this; while (current != NULL) { if (current->_option == option) { if (current->matches(method)) { return current;
}
}
current = current->next();
} return NULL;
}
template<typename T> staticvoid register_command(TypedMethodOptionMatcher* matcher, enum CompileCommand option,
T value) {
assert(matcher != option_list, "No circular lists please"); if (option == CompileCommand::Log && !LogCompilation) {
tty->print_cr("Warning: +LogCompilation must be enabled in order for individual methods to be logged with ");
tty->print_cr(" CompileCommand=log,");
}
assert(CompilerOracle::option_matches_type(option, value), "Value must match option type");
if (option == CompileCommand::Blackhole && !UnlockExperimentalVMOptions) {
warning("Blackhole compile option is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions"); return;
}
if (!CompilerOracle::be_quiet()) { // Print out the successful registration of a compile command
ttyLocker ttyl;
tty->print("CompileCommand: %s ", option2name(option));
matcher->print();
} return;
}
template<typename T> bool CompilerOracle::has_option_value(const methodHandle& method, enum CompileCommand option, T& value) {
assert(option_matches_type(option, value), "Value must match option type"); if (!has_command(option)) { returnfalse;
} if (option_list != NULL) {
TypedMethodOptionMatcher* m = option_list->match(method, option); if (m != NULL) {
value = m->value<T>(); returntrue;
}
} returnfalse;
}
staticbool resolve_inlining_predicate(enum CompileCommand option, const methodHandle& method) {
assert(option == CompileCommand::Inline || option == CompileCommand::DontInline, "Sanity"); bool v1 = false; bool v2 = false; bool has_inline = CompilerOracle::has_option_value(method, CompileCommand::Inline, v1); bool has_dnotinline = CompilerOracle::has_option_value(method, CompileCommand::DontInline, v2); if (has_inline && has_dnotinline) { if (v1 && v2) { // Conflict options detected // Find the last one for that method and return the predicate accordingly // option_list lists options in reverse order. So the first option we find is the last which was specified. enum CompileCommand last_one = CompileCommand::Unknown;
TypedMethodOptionMatcher* current = option_list; while (current != NULL) {
last_one = current->option(); if (last_one == CompileCommand::Inline || last_one == CompileCommand::DontInline) { if (current->matches(method)) { return last_one == option;
}
}
current = current->next();
}
ShouldNotReachHere(); returnfalse;
} else { // No conflicts return option == CompileCommand::Inline ? v1 : v2;
}
} else { if (option == CompileCommand::Inline) { return has_inline ? v1 : false;
} else { return has_dnotinline ? v2 : false;
}
}
}
staticbool check_predicate(enum CompileCommand option, const methodHandle& method) { // Special handling for Inline and DontInline since conflict options may be specified if (option == CompileCommand::Inline || option == CompileCommand::DontInline) { return resolve_inlining_predicate(option, method);
}
bool value = false; if (CompilerOracle::has_option_value(method, option, value)) { return value;
} returnfalse;
}
void CompilerOracle::tag_blackhole_if_possible(const methodHandle& method) { if (!check_predicate(CompileCommand::Blackhole, method)) { return;
}
guarantee(UnlockExperimentalVMOptions, "Checked during initial parsing"); if (method->result_type() != T_VOID) {
warning("Blackhole compile option only works for methods with void type: %s",
method->name_and_sig_as_C_string()); return;
} if (!method->is_empty_method()) {
warning("Blackhole compile option only works for empty methods: %s",
method->name_and_sig_as_C_string()); return;
} if (!method->is_static()) {
warning("Blackhole compile option only works for static methods: %s",
method->name_and_sig_as_C_string()); return;
} if (method->intrinsic_id() == vmIntrinsics::_blackhole) { return;
} if (method->intrinsic_id() != vmIntrinsics::_none) {
warning("Blackhole compile option only works for methods that do not have intrinsic set: %s, %s",
method->name_and_sig_as_C_string(), vmIntrinsics::name_at(method->intrinsic_id())); return;
}
method->set_intrinsic_id(vmIntrinsics::_blackhole);
}
*bytes_read = 0; char option_buf[256]; int matches = sscanf(line, "%255[a-zA-Z0-9]%n", option_buf, bytes_read); if (matches > 0 && strcasecmp(option_buf, "unknown") != 0) { for (uint i = 0; i < ARRAY_SIZE(option_names); i++) { if (strcasecmp(option_buf, option_names[i]) == 0) { returnstatic_cast<enum CompileCommand>(i);
}
}
}
jio_snprintf(errorbuf, bufsize, "Unrecognized option '%s'", option_buf); return CompileCommand::Unknown;
}
// match exactly and don't mess with errorbuf enum CompileCommand CompilerOracle::parse_option_name(constchar* line) { for (uint i = 0; i < ARRAY_SIZE(option_names); i++) { if (strcasecmp(line, option_names[i]) == 0) { returnstatic_cast<enum CompileCommand>(i);
}
} return CompileCommand::Unknown;
}
enum OptionType CompilerOracle::parse_option_type(constchar* type_str) { for (uint i = 0; i < ARRAY_SIZE(optiontype_names); i++) { if (strcasecmp(type_str, optiontype_names[i]) == 0) { returnstatic_cast<enum OptionType>(i);
}
} return OptionType::Unknown;
}
void print_tip() { // CMH Update info
tty->cr();
tty->print_cr("Usage: '-XX:CompileCommand=);
tty->print_cr("Usage: '-XX:CompileCommand=);
tty->print_cr("Use: '-XX:CompileCommand=help' for more information and to list all option.");
tty->cr();
}
staticvoid usage() {
tty->cr();
tty->print_cr("The CompileCommand option enables the user of the JVM to control specific");
tty->print_cr("behavior of the dynamic compilers.");
tty->cr();
tty->print_cr("Compile commands has this general form:");
tty->print_cr("-XX:CompileCommand=);
tty->print_cr(" Sets );
tty->print_cr(" All options are typed");
tty->cr();
tty->print_cr("-XX:CompileCommand=);
tty->print_cr(" Sets );
tty->print_cr(" Only applies to boolean options.");
tty->cr();
tty->print_cr("-XX:CompileCommand=quiet");
tty->print_cr(" Silence the compile command output");
tty->cr();
tty->print_cr("-XX:CompileCommand=help");
tty->print_cr(" Prints this help text");
tty->cr();
print_commands();
tty->cr();
tty->print_cr("Method patterns has the format:");
tty->print_cr(" package/Class.method()");
tty->cr();
tty->print_cr("For backward compatibility this form is also allowed:");
tty->print_cr(" package.Class::method()");
tty->cr();
tty->print_cr("The signature can be separated by an optional whitespace or comma:");
tty->print_cr(" package/Class.method ()");
tty->cr();
tty->print_cr("The class and method identifier can be used together with leading or");
tty->print_cr("trailing *'s for wildcard matching:");
tty->print_cr(" *ackage/Clas*.*etho*()");
tty->cr();
tty->print_cr("It is possible to use more than one CompileCommand on the command line:");
tty->print_cr(" -XX:CompileCommand=exclude,java/*.* -XX:CompileCommand=log,java*.*");
tty->cr();
tty->print_cr("The CompileCommands can be loaded from a file with the flag");
tty->print_cr("-XX:CompileCommandFile= or be added to the file '.hotspot_compiler'");
tty->print_cr("Use the same format in the file as the argument to the CompileCommand flag.");
tty->print_cr("Add one command on each line.");
tty->print_cr(" exclude java/*.*");
tty->print_cr(" option java/*.* ReplayInline");
tty->cr();
tty->print_cr("The following commands have conflicting behavior: 'exclude', 'inline', 'dontinline',");
tty->print_cr("and 'compileonly'. There is no priority of commands. Applying (a subset of) these");
tty->print_cr("commands to the same method results in undefined behavior.");
tty->cr();
};
int skip_whitespace(char* &line) { // Skip any leading spaces int whitespace_read = 0;
sscanf(line, "%*[ \t]%n", &whitespace_read);
line += whitespace_read; return whitespace_read;
}
void skip_comma(char* &line) { // Skip any leading spaces if (*line == ',') {
line++;
}
}
staticvoid scan_value(enum OptionType type, char* line, int& total_bytes_read,
TypedMethodOptionMatcher* matcher, enum CompileCommand option, char* errorbuf, constintbuf_size) { int bytes_read = 0; constchar* ccname = option2name(option); constchar* type_str = optiontype2name(type); int skipped = skip_whitespace(line);
total_bytes_read += skipped; if (type == OptionType::Intx) {
intx value; if (sscanf(line, "" INTX_FORMAT "%n", &value, &bytes_read) == 1) {
total_bytes_read += bytes_read;
line += bytes_read;
register_command(matcher, option, value); return;
} else {
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
}
} elseif (type == OptionType::Uintx) {
uintx value; if (sscanf(line, "" UINTX_FORMAT "%n", &value, &bytes_read) == 1) {
total_bytes_read += bytes_read;
line += bytes_read;
register_command(matcher, option, value); return;
} else {
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
}
} elseif (type == OptionType::Ccstr) {
ResourceMark rm; char* value = NEW_RESOURCE_ARRAY(char, strlen(line) + 1); if (sscanf(line, "%255[_a-zA-Z0-9]%n", value, &bytes_read) == 1) {
total_bytes_read += bytes_read;
line += bytes_read;
register_command(matcher, option, (ccstr) value); return;
} else {
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
}
} elseif (type == OptionType::Ccstrlist) { // Accumulates several strings into one. The internal type is ccstr.
ResourceMark rm; char* value = NEW_RESOURCE_ARRAY(char, strlen(line) + 1); char* next_value = value; if (sscanf(line, "%255[_a-zA-Z0-9+\\-]%n", next_value, &bytes_read) == 1) {
total_bytes_read += bytes_read;
line += bytes_read;
next_value += bytes_read + 1; char* end_value = next_value - 1; while (sscanf(line, "%*[ \t]%255[_a-zA-Z0-9+\\-]%n", next_value, &bytes_read) == 1) {
total_bytes_read += bytes_read;
line += bytes_read;
*end_value = ' '; // override '\0'
next_value += bytes_read;
end_value = next_value-1;
}
if (!validator.is_valid()) {
jio_snprintf(errorbuf, buf_size, "Unrecognized phase name in %s: %s", option2name(option), validator.what());
}
} elseif (option == CompileCommand::TestOptionList) { // all values are ok
} #endif else {
assert(false, "Ccstrlist type option missing validator");
}
register_command(matcher, option, (ccstr) value); return;
} else {
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
}
} elseif (type == OptionType::Bool) { char value[256]; if (*line == '\0') { // Short version of a CompileCommand sets a boolean Option to true // -XXCompileCommand=<Option>,<method pattern>
register_command(matcher, option, true); return;
} if (sscanf(line, "%255[a-zA-Z]%n", value, &bytes_read) == 1) { if (strcasecmp(value, "true") == 0) {
total_bytes_read += bytes_read;
line += bytes_read;
register_command(matcher, option, true); return;
} elseif (strcasecmp(value, "false") == 0) {
total_bytes_read += bytes_read;
line += bytes_read;
register_command(matcher, option, false); return;
} else {
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
}
} else {
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
}
} elseif (type == OptionType::Double) { char buffer[2][256]; // Decimal separator '.' has been replaced with ' ' or '/' earlier, // so read integer and fraction part of double value separately. if (sscanf(line, "%255[0-9]%*[ /\t]%255[0-9]%n", buffer[0], buffer[1], &bytes_read) == 2) { char value[512] = "";
jio_snprintf(value, sizeof(value), "%s.%s", buffer[0], buffer[1]);
total_bytes_read += bytes_read;
line += bytes_read;
register_command(matcher, option, atof(value)); return;
} else {
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
}
} else {
jio_snprintf(errorbuf, buf_size, "Type '%s' not supported ", type_str);
}
}
// Scan next option and value in line, return MethodMatcher object on success, NULL on failure. // On failure, error_msg contains description for the first error. // For future extensions: set error_msg on first error. staticvoid scan_option_and_value(enum OptionType type, char* line, int& total_bytes_read,
TypedMethodOptionMatcher* matcher, char* errorbuf, constint buf_size) {
total_bytes_read = 0; int bytes_read = 0; char option_buf[256];
// Read option name. if (sscanf(line, "%*[ \t]%255[a-zA-Z0-9]%n", option_buf, &bytes_read) == 1) {
line += bytes_read;
total_bytes_read += bytes_read; int bytes_read2 = 0;
total_bytes_read += skip_whitespace(line); enum CompileCommand option = match_option_name(option_buf, &bytes_read2, errorbuf, buf_size); if (option == CompileCommand::Unknown) {
assert(*errorbuf != '\0', "error must have been set"); return;
} enum OptionType optiontype = option2type(option); if (option2type(option) != type) { constchar* optiontype_name = optiontype2name(optiontype); constchar* type_name = optiontype2name(type);
jio_snprintf(errorbuf, buf_size, "Option '%s' with type '%s' doesn't match supplied type '%s'", option_buf, optiontype_name, type_name); return;
}
scan_value(type, line, total_bytes_read, matcher, option, errorbuf, buf_size);
} else { constchar* type_str = optiontype2name(type);
jio_snprintf(errorbuf, buf_size, "Option name for type '%s' should be alphanumeric ", type_str);
} return;
}
void CompilerOracle::print_parse_error(char* error_msg, char* original_line) {
assert(*error_msg != '\0', "Must have error_message");
ttyLocker ttyl;
tty->print_cr("CompileCommand: An error occurred during parsing");
tty->print_cr("Error: %s", error_msg);
tty->print_cr("Line: '%s'", original_line);
print_tip();
}
if (option == CompileCommand::Unknown) {
print_parse_error(error_buf, original.get()); return;
}
if (option == CompileCommand::Quiet) {
_quiet = true; return;
}
if (option == CompileCommand::Help) {
usage(); return;
}
if (option == CompileCommand::Option) { // Look for trailing options. // // Two types of trailing options are // supported: // // (1) CompileCommand=option,Klass::method,option // (2) CompileCommand=option,Klass::method,type,option,value // // Type (1) is used to enable a boolean option for a method. // // Type (2) is used to support options with a value. Values can have the // the following types: intx, uintx, bool, ccstr, ccstrlist, and double.
char option_type[256]; // stores option for Type (1) and type of Type (2)
skip_comma(line);
TypedMethodOptionMatcher* archetype = TypedMethodOptionMatcher::parse_method_pattern(line, error_buf, sizeof(error_buf)); if (archetype == NULL) {
print_parse_error(error_buf, original.get()); return;
}
skip_whitespace(line);
// This is unnecessarily complex. Should retire multi-option lines and skip while loop while (sscanf(line, "%255[a-zA-Z0-9]%n", option_type, &bytes_read) == 1) {
line += bytes_read;
// typed_matcher is used as a blueprint for each option, deleted at the end
TypedMethodOptionMatcher* typed_matcher = archetype->clone(); enum OptionType type = parse_option_type(option_type); if (type != OptionType::Unknown) { // Type (2) option: parse option name and value.
scan_option_and_value(type, line, bytes_read, typed_matcher, error_buf, sizeof(error_buf)); if (*error_buf != '\0') {
print_parse_error(error_buf, original.get()); return;
}
line += bytes_read;
} else { // Type (1) option - option_type contains the option name -> bool value = true is implied int bytes_read; enum CompileCommand option = match_option_name(option_type, &bytes_read, error_buf, sizeof(error_buf)); if (option == CompileCommand::Unknown) {
print_parse_error(error_buf, original.get()); return;
} if (option2type(option) == OptionType::Bool) {
register_command(typed_matcher, option, true);
} else {
jio_snprintf(error_buf, sizeof(error_buf), " Missing type '%s' before option '%s'",
optiontype2name(option2type(option)), option2name(option));
print_parse_error(error_buf, original.get()); return;
}
}
assert(typed_matcher != NULL, "sanity");
assert(*error_buf == '\0', "No error here");
skip_whitespace(line);
} // while( delete archetype;
} else { // not an OptionCommand // Command has the following form: // CompileCommand=<option>,<method pattern><value> // CompileCommand=<option>,<method pattern> (implies option is bool and value is true)
assert(*error_buf == '\0', "Don't call here with error_buf already set"); enum OptionType type = option2type(option); int bytes_read = 0;
skip_comma(line);
TypedMethodOptionMatcher* matcher = TypedMethodOptionMatcher::parse_method_pattern(line, error_buf, sizeof(error_buf)); if (matcher == NULL) {
print_parse_error(error_buf, original.get()); return;
}
skip_whitespace(line); if (*line == '\0') { // if this is a bool option this implies true if (option2type(option) == OptionType::Bool) {
register_command(matcher, option, true); return;
} else {
jio_snprintf(error_buf, sizeof(error_buf), " Option '%s' is not followed by a value", option2name(option));
print_parse_error(error_buf, original.get()); return;
}
}
scan_value(type, line, bytes_read, matcher, option, error_buf, sizeof(error_buf)); if (*error_buf != '\0') {
print_parse_error(error_buf, original.get()); return;
}
assert(matcher != NULL, "consistency");
}
}
void CompilerOracle::parse_from_file() {
assert(has_command_file(), "command file must be specified");
FILE* stream = os::fopen(cc_file(), "rt"); if (stream == NULL) return;
char token[1024]; int pos = 0; int c = getc(stream); while(c != EOF && pos < (int)(sizeof(token)-1)) { if (c == '\n') {
token[pos++] = '\0';
parse_from_line(token);
pos = 0;
} else {
token[pos++] = c;
}
c = getc(stream);
}
token[pos++] = '\0';
parse_from_line(token);
fclose(stream);
}
void CompilerOracle::parse_from_string(constchar* str, void (*parse_line)(char*)) { char token[1024]; int pos = 0; constchar* sp = str; int c = *sp++; while (c != '\0' && pos < (int)(sizeof(token)-1)) { if (c == '\n') {
token[pos++] = '\0';
parse_line(token);
pos = 0;
} else {
token[pos++] = c;
}
c = *sp++;
}
token[pos++] = '\0';
parse_line(token);
}
void compilerOracle_init() {
CompilerOracle::parse_from_string(CompileCommand, CompilerOracle::parse_from_line);
CompilerOracle::parse_from_string(CompileOnly, CompilerOracle::parse_compile_only); if (CompilerOracle::has_command_file()) {
CompilerOracle::parse_from_file();
} else { struct stat buf; if (os::stat(default_cc_file, &buf) == 0) {
warning("%s file is present but has been ignored. " "Run with -XX:CompileCommandFile=%s to load the file.",
default_cc_file, default_cc_file);
}
} if (has_command(CompileCommand::Print)) { if (PrintAssembly) {
warning("CompileCommand and/or %s file contains 'print' commands, but PrintAssembly is also enabled", default_cc_file);
}
}
}
if (*line == method_sep) { if (className == NULL) {
className = "";
c_match = MethodMatcher::Any;
}
} else { // got foo or foo/bar if (className == NULL) {
ShouldNotReachHere();
} else { // missing class name handled as "Any" class match if (className[0] == '\0') {
c_match = MethodMatcher::Any;
}
}
}
// each directive is terminated by , or NUL or . followed by NUL if (*line == ',' || *line == '\0' || (line[0] == '.' && line[1] == '\0')) { if (methodName == NULL) {
methodName = ""; if (*line != method_sep) {
m_match = MethodMatcher::Any;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.