mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-16 08:29:34 +00:00
8313713: Allow CompileCommand flag to specify compilation level
Reviewed-by: kvn, asmehra
This commit is contained in:
parent
3c1af6b9c8
commit
c35c32502d
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2026, 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
|
||||
@ -3558,7 +3558,7 @@ const char* GraphBuilder::check_can_parse(ciMethod* callee) const {
|
||||
|
||||
// negative filter: should callee NOT be inlined? returns null, ok to inline, or rejection msg
|
||||
const char* GraphBuilder::should_not_inline(ciMethod* callee) const {
|
||||
if ( compilation()->directive()->should_not_inline(callee)) return "disallowed by CompileCommand";
|
||||
if ( compilation()->directive()->should_not_inline(callee, compilation()->env()->comp_level())) return "disallowed by CompileCommand";
|
||||
if ( callee->dont_inline()) return "don't inline by annotation";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 2026, 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
|
||||
@ -814,23 +814,32 @@ CompileTask* CompilationPolicy::select_task(CompileQueue* compile_queue, JavaThr
|
||||
max_method = max_task->method();
|
||||
}
|
||||
|
||||
methodHandle max_method_h(THREAD, max_method);
|
||||
if (max_task != nullptr && max_method != nullptr) {
|
||||
methodHandle max_method_h(THREAD, max_method);
|
||||
|
||||
if (max_task != nullptr && max_task->comp_level() == CompLevel_full_profile && TieredStopAtLevel > CompLevel_full_profile &&
|
||||
max_method != nullptr && is_method_profiled(max_method_h) && !Arguments::is_compiler_only()) {
|
||||
max_task->set_comp_level(CompLevel_limited_profile);
|
||||
if (max_task->comp_level() == CompLevel_full_profile && TieredStopAtLevel > CompLevel_full_profile &&
|
||||
is_method_profiled(max_method_h) && !Arguments::is_compiler_only()) {
|
||||
|
||||
if (CompileBroker::compilation_is_complete(max_method_h, max_task->osr_bci(), CompLevel_limited_profile)) {
|
||||
if (PrintTieredEvents) {
|
||||
print_event(REMOVE_FROM_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
|
||||
CompilerDirectiveMatcher directive_matcher(max_method_h, CompLevel_limited_profile);
|
||||
bool exclude_limited_profile = directive_matcher.directive_set()->ExcludeOption;
|
||||
|
||||
if (!exclude_limited_profile) {
|
||||
max_task->set_comp_level(CompLevel_limited_profile);
|
||||
max_task->transfer_directive(directive_matcher);
|
||||
|
||||
if (CompileBroker::compilation_is_complete(max_method_h, max_task->osr_bci(), CompLevel_limited_profile)) {
|
||||
if (PrintTieredEvents) {
|
||||
print_event(REMOVE_FROM_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
|
||||
}
|
||||
compile_queue->remove_and_mark_stale(max_task);
|
||||
max_method->clear_queued_for_compilation();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PrintTieredEvents) {
|
||||
print_event(UPDATE_IN_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
|
||||
}
|
||||
}
|
||||
compile_queue->remove_and_mark_stale(max_task);
|
||||
max_method->clear_queued_for_compilation();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PrintTieredEvents) {
|
||||
print_event(UPDATE_IN_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
|
||||
}
|
||||
}
|
||||
return max_task;
|
||||
|
||||
@ -1382,7 +1382,7 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
|
||||
}
|
||||
#endif
|
||||
|
||||
CompilerDirectiveMatcher matcher(method, comp);
|
||||
CompilerDirectiveMatcher matcher(method, comp_level);
|
||||
// CompileBroker::compile_method can trap and can have pending async exception.
|
||||
nmethod* nm = CompileBroker::compile_method(method, osr_bci, comp_level, hot_count, compile_reason, matcher.directive_set(), THREAD);
|
||||
return nm;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -57,7 +57,7 @@ CompileTask::CompileTask(int compile_id,
|
||||
_nm_insts_size(0),
|
||||
_comp_level(comp_level),
|
||||
_compiler(CompileBroker::compiler(comp_level)),
|
||||
_comp_directive_matcher(method, _compiler),
|
||||
_comp_directive_matcher(method, static_cast<CompLevel>(comp_level)),
|
||||
JVMCI_ONLY(_has_waiter(_compiler->is_jvmci()) COMMA)
|
||||
JVMCI_ONLY(_blocking_jvmci_compile_state(nullptr) COMMA)
|
||||
_num_inlined_bytecodes(0),
|
||||
|
||||
@ -131,6 +131,7 @@ class CompileTask : public CHeapObj<mtCompiler> {
|
||||
bool is_blocking() const { return _is_blocking; }
|
||||
bool is_success() const { return _is_success; }
|
||||
DirectiveSet* directive() const { return _comp_directive_matcher.directive_set(); }
|
||||
void transfer_directive(CompilerDirectiveMatcher& matcher) { _comp_directive_matcher.transfer_from(matcher); }
|
||||
CompileReason compile_reason() const { return _compile_reason; }
|
||||
CodeSection::csize_t nm_content_size() { return _nm_content_size; }
|
||||
void set_nm_content_size(CodeSection::csize_t size) { _nm_content_size = size; }
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -25,6 +25,7 @@
|
||||
#include "ci/ciMethod.hpp"
|
||||
#include "ci/ciUtilities.inline.hpp"
|
||||
#include "compiler/abstractCompiler.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "compiler/compilerDefinitions.inline.hpp"
|
||||
#include "compiler/compilerDirectives.hpp"
|
||||
#include "compiler/compilerOracle.hpp"
|
||||
@ -378,7 +379,7 @@ class DirectiveSetPtr {
|
||||
// - if some option is changed we need to copy directiveset since it no longer can be shared
|
||||
// - Need to free copy after use
|
||||
// - Requires a modified bit so we don't overwrite options that is set by directives
|
||||
DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle& method) {
|
||||
DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle& method, int comp_level) {
|
||||
// Early bail out - checking all options is expensive - we rely on them not being used
|
||||
// Only set a flag if it has not been modified and value changes.
|
||||
// Only copy set if a flag needs to be set
|
||||
@ -397,7 +398,7 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle
|
||||
|
||||
// All CompileCommands are not equal so this gets a bit verbose
|
||||
// When CompileCommands have been refactored less clutter will remain.
|
||||
if (CompilerOracle::should_break_at(method)) {
|
||||
if (CompilerOracle::should_break_at(method, static_cast<CompLevel>(comp_level))) {
|
||||
// If the directives didn't have 'BreakAtCompile' or 'BreakAtExecute',
|
||||
// the sub-command 'Break' of the 'CompileCommand' would become effective.
|
||||
if (!_modified[BreakAtCompileIndex]) {
|
||||
@ -414,13 +415,13 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle
|
||||
}
|
||||
}
|
||||
|
||||
if (CompilerOracle::should_print(method)) {
|
||||
if (CompilerOracle::should_print(method, static_cast<CompLevel>(comp_level))) {
|
||||
if (!_modified[PrintAssemblyIndex]) {
|
||||
set.cloned()->PrintAssemblyOption = true;
|
||||
}
|
||||
}
|
||||
// Exclude as in should not compile == Enabled
|
||||
if (CompilerOracle::should_exclude(method)) {
|
||||
if (CompilerOracle::should_exclude(method, static_cast<CompLevel>(comp_level))) {
|
||||
if (!_modified[ExcludeIndex]) {
|
||||
set.cloned()->ExcludeOption = true;
|
||||
}
|
||||
@ -547,7 +548,7 @@ bool DirectiveSet::should_inline(ciMethod* inlinee) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectiveSet::should_not_inline(ciMethod* inlinee) {
|
||||
bool DirectiveSet::should_not_inline(ciMethod* inlinee, int comp_level) {
|
||||
inlinee->check_is_loaded();
|
||||
VM_ENTRY_MARK;
|
||||
methodHandle mh(THREAD, inlinee->get_Method());
|
||||
@ -556,7 +557,7 @@ bool DirectiveSet::should_not_inline(ciMethod* inlinee) {
|
||||
return matches_inline(mh, InlineMatcher::dont_inline);
|
||||
}
|
||||
if (!CompilerDirectivesIgnoreCompileCommandsOption) {
|
||||
return CompilerOracle::should_not_inline(mh);
|
||||
return CompilerOracle::should_not_inline(mh, static_cast<CompLevel>(comp_level));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -755,7 +756,7 @@ void DirectivesStack::release(DirectiveSet* set) {
|
||||
assert(set != nullptr, "Never nullptr");
|
||||
MutexLocker locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (set->is_exclusive_copy()) {
|
||||
// Old CompilecCmmands forced us to create an exclusive copy
|
||||
// Old CompileCommands forced us to create an exclusive copy
|
||||
delete set;
|
||||
} else {
|
||||
assert(set->directive() != nullptr, "Never nullptr");
|
||||
@ -772,8 +773,9 @@ void DirectivesStack::release(CompilerDirectives* dir) {
|
||||
}
|
||||
}
|
||||
|
||||
DirectiveSet* DirectivesStack::getMatchingDirective(const methodHandle& method, AbstractCompiler *comp) {
|
||||
DirectiveSet* DirectivesStack::getMatchingDirective(const methodHandle& method, int comp_level) {
|
||||
assert(_depth > 0, "Must never be empty");
|
||||
AbstractCompiler* comp = CompileBroker::compiler(comp_level);
|
||||
|
||||
DirectiveSet* match = nullptr;
|
||||
{
|
||||
@ -798,5 +800,5 @@ DirectiveSet* DirectivesStack::getMatchingDirective(const methodHandle& method,
|
||||
guarantee(match != nullptr, "There should always be a default directive that matches");
|
||||
|
||||
// Check for legacy compile commands update, without DirectivesStack_lock
|
||||
return match->compilecommand_compatibility_init(method);
|
||||
return match->compilecommand_compatibility_init(method, comp_level);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -115,7 +115,7 @@ private:
|
||||
static int _depth;
|
||||
|
||||
static void pop_inner(); // no lock version of pop
|
||||
static DirectiveSet* getMatchingDirective(const methodHandle& mh, AbstractCompiler* comp);
|
||||
static DirectiveSet* getMatchingDirective(const methodHandle& mh, int comp_level);
|
||||
static DirectiveSet* getDefaultDirective(AbstractCompiler* comp);
|
||||
static void release(DirectiveSet* set);
|
||||
static void release(CompilerDirectives* dir);
|
||||
@ -145,10 +145,10 @@ public:
|
||||
bool parse_and_add_inline(char* str, const char*& error_msg);
|
||||
void append_inline(InlineMatcher* m);
|
||||
bool should_inline(ciMethod* inlinee);
|
||||
bool should_not_inline(ciMethod* inlinee);
|
||||
bool should_not_inline(ciMethod* inlinee, int comp_level);
|
||||
bool should_delay_inline(ciMethod* inlinee);
|
||||
void print_inline(outputStream* st);
|
||||
DirectiveSet* compilecommand_compatibility_init(const methodHandle& method);
|
||||
DirectiveSet* compilecommand_compatibility_init(const methodHandle& method, int comp_level);
|
||||
bool is_exclusive_copy() { return _directive == nullptr; }
|
||||
bool matches_inline(const methodHandle& method, int inline_action);
|
||||
static DirectiveSet* clone(DirectiveSet const* src);
|
||||
@ -335,21 +335,35 @@ public:
|
||||
class CompilerDirectiveMatcher {
|
||||
private:
|
||||
DirectiveSet* _match;
|
||||
|
||||
void release_match() {
|
||||
if (_match != nullptr) {
|
||||
DirectivesStack::release(_match);
|
||||
_match = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// Use this constructor to get default directive
|
||||
CompilerDirectiveMatcher(AbstractCompiler* comp) {
|
||||
_match = DirectivesStack::getDefaultDirective(comp);
|
||||
}
|
||||
|
||||
CompilerDirectiveMatcher(const methodHandle& mh, AbstractCompiler* comp) {
|
||||
_match = DirectivesStack::getMatchingDirective(mh, comp);
|
||||
CompilerDirectiveMatcher(const methodHandle& mh, int comp_level) {
|
||||
_match = DirectivesStack::getMatchingDirective(mh, comp_level);
|
||||
}
|
||||
|
||||
~CompilerDirectiveMatcher() {
|
||||
DirectivesStack::release(_match);
|
||||
release_match();
|
||||
}
|
||||
|
||||
DirectiveSet* directive_set() const { return _match; }
|
||||
|
||||
void transfer_from(CompilerDirectiveMatcher& src) {
|
||||
release_match();
|
||||
_match = src._match;
|
||||
src._match = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_COMPILER_COMPILERDIRECTIVES_HPP
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -60,6 +60,37 @@ static const char* const default_compile_commands[] = {
|
||||
#endif
|
||||
nullptr };
|
||||
|
||||
// CompLevel | -XX:CompileCommand bitmask
|
||||
// ----------------------------------------------------
|
||||
// 0 (interpreter) | N/A
|
||||
// 1 (C1) | 1
|
||||
// 2 (C1 + counters) | 10
|
||||
// 3 (C1 + counters + mdo) | 100
|
||||
// 4 (C2/JVMCI) | 1000
|
||||
// All C1 levels | 111
|
||||
// All levels | 1111
|
||||
|
||||
static const int comp_level_bitmask[CompLevel_count] = {0, 1, 10, 100, 1000};
|
||||
static const int comp_level_bitmask_all_levels = 1111;
|
||||
static const intx default_comp_level_argument = comp_level_bitmask_all_levels;
|
||||
|
||||
inline bool bitmask_applies_to_comp_level(int bitmask, int comp_level) {
|
||||
assert(comp_level > CompLevel_none && comp_level < CompLevel_count, "CompLevel out of bounds");
|
||||
return (bitmask / comp_level_bitmask[comp_level]) % 10 == 1;
|
||||
}
|
||||
|
||||
static bool is_valid_comp_level_bitmask(intx bitmask) {
|
||||
if (bitmask < 0 || bitmask > comp_level_bitmask_all_levels) {
|
||||
return false;
|
||||
}
|
||||
for (; bitmask != 0; bitmask /= 10) {
|
||||
if (bitmask % 10 > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* optiontype_names[] = {
|
||||
#define enum_of_types(type, name) name,
|
||||
OPTION_TYPES(enum_of_types)
|
||||
@ -456,36 +487,56 @@ template bool CompilerOracle::option_matches_type<bool>(CompileCommandEnum optio
|
||||
template bool CompilerOracle::option_matches_type<ccstr>(CompileCommandEnum option, ccstr& value);
|
||||
template bool CompilerOracle::option_matches_type<double>(CompileCommandEnum option, double& value);
|
||||
|
||||
bool CompilerOracle::applies_to_comp_level(const methodHandle& method, CompileCommandEnum command, CompLevel current_level) {
|
||||
if (current_level == CompLevel_none) {
|
||||
return false;
|
||||
}
|
||||
|
||||
intx bitmask = 0;
|
||||
if (!has_option_value(method, command, bitmask)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since we don't have bitmask for interpreter level (0), but still need to call CompilerOracle::should_print()
|
||||
// from collect_profiled_methods() in java.cpp, a special value of CompLevel_any produces a match with any bitmask, even 0
|
||||
return current_level == CompLevel_any
|
||||
|| bitmask_applies_to_comp_level(bitmask, current_level);
|
||||
}
|
||||
|
||||
bool CompilerOracle::has_option(const methodHandle& method, CompileCommandEnum option) {
|
||||
bool value = false;
|
||||
has_option_value(method, option, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_exclude(const methodHandle& method) {
|
||||
if (check_predicate(CompileCommandEnum::Exclude, method)) {
|
||||
bool CompilerOracle::should_exclude(const methodHandle& method, const CompLevel level) {
|
||||
if (has_exclude(method, level)) {
|
||||
return true;
|
||||
}
|
||||
if (has_command(CompileCommandEnum::CompileOnly)) {
|
||||
return !check_predicate(CompileCommandEnum::CompileOnly, method);
|
||||
return !applies_to_comp_level(method, CompileCommandEnum::CompileOnly, level);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompilerOracle::has_exclude(const methodHandle& method, const CompLevel level) {
|
||||
return applies_to_comp_level(method, CompileCommandEnum::Exclude, level);
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_inline(const methodHandle& method) {
|
||||
return (check_predicate(CompileCommandEnum::Inline, method));
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_not_inline(const methodHandle& method) {
|
||||
return check_predicate(CompileCommandEnum::DontInline, method) || check_predicate(CompileCommandEnum::Exclude, method);
|
||||
bool CompilerOracle::should_not_inline(const methodHandle& method, const CompLevel level) {
|
||||
return check_predicate(CompileCommandEnum::DontInline, method) || has_exclude(method, level);
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_delay_inline(const methodHandle& method) {
|
||||
return (check_predicate(CompileCommandEnum::DelayInline, method));
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_print(const methodHandle& method) {
|
||||
return check_predicate(CompileCommandEnum::Print, method);
|
||||
bool CompilerOracle::should_print(const methodHandle& method, const CompLevel level) {
|
||||
return applies_to_comp_level(method, CompileCommandEnum::Print, level);
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_print_methods() {
|
||||
@ -505,8 +556,8 @@ bool CompilerOracle::should_log(const methodHandle& method) {
|
||||
return (check_predicate(CompileCommandEnum::Log, method));
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_break_at(const methodHandle& method) {
|
||||
return check_predicate(CompileCommandEnum::Break, method);
|
||||
bool CompilerOracle::should_break_at(const methodHandle& method, const CompLevel level) {
|
||||
return applies_to_comp_level(method, CompileCommandEnum::Break, level);
|
||||
}
|
||||
|
||||
void CompilerOracle::tag_blackhole_if_possible(const methodHandle& method) {
|
||||
@ -678,6 +729,19 @@ static void usage() {
|
||||
tty->print_cr("from inlining, whereas the 'compileonly' command only excludes methods from");
|
||||
tty->print_cr("top-level compilations (i.e. they can still be inlined into other compilation units).");
|
||||
tty->cr();
|
||||
tty->print_cr("Compilation levels can be specified in the 'compileonly', 'exclude', 'print',");
|
||||
tty->print_cr("and 'break' commands using a binary bitmask as an optional value:");
|
||||
tty->print_cr(" -XX:CompileCommand=exclude,java/*.*,1011 -XX:CompileCommand=print,java/*.*,100");
|
||||
tty->cr();
|
||||
tty->print_cr("The bitmask is calculated by summing the desired compilation level values:");
|
||||
tty->print_cr(" C1 without profiling = 1");
|
||||
tty->print_cr(" C1 with limited profiling = 10");
|
||||
tty->print_cr(" C1 with full profiling = 100");
|
||||
tty->print_cr(" C2 = 1000");
|
||||
tty->cr();
|
||||
tty->print_cr("Note: Excluding specific compilation levels may disrupt normal state transitions");
|
||||
tty->print_cr("between the levels, as the VM will not automatically work around the excluded ones.");
|
||||
tty->cr();
|
||||
};
|
||||
|
||||
static int skip_whitespace(char* &line) {
|
||||
@ -712,7 +776,7 @@ static bool parseMemLimit(const char* line, intx& value, int& bytes_read, char*
|
||||
size_t s = 0;
|
||||
char* end;
|
||||
if (!parse_integer<size_t>(line, &end, &s)) {
|
||||
jio_snprintf(errorbuf, buf_size, "MemLimit: invalid value");
|
||||
jio_snprintf(errorbuf, buf_size, ": invalid integer: '%.20s'", line);
|
||||
return false;
|
||||
}
|
||||
bytes_read = (int)(end - line);
|
||||
@ -726,7 +790,7 @@ static bool parseMemLimit(const char* line, intx& value, int& bytes_read, char*
|
||||
// ok, this is the default
|
||||
bytes_read += 5;
|
||||
} else {
|
||||
jio_snprintf(errorbuf, buf_size, "MemLimit: invalid option");
|
||||
jio_snprintf(errorbuf, buf_size, ": invalid suffix: '%.6s'", end);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -751,7 +815,7 @@ static bool parseMemStat(const char* line, uintx& value, int& bytes_read, char*
|
||||
});
|
||||
#undef IF_ENUM_STRING
|
||||
|
||||
jio_snprintf(errorbuf, buf_size, "MemStat: invalid option");
|
||||
jio_snprintf(errorbuf, buf_size, ": invalid option: '%.8s'", line);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -763,21 +827,42 @@ static bool scan_value(enum OptionType type, char* line, int& total_bytes_read,
|
||||
const char* type_str = optiontype2name(type);
|
||||
int skipped = skip_whitespace(line);
|
||||
total_bytes_read += skipped;
|
||||
char parse_error_buf[80] = {};
|
||||
|
||||
if (type == OptionType::Intx) {
|
||||
intx value;
|
||||
bool success = false;
|
||||
if (option == CompileCommandEnum::MemLimit) {
|
||||
// Special parsing for MemLimit
|
||||
success = parseMemLimit(line, value, bytes_read, errorbuf, buf_size);
|
||||
} else {
|
||||
// Is it a raw number?
|
||||
success = sscanf(line, "%zd%n", &value, &bytes_read) == 1;
|
||||
switch (option) {
|
||||
case CompileCommandEnum::MemLimit:
|
||||
// Special parsing for MemLimit
|
||||
success = parseMemLimit(line, value, bytes_read, parse_error_buf, sizeof(parse_error_buf));
|
||||
break;
|
||||
case CompileCommandEnum::Break:
|
||||
case CompileCommandEnum::CompileOnly:
|
||||
case CompileCommandEnum::Exclude:
|
||||
case CompileCommandEnum::Print:
|
||||
// In the commands above the parameter used to be a boolean. Now it is an int (a compilation level mask).
|
||||
// For compatibility with previous versions we keep it optional. If user did not specify the mask, assume default value
|
||||
if (*line == '\0') {
|
||||
value = default_comp_level_argument;
|
||||
success = true;
|
||||
} else {
|
||||
success = sscanf(line, "%zd%n", &value, &bytes_read) == 1;
|
||||
if (success && !is_valid_comp_level_bitmask(value)) {
|
||||
jio_snprintf(parse_error_buf, sizeof(parse_error_buf), ": invalid compilation level bitmask '%.*s'", bytes_read, line);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Is it a raw number?
|
||||
success = sscanf(line, "%zd%n", &value, &bytes_read) == 1;
|
||||
}
|
||||
if (success) {
|
||||
total_bytes_read += bytes_read;
|
||||
return register_command(matcher, option, errorbuf, buf_size, value);
|
||||
} else {
|
||||
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
|
||||
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'%s", ccname, type_str, parse_error_buf);
|
||||
return false;
|
||||
}
|
||||
} else if (type == OptionType::Uintx) {
|
||||
@ -785,7 +870,7 @@ static bool scan_value(enum OptionType type, char* line, int& total_bytes_read,
|
||||
bool success = false;
|
||||
if (option == CompileCommandEnum::MemStat) {
|
||||
// Special parsing for MemStat
|
||||
success = parseMemStat(line, value, bytes_read, errorbuf, buf_size);
|
||||
success = parseMemStat(line, value, bytes_read, parse_error_buf, sizeof(parse_error_buf));
|
||||
} else {
|
||||
// parse as raw number
|
||||
success = sscanf(line, "%zu%n", &value, &bytes_read) == 1;
|
||||
@ -794,7 +879,7 @@ static bool scan_value(enum OptionType type, char* line, int& total_bytes_read,
|
||||
total_bytes_read += bytes_read;
|
||||
return register_command(matcher, option, errorbuf, buf_size, value);
|
||||
} else {
|
||||
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'", ccname, type_str);
|
||||
jio_snprintf(errorbuf, buf_size, "Value cannot be read for option '%s' of type '%s'%s", ccname, type_str, parse_error_buf);
|
||||
return false;
|
||||
}
|
||||
} else if (type == OptionType::Ccstr) {
|
||||
@ -1089,17 +1174,25 @@ bool CompilerOracle::parse_from_line(char* line) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (option == CompileCommandEnum::MemStat) {
|
||||
// MemStat default action is to collect data but to not print
|
||||
if (!register_command(matcher, option, error_buf, sizeof(error_buf), (uintx)MemStatAction::collect)) {
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case CompileCommandEnum::Break:
|
||||
case CompileCommandEnum::CompileOnly:
|
||||
case CompileCommandEnum::Exclude:
|
||||
case CompileCommandEnum::Print:
|
||||
break;
|
||||
case CompileCommandEnum::MemStat:
|
||||
// MemStat default action is to collect data but to not print
|
||||
if (!register_command(matcher, option, error_buf, sizeof(error_buf), (uintx)MemStatAction::collect)) {
|
||||
print_parse_error(error_buf, original.get());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
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 false;
|
||||
}
|
||||
return true;
|
||||
} 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 false;
|
||||
}
|
||||
}
|
||||
if (!scan_value(type, line, bytes_read, matcher, option, error_buf, sizeof(error_buf))) {
|
||||
@ -1209,7 +1302,7 @@ bool CompilerOracle::parse_compile_only(char* line) {
|
||||
if (method_pattern != nullptr) {
|
||||
TypedMethodOptionMatcher* matcher = TypedMethodOptionMatcher::parse_method_pattern(method_pattern, error_buf, sizeof(error_buf));
|
||||
if (matcher != nullptr) {
|
||||
if (register_command(matcher, CompileCommandEnum::CompileOnly, error_buf, sizeof(error_buf), true)) {
|
||||
if (register_command(matcher, CompileCommandEnum::CompileOnly, error_buf, sizeof(error_buf), default_comp_level_argument)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -25,6 +25,7 @@
|
||||
#ifndef SHARE_COMPILER_COMPILERORACLE_HPP
|
||||
#define SHARE_COMPILER_COMPILERORACLE_HPP
|
||||
|
||||
#include "compiler/compilerDirectives.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "oops/oopsHierarchy.hpp"
|
||||
#include "utilities/istream.hpp"
|
||||
@ -49,14 +50,14 @@ class methodHandle;
|
||||
option(Help, "help", Unknown) \
|
||||
option(Quiet, "quiet", Unknown) \
|
||||
option(Log, "log", Bool) \
|
||||
option(Print, "print", Bool) \
|
||||
option(Print, "print", Intx) \
|
||||
option(Inline, "inline", Bool) \
|
||||
option(DelayInline, "delayinline", Bool) \
|
||||
option(DontInline, "dontinline", Bool) \
|
||||
option(Blackhole, "blackhole", Bool) \
|
||||
option(CompileOnly, "compileonly", Bool)\
|
||||
option(Exclude, "exclude", Bool) \
|
||||
option(Break, "break", Bool) \
|
||||
option(CompileOnly, "compileonly", Intx) \
|
||||
option(Exclude, "exclude", Intx) \
|
||||
option(Break, "break", Intx) \
|
||||
option(BreakAtExecute, "BreakAtExecute", Bool) \
|
||||
option(BreakAtCompile, "BreakAtCompile", Bool) \
|
||||
option(MemLimit, "MemLimit", Intx) \
|
||||
@ -135,6 +136,9 @@ class CompilerOracle : AllStatic {
|
||||
static bool parse_from_input(inputStream::Input* input,
|
||||
parse_from_line_fn_t* parse_from_line);
|
||||
|
||||
static bool has_exclude(const methodHandle& method, CompLevel level);
|
||||
static bool applies_to_comp_level(const methodHandle& method, CompileCommandEnum command, CompLevel current_level);
|
||||
|
||||
public:
|
||||
// True if the command file has been specified or is implicit
|
||||
static bool has_command_file();
|
||||
@ -143,14 +147,15 @@ class CompilerOracle : AllStatic {
|
||||
static bool parse_from_file();
|
||||
|
||||
// Tells whether we to exclude compilation of method
|
||||
static bool should_exclude(const methodHandle& method);
|
||||
static bool should_exclude(const methodHandle & method, CompLevel level);
|
||||
|
||||
static bool be_quiet() { return _quiet; }
|
||||
|
||||
// Tells whether we want to inline this method
|
||||
static bool should_inline(const methodHandle& method);
|
||||
|
||||
// Tells whether we want to disallow inlining of this method
|
||||
static bool should_not_inline(const methodHandle& method);
|
||||
static bool should_not_inline(const methodHandle& method, CompLevel level);
|
||||
|
||||
// Tells whether we want to delay inlining of this method
|
||||
static bool should_delay_inline(const methodHandle& method);
|
||||
@ -159,13 +164,14 @@ class CompilerOracle : AllStatic {
|
||||
static bool changes_current_thread(const methodHandle& method);
|
||||
|
||||
// Tells whether we should print the assembly for this method
|
||||
static bool should_print(const methodHandle& method);
|
||||
// If level == CompLevel_none or CompLevel_any, returns true if there is a print command with any mask
|
||||
static bool should_print(const methodHandle& method, CompLevel level);
|
||||
|
||||
// Tells whether we should log the compilation data for this method
|
||||
static bool should_log(const methodHandle& method);
|
||||
|
||||
// Tells whether to break when compiling method
|
||||
static bool should_break_at(const methodHandle& method);
|
||||
static bool should_break_at(const methodHandle& method, CompLevel level);
|
||||
|
||||
// Tells whether there are any methods to print for print_method_statistics()
|
||||
static bool should_print_methods();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2026, 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
|
||||
@ -818,7 +818,7 @@ JVMCI::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler,
|
||||
cb = nm;
|
||||
if (compile_state == nullptr) {
|
||||
// This compile didn't come through the CompileBroker so perform the printing here
|
||||
CompilerDirectiveMatcher matcher(method, compiler);
|
||||
CompilerDirectiveMatcher matcher(method, CompLevel_full_optimization);
|
||||
nm->maybe_print_nmethod(matcher.directive_set());
|
||||
|
||||
// Since this compilation didn't pass through the broker it wasn't logged yet.
|
||||
|
||||
@ -585,7 +585,7 @@ C2V_END
|
||||
|
||||
C2V_VMENTRY_0(jboolean, hasNeverInlineDirective,(JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
|
||||
methodHandle method (THREAD, UNPACK_PAIR(Method, method));
|
||||
return !Inline || CompilerOracle::should_not_inline(method) || method->dont_inline();
|
||||
return !Inline || CompilerOracle::should_not_inline(method, CompLevel_full_optimization) || method->dont_inline();
|
||||
C2V_END
|
||||
|
||||
C2V_VMENTRY_0(jboolean, shouldInlineMethod,(JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
|
||||
|
||||
@ -234,7 +234,7 @@ bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_met
|
||||
return false;
|
||||
}
|
||||
|
||||
if (C->directive()->should_not_inline(callee_method)) {
|
||||
if (C->directive()->should_not_inline(callee_method, CompLevel_full_optimization)) {
|
||||
set_msg("disallowed by CompileCommand");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -872,11 +872,11 @@ WB_ENTRY(jboolean, WB_IsMethodCompiled(JNIEnv* env, jobject o, jobject method, j
|
||||
return !code->is_marked_for_deoptimization();
|
||||
WB_END
|
||||
|
||||
static bool is_excluded_for_compiler(AbstractCompiler* comp, methodHandle& mh) {
|
||||
static bool is_excluded_for_compiler(AbstractCompiler* comp, int comp_level, methodHandle& mh) {
|
||||
if (comp == nullptr) {
|
||||
return true;
|
||||
}
|
||||
CompilerDirectiveMatcher matcher(mh, comp);
|
||||
CompilerDirectiveMatcher matcher(mh, comp_level);
|
||||
return matcher.directive_set()->ExcludeOption;
|
||||
}
|
||||
|
||||
@ -902,8 +902,10 @@ WB_ENTRY(jboolean, WB_IsMethodCompilable(JNIEnv* env, jobject o, jobject method,
|
||||
// to exclude a compilation of 'method'.
|
||||
if (comp_level == CompLevel_any) {
|
||||
// Both compilers could have ExcludeOption set. Check all combinations.
|
||||
bool excluded_c1 = is_excluded_for_compiler(CompileBroker::compiler1(), mh);
|
||||
bool excluded_c2 = is_excluded_for_compiler(CompileBroker::compiler2(), mh);
|
||||
bool excluded_c1 = is_excluded_for_compiler(CompileBroker::compiler1(), CompLevel_simple, mh)
|
||||
&& is_excluded_for_compiler(CompileBroker::compiler1(), CompLevel_limited_profile, mh)
|
||||
&& is_excluded_for_compiler(CompileBroker::compiler1(), CompLevel_full_profile, mh);
|
||||
bool excluded_c2 = is_excluded_for_compiler(CompileBroker::compiler2(), CompLevel_full_optimization, mh);
|
||||
if (excluded_c1 && excluded_c2) {
|
||||
// Compilation of 'method' excluded by both compilers.
|
||||
return false;
|
||||
@ -914,9 +916,11 @@ WB_ENTRY(jboolean, WB_IsMethodCompilable(JNIEnv* env, jobject o, jobject method,
|
||||
return can_be_compiled_at_level(mh, is_osr, CompLevel_full_optimization);
|
||||
} else if (excluded_c2) {
|
||||
// C2 only has ExcludeOption set: Check if compilable with C1.
|
||||
return can_be_compiled_at_level(mh, is_osr, CompLevel_simple);
|
||||
return can_be_compiled_at_level(mh, is_osr, CompLevel_simple)
|
||||
|| can_be_compiled_at_level(mh, is_osr, CompLevel_limited_profile)
|
||||
|| can_be_compiled_at_level(mh, is_osr, CompLevel_full_profile);
|
||||
}
|
||||
} else if (comp_level > CompLevel_none && is_excluded_for_compiler(CompileBroker::compiler((int)comp_level), mh)) {
|
||||
} else if (comp_level > CompLevel_none && is_excluded_for_compiler(CompileBroker::compiler((int)comp_level), comp_level, mh)) {
|
||||
// Compilation of 'method' excluded by compiler used for 'comp_level'.
|
||||
return false;
|
||||
}
|
||||
@ -952,7 +956,7 @@ WB_ENTRY(jboolean, WB_IsIntrinsicAvailable(JNIEnv* env, jobject o, jobject metho
|
||||
compilation_context_id = reflected_method_to_jmid(thread, env, compilation_context);
|
||||
CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
|
||||
methodHandle cch(THREAD, Method::checked_resolve_jmethod_id(compilation_context_id));
|
||||
CompilerDirectiveMatcher matcher(cch, comp);
|
||||
CompilerDirectiveMatcher matcher(cch, compLevel);
|
||||
return comp->is_intrinsic_available(mh, matcher.directive_set());
|
||||
} else {
|
||||
// Calling with null matches default directive
|
||||
@ -1132,7 +1136,7 @@ bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThrea
|
||||
|
||||
// Check if compilation is blocking
|
||||
methodHandle mh(THREAD, method);
|
||||
CompilerDirectiveMatcher matcher(mh, comp);
|
||||
CompilerDirectiveMatcher matcher(mh, comp_level);
|
||||
bool is_blocking = !matcher.directive_set()->BackgroundCompilationOption;
|
||||
|
||||
// Compile method and check result
|
||||
@ -1151,7 +1155,7 @@ bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThrea
|
||||
} else if (mh->lookup_osr_nmethod_for(bci, comp_level, false) != nullptr) {
|
||||
return true;
|
||||
}
|
||||
tty->print("WB error: failed to %s compile at level %d method ", is_blocking ? "blocking" : "", comp_level);
|
||||
tty->print("WB error: failed to%s compile at level %d method ", is_blocking ? " blocking" : "", comp_level);
|
||||
mh->print_short_name(tty);
|
||||
tty->cr();
|
||||
if (is_blocking && is_queued) {
|
||||
@ -1184,7 +1188,7 @@ WB_ENTRY(jboolean, WB_ShouldPrintAssembly(JNIEnv* env, jobject o, jobject method
|
||||
CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
|
||||
|
||||
methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
|
||||
CompilerDirectiveMatcher matcher(mh, CompileBroker::compiler(comp_level));
|
||||
CompilerDirectiveMatcher matcher(mh, comp_level);
|
||||
return matcher.directive_set()->PrintAssemblyOption;
|
||||
WB_END
|
||||
|
||||
|
||||
@ -114,11 +114,16 @@ static int compare_methods(Method** a, Method** b) {
|
||||
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline CompLevel method_code_comp_level(const Method* m) {
|
||||
const nmethod* code = m->code();
|
||||
return code != nullptr ? static_cast<CompLevel>(code->comp_level()) : CompLevel_any;
|
||||
}
|
||||
|
||||
static void collect_profiled_methods(Method* m) {
|
||||
Thread* thread = Thread::current();
|
||||
methodHandle mh(thread, m);
|
||||
if ((m->method_data() != nullptr) &&
|
||||
(PrintMethodData || CompilerOracle::should_print(mh))) {
|
||||
(PrintMethodData || CompilerOracle::should_print(mh, method_code_comp_level(m)))) {
|
||||
collected_profiled_methods->push(m);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3194,7 +3194,7 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) {
|
||||
}
|
||||
}
|
||||
|
||||
CompilerDirectiveMatcher matcher(method, CompileBroker::compiler(CompLevel_simple));
|
||||
CompilerDirectiveMatcher matcher(method, CompLevel_simple);
|
||||
if (matcher.directive_set()->PrintAssemblyOption) {
|
||||
nm->print_code();
|
||||
}
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8313713
|
||||
* @summary Test if the following CompileCommand options support compilation
|
||||
* level bitmask argument: break, compileonly, exclude, print
|
||||
* @library /test/lib
|
||||
* @run main ${test.main.class}
|
||||
*/
|
||||
|
||||
package compiler.compilercontrol.commands;
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CompileLevelParseTest {
|
||||
private static final List<String> commandsWithCompileLevel = List.of("break", "compileonly", "exclude", "print");
|
||||
private static final List<String> compLevels = List.of("0", "1", "11", "111", "10", "100", "101", "1000", "1111");
|
||||
private static final List<String> invalidCompLevels = List.of("-9223372036854775808", "-1", "-1111", "10000", "2", "20000", "01012",
|
||||
"91", "9", "c1", "true", "false");
|
||||
private static final String DEFAULT_COMP_LEVEL = "1111";
|
||||
private static final String METHOD_EXP = "java/lang/Object.toString";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (String cmd : commandsWithCompileLevel) {
|
||||
ProcessTools.executeTestJava("-XX:CompileCommand=" + cmd + "," + METHOD_EXP, "-version")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldNotContain("CompileCommand: An error occurred during parsing")
|
||||
.shouldContain("CompileCommand: " + cmd + " " + METHOD_EXP + " intx " + cmd + " = " + DEFAULT_COMP_LEVEL); // should be registered
|
||||
for (String level : compLevels) {
|
||||
ProcessTools.executeTestJava("-XX:CompileCommand=" + cmd + "," + METHOD_EXP + "," + level, "-version")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldNotContain("CompileCommand: An error occurred during parsing")
|
||||
.shouldContain("CompileCommand: " + cmd + " " + METHOD_EXP + " intx " + cmd + " = " + level); // should be registered
|
||||
}
|
||||
// Note that values like "1suffix" are still accepted
|
||||
for (String incorrectLevel : invalidCompLevels) {
|
||||
ProcessTools.executeTestJava("-XX:CompileCommand=" + cmd + "," + METHOD_EXP + "," +incorrectLevel, "-version")
|
||||
.shouldHaveExitValue(1)
|
||||
.shouldContain("CompileCommand: An error occurred during parsing")
|
||||
.shouldNotContain("CompileCommand: " + cmd + " " + METHOD_EXP + " intx " + cmd + " = " + incorrectLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8313713
|
||||
* @summary Test -XX:CompileCommand=exclude and compileonly with different compilation levels,
|
||||
* monitoring compilation events in VM -XX:+PrintCompilation and -XX:+PrintTieredEvents output
|
||||
* @requires vm.compMode != "Xint" & vm.flavor == "server"
|
||||
* & (vm.opt.TieredStopAtLevel == 4 | vm.opt.TieredStopAtLevel == null)
|
||||
* & (vm.opt.CompilationMode == "normal" | vm.opt.CompilationMode == null)
|
||||
* @library /test/lib
|
||||
* @run main ${test.main.class} runner
|
||||
*/
|
||||
|
||||
package compiler.compilercontrol.commands;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.management.InputArguments;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CompileLevelPrintTest {
|
||||
static final Method TEST_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
TEST_METHOD = Testee.class.getDeclaredMethod("compiledMethod", new Class[] {int.class});
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
static final String TEST_METHOD_NAME_DOT = TEST_METHOD.getDeclaringClass().getName().replace('.', '/')
|
||||
+ "." + TEST_METHOD.getName();
|
||||
static final String TEST_METHOD_NAME_DBL_COLON = TEST_METHOD.getDeclaringClass().getName()
|
||||
+ "::" + TEST_METHOD.getName();
|
||||
static final String TEST_METHOD_SIGNATURE = TEST_METHOD_NAME_DBL_COLON + "(";
|
||||
|
||||
static final String TESTEE_WAITING_FOR_START_CMD = "==> waiting for start command";
|
||||
|
||||
static final String START_CMD = "start";
|
||||
static final String STOP_CMD = "stop";
|
||||
|
||||
static final boolean DEBUG_OUTPUT = false;
|
||||
|
||||
static int TIMEOUT_SEC = 30;
|
||||
|
||||
static class TesteeState {
|
||||
final CountDownLatch waitingForStartTest = new CountDownLatch(1);
|
||||
final AtomicInteger compiler1QueueSize = new AtomicInteger();
|
||||
final AtomicInteger compiler2QueueSize = new AtomicInteger(0);
|
||||
final Set<String> compileCommandsReported = Collections.synchronizedSet(new HashSet<>());
|
||||
volatile Set<String> testMethodCompiledAtLevel = Collections.synchronizedSet(new HashSet<>());
|
||||
final Set<String> testMethodExcludedAtLevel = Collections.synchronizedSet(new HashSet<>());
|
||||
volatile Set<String> testMethodPrintedAtLevel = Collections.synchronizedSet(new HashSet<>());
|
||||
volatile boolean testMethodMDOPrinted = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TesteeState{" +
|
||||
"\n compileCommandsReported=" + compileCommandsReported +
|
||||
"\n testMethodCompiledAtLevel=" + testMethodCompiledAtLevel +
|
||||
"\n testMethodExcludedAtLevel=" + testMethodExcludedAtLevel +
|
||||
"\n testMethodPrintedAtLevel=" + testMethodPrintedAtLevel +
|
||||
"\n testMethodMDOPrinted=" + testMethodMDOPrinted +
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
|
||||
// Use the same launcher to avoid double launch cost compared to multiple jtreg @test annotations
|
||||
if (args.length > 0 && "runner".equals(args[0])) {
|
||||
if (Arrays.asList(InputArguments.getVmInputArgs()).contains("-Xcomp")) {
|
||||
TIMEOUT_SEC *= 3;
|
||||
}
|
||||
|
||||
if (Arrays.asList(InputArguments.getVmInputArgs()).contains("-XX:-TieredCompilation")) {
|
||||
// If we have -XX:-TieredCompilation, we check only for C2 compilation
|
||||
// A space is printed instead of compile level
|
||||
Runner.run("compileonly", 1, 1, 1, Set.of(), Set.of("4"), true, false);
|
||||
Runner.run("compileonly", 10, 1, 1, Set.of(), Set.of("4"), true, false);
|
||||
Runner.run("compileonly", 100, 1, 1, Set.of(), Set.of("4"), true, false);
|
||||
Runner.run("compileonly", 1000, 1000, 4, Set.of(" "), Set.of(), true, false);
|
||||
Runner.run("compileonly", 1100, 1100, 4, Set.of(" "), Set.of(), true, false);
|
||||
|
||||
Runner.run("exclude", 1110, 1, 1, Set.of(), Set.of("4"), true, false);
|
||||
Runner.run("exclude", 1101, 10, 2, Set.of(), Set.of("4"), true, false);
|
||||
Runner.run("exclude", 1011, 100, 3, Set.of(), Set.of("4"), true, false);
|
||||
Runner.run("exclude", 111, 1000, 4, Set.of(" "), Set.of(), true, false);
|
||||
} else {
|
||||
// -XX:+TieredCompilation
|
||||
Runner.run("compileonly", 1, 1, 1, Set.of("1"), Set.of(), false, true);
|
||||
Runner.run("compileonly", 10, 10, 2, Set.of("2"), Set.of(), true, true);
|
||||
Runner.run("compileonly", 100, 100, 3, Set.of("3"), Set.of(), true, true);
|
||||
Runner.run("compileonly", 1000, 1000, 4, Set.of("4"), Set.of("3"), true, true);
|
||||
Runner.run("compileonly", 1100, 1100, 4, Set.of("3", "4"), Set.of(), true, true);
|
||||
|
||||
Runner.run("exclude", 1110, 1, 1, Set.of("1"), Set.of(), false, true);
|
||||
Runner.run("exclude", 1101, 10, 2, Set.of("2"), Set.of(), true, true);
|
||||
Runner.run("exclude", 1011, 100, 3, Set.of("3"), Set.of(), true, true);
|
||||
Runner.run("exclude", 111, 1000, 4, Set.of("4"), Set.of("3"), true, true);
|
||||
}
|
||||
} else if (args.length > 1 && "parse-logs".equals(args[0])) {
|
||||
// For test troubleshooting: if test has failed due to regexp matching,
|
||||
// try parsing testee-<pid>.out and hotspot_pid<pid>.log and adjust the patterns
|
||||
TesteeState testeeState = new TesteeState();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
Runner.matchMessagesInHotspotLog(args[i], testeeState);
|
||||
}
|
||||
IO.println(testeeState.toString());
|
||||
} else {
|
||||
Testee.run();
|
||||
}
|
||||
}
|
||||
|
||||
static class Runner {
|
||||
private static final int LAST_N_LINES_COUNT = 5;
|
||||
|
||||
private static final Pattern reCompileCommand = Pattern.compile(
|
||||
"CompileCommand: (.*)");
|
||||
private static final Pattern reTieredEvent = Pattern.compile(
|
||||
"[0-9.]+: \\[(call|loop|compile|force-compile|remove-from-queue|update-in-queue|reprofile|make-not-entrant) "
|
||||
+ "level=\\d \\[([^]]+)] @-?\\d+ queues=(\\d+),(\\d+).*]");
|
||||
private static final Pattern reCompilation = Pattern.compile(
|
||||
"(\\d+) (C1|C2|no compiler): *(\\d+) ([ %][ s][ !][ b][ n]) ([-0-4 ]) +([^ ]+).*");
|
||||
private static final Pattern reExcludeCompile = Pattern.compile(
|
||||
".*made not compilable on level (\\d) +([^ ]+) .* excluded by CompileCommand");
|
||||
private static final Pattern reCompiledMethod = Pattern.compile(
|
||||
".*-{35} Assembly -{35}\\n(?:\\[[0-9.]+s]\\[warning]\\[os] Loading hsdis library failed\\n)?\\nCompiled method \\((?:c1|c2)\\) (\\d+) ([Cc][12]): *"
|
||||
+ "(\\d+) ([ %][ s][ !][ b][ n]) ([-0-4 ]) +([^ ]+) +(@ -?[0-9]+ +)?\\(\\d+ bytes\\)", Pattern.DOTALL);
|
||||
private static final Pattern reMethodData = Pattern.compile(
|
||||
".*-{72}\\nstatic ([^\\n]+)\\n *interpreter_invocation_count: *\\d+\\n *invocation_counter: *\\d+", Pattern.DOTALL);
|
||||
private static final Pattern reEndOfLog = Pattern.compile("<hotspot_log_done .*/>");
|
||||
|
||||
public static void run(String compileCmd,
|
||||
int cmdCompLevel,
|
||||
int printCmdCompLevel,
|
||||
int tieredStopAtLevel,
|
||||
Set<String> expectedCompLevel,
|
||||
Set<String> expectExcludedAtLevels,
|
||||
boolean expectMDOPrinted,
|
||||
boolean tieredCompilation)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
IO.println("\n########> Testing " + compileCmd + " " + cmdCompLevel);
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:+PrintCompilation",
|
||||
"-XX:+CIPrintCompilerName",
|
||||
"-XX:+PrintTieredEvents",
|
||||
"-XX:+LogVMOutput",
|
||||
"-XX:+LogCompilation",
|
||||
"-XX:" + (tieredCompilation ? "+" : "-") + "TieredCompilation",
|
||||
"-XX:TieredStopAtLevel=" + tieredStopAtLevel,
|
||||
"-XX:CompileCommand=" + compileCmd + "," + TEST_METHOD_NAME_DBL_COLON + "," + cmdCompLevel,
|
||||
"-XX:CompileCommand=print," + TEST_METHOD_NAME_DBL_COLON + "," + printCmdCompLevel,
|
||||
CompileLevelPrintTest.class.getName());
|
||||
|
||||
try (Process process = pb.start();
|
||||
BufferedWriter processInput = process.outputWriter();
|
||||
BufferedReader processOutput = process.inputReader();
|
||||
BufferedReader processErrOut = process.errorReader()) {
|
||||
long startNanos = System.nanoTime();
|
||||
try {
|
||||
IO.println("##> Testee PID: " + process.pid());
|
||||
TesteeState testeeState = new TesteeState();
|
||||
|
||||
Thread stdoutParser = startDaemonThread(() ->
|
||||
matchVmMessages(processOutput, testeeState, "", "testee-" + process.pid() + ".out"));
|
||||
Thread stderrParser = startDaemonThread(() ->
|
||||
matchTesteeMessages(processErrOut, testeeState, "testee-" + process.pid() + ".err"));
|
||||
|
||||
IO.println("##> Waiting for testee to get ready for the start command");
|
||||
if (!testeeState.waitingForStartTest.await(TIMEOUT_SEC, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("No start signal from testee");
|
||||
}
|
||||
|
||||
Asserts.assertTrue(waitUntil(() -> !process.isAlive()
|
||||
|| (testeeState.compiler1QueueSize.get() < 5
|
||||
&& testeeState.compiler2QueueSize.get() < 5)),
|
||||
"Compiler queue is still not empty");
|
||||
Asserts.assertTrue(testeeState.compileCommandsReported.contains(
|
||||
compileCmd + " " + TEST_METHOD_NAME_DOT + " intx " + compileCmd + " = " + cmdCompLevel),
|
||||
"'CompileCommand: " + compileCmd + "...' was not printed");
|
||||
Asserts.assertTrue(testeeState.compileCommandsReported.contains(
|
||||
"print " + TEST_METHOD_NAME_DOT + " intx print = " + printCmdCompLevel),
|
||||
"'CompileCommand: print ...' was not printed");
|
||||
|
||||
IO.println("##> Order testee to start");
|
||||
processInput.write(START_CMD); processInput.newLine(); processInput.flush();
|
||||
|
||||
waitUntil(() -> !process.isAlive()
|
||||
|| (!expectedCompLevel.isEmpty() && !expectExcludedAtLevels.isEmpty()
|
||||
&& expectedCompLevel.equals(testeeState.testMethodCompiledAtLevel)
|
||||
&& expectedCompLevel.equals(testeeState.testMethodPrintedAtLevel)
|
||||
&& expectExcludedAtLevels.equals(testeeState.testMethodExcludedAtLevel)));
|
||||
|
||||
if (process.isAlive()) {
|
||||
IO.println("##> Required messages have been found in testee output, now stop it");
|
||||
processInput.write(STOP_CMD + "\n");
|
||||
processInput.flush();
|
||||
processInput.close();
|
||||
}
|
||||
|
||||
Asserts.assertEquals(0, process.waitFor());
|
||||
stdoutParser.join();
|
||||
stderrParser.join();
|
||||
|
||||
// Process stdout can be garbled: pieces of different messages can be intertwined and regexps may
|
||||
// intermittently fail to match the messages.
|
||||
// Now parse Hotspot log file to re-match them. Duplicates are OK.
|
||||
matchMessagesInHotspotLog("hotspot_pid" + process.pid() + ".log", testeeState);
|
||||
|
||||
Asserts.assertEquals(expectedCompLevel, testeeState.testMethodCompiledAtLevel,
|
||||
"Test method was not compiled at required level (" + expectedCompLevel + ")");
|
||||
Asserts.assertEquals(expectedCompLevel, testeeState.testMethodPrintedAtLevel,
|
||||
"Test method assembly was not printed at required level (" + expectedCompLevel + ")");
|
||||
Asserts.assertEquals(expectExcludedAtLevels, testeeState.testMethodExcludedAtLevel,
|
||||
"Test method compilation was not excluded at required levels (" + expectExcludedAtLevels + ")");
|
||||
Asserts.assertEquals(expectMDOPrinted, testeeState.testMethodMDOPrinted,
|
||||
"Test method MDO was" + (expectMDOPrinted ? " NOT" : "") + " printed");
|
||||
|
||||
IO.println("########> Test passed");
|
||||
} catch (Exception ex) {
|
||||
IO.println("########> Test failed");
|
||||
ex.printStackTrace();
|
||||
process.destroyForcibly();
|
||||
throw ex;
|
||||
} finally {
|
||||
IO.println("########> Elapsed " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos) + " ms");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void matchMessagesInHotspotLog(String logFileName, TesteeState testeeState) throws IOException {
|
||||
IO.println("##> Parsing " + logFileName + " to match possibly missed messages");
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(logFileName))) {
|
||||
matchVmMessages(reader, testeeState, "Log: ", null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void matchVmMessages(BufferedReader testeeOutput, TesteeState testeeState, String logPrefix, String fileName) {
|
||||
try (Writer outWriter = fileName != null ? new BufferedWriter(new FileWriter(fileName)) : null) {
|
||||
String line;
|
||||
LinkedList<String> lastNLines = new LinkedList<>();
|
||||
boolean endOfLog = false;
|
||||
|
||||
while (!endOfLog && (line = testeeOutput.readLine()) != null) {
|
||||
if (outWriter != null) {
|
||||
outWriter.write(line);
|
||||
outWriter.write('\n');
|
||||
}
|
||||
|
||||
line = line.trim();
|
||||
|
||||
lastNLines.addLast(line);
|
||||
while (lastNLines.size() > LAST_N_LINES_COUNT) {
|
||||
lastNLines.removeFirst();
|
||||
}
|
||||
String lastNLinesStr = String.join("\n", lastNLines);
|
||||
|
||||
Matcher matcher;
|
||||
String msg = "";
|
||||
|
||||
if ((matcher = reCompileCommand.matcher(line)).matches()) {
|
||||
testeeState.compileCommandsReported.add(matcher.group(1));
|
||||
|
||||
msg = "Compile command reported: " + matcher.group(1);
|
||||
|
||||
} else if ((matcher = reTieredEvent.matcher(line)).matches()) {
|
||||
testeeState.compiler1QueueSize.set(Integer.parseInt(matcher.group(3)));
|
||||
testeeState.compiler2QueueSize.set(Integer.parseInt(matcher.group(4)));
|
||||
|
||||
} else if ((matcher = reCompilation.matcher(line)).matches()) {
|
||||
if ("C1".equalsIgnoreCase(matcher.group(2))) {
|
||||
testeeState.compiler1QueueSize.decrementAndGet();
|
||||
} else {
|
||||
testeeState.compiler2QueueSize.decrementAndGet();
|
||||
}
|
||||
|
||||
if (matcher.group(6).contains(TEST_METHOD_NAME_DBL_COLON)) {
|
||||
testeeState.testMethodCompiledAtLevel.add(matcher.group(5));
|
||||
|
||||
msg = "Test method compiled:"
|
||||
+ " compiler=" + matcher.group(2)
|
||||
+ " level=" + matcher.group(5)
|
||||
+ " compilation#=" + matcher.group(3)
|
||||
+ " flags=" + matcher.group(4).trim()
|
||||
+ " name=" + matcher.group(6);
|
||||
}
|
||||
} else if ((matcher = reCompiledMethod.matcher(lastNLinesStr)).matches()) {
|
||||
if (matcher.group(6).contains(TEST_METHOD_NAME_DBL_COLON)) {
|
||||
testeeState.testMethodPrintedAtLevel.add(matcher.group(5));
|
||||
|
||||
msg = "Test method assembly printed:"
|
||||
+ " compiler=" + matcher.group(2)
|
||||
+ " level=" + matcher.group(5)
|
||||
+ " compilation#=" + matcher.group(3)
|
||||
+ " flags=" + matcher.group(4).trim()
|
||||
+ " name=" + matcher.group(6)
|
||||
+ " bci=" + (matcher.group(7) != null ? matcher.group(7).trim() : "");
|
||||
}
|
||||
} else if ((matcher = reExcludeCompile.matcher(line)).matches()) {
|
||||
if (matcher.group(2).contains(TEST_METHOD_NAME_DBL_COLON)) {
|
||||
testeeState.testMethodExcludedAtLevel.add(matcher.group(1));
|
||||
|
||||
msg = "Test method not compilable:"
|
||||
+ " level=" + matcher.group(1)
|
||||
+ " name=" + matcher.group(2);
|
||||
}
|
||||
} else if ((matcher = reMethodData.matcher(lastNLinesStr)).matches()) {
|
||||
if (matcher.group(1).contains(TEST_METHOD_SIGNATURE)) {
|
||||
testeeState.testMethodMDOPrinted = true;
|
||||
|
||||
msg = "Test method data:"
|
||||
+ " name=" + matcher.group(1);
|
||||
}
|
||||
} else if (reEndOfLog.matcher(line).matches()) {
|
||||
endOfLog = true;
|
||||
|
||||
msg = "End of log";
|
||||
}
|
||||
|
||||
if (!msg.isEmpty()) {
|
||||
msg = "##> " + logPrefix + msg;
|
||||
IO.println(msg);
|
||||
if (outWriter != null) {
|
||||
outWriter.write(msg);
|
||||
outWriter.write('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void matchTesteeMessages(BufferedReader testeeErrorOutput, TesteeState testeeState, String fileName) {
|
||||
try (BufferedWriter outWriter = new BufferedWriter(new FileWriter(fileName))) {
|
||||
String line;
|
||||
while ((line = testeeErrorOutput.readLine()) != null) {
|
||||
outWriter.write(line);
|
||||
outWriter.newLine();
|
||||
|
||||
line = line.trim();
|
||||
|
||||
if (TESTEE_WAITING_FOR_START_CMD.equals(line)) {
|
||||
IO.println("##> Testee is waiting for start command");
|
||||
testeeState.waitingForStartTest.countDown();
|
||||
} else if (line.startsWith("==>")) {
|
||||
IO.println(line);
|
||||
} else if (line.startsWith("Exception in thread ") || line.startsWith("at ")) {
|
||||
IO.println("==>" + line);
|
||||
} else if (DEBUG_OUTPUT) {
|
||||
IO.println("Did not parse stderr: " + line);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Testee {
|
||||
private static final CountDownLatch startCmd = new CountDownLatch(1);
|
||||
private static final CountDownLatch stopCmd = new CountDownLatch(1);
|
||||
|
||||
static void run() throws IOException, InterruptedException {
|
||||
System.err.println("==> entering testee()");
|
||||
|
||||
try {
|
||||
startDaemonThread(Testee::inputMonitor);
|
||||
|
||||
if (stopCmd.getCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Print 3 times, since the output can be intermixed with
|
||||
System.err.println(TESTEE_WAITING_FOR_START_CMD);
|
||||
if (!startCmd.await(TIMEOUT_SEC + 1, TimeUnit.SECONDS)) {
|
||||
System.err.println("==> 'start' command was not given in stdin");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stopCmd.getCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.err.println("==> starting test");
|
||||
runTestCode();
|
||||
} finally {
|
||||
System.err.println("==> exiting testee()");
|
||||
}
|
||||
}
|
||||
|
||||
private static void inputMonitor() {
|
||||
try (BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
|
||||
String line;
|
||||
while ((line = stdin.readLine()) != null) {
|
||||
line = line.trim();
|
||||
System.err.println("==> STDIN: " + line);
|
||||
|
||||
switch (line) {
|
||||
case START_CMD:
|
||||
startCmd.countDown();
|
||||
break;
|
||||
|
||||
case STOP_CMD:
|
||||
stopCmd.countDown();
|
||||
break;
|
||||
|
||||
default:
|
||||
System.err.println("==> ERROR: unknown command");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// For Tier4 600 invocation of this method with avg 25 loops for each should be enough
|
||||
// to trigger Tier4CompilationThreshold=15000
|
||||
private static void compiledMethod(final int a) {
|
||||
int r = 0;
|
||||
for (int i = 0; i < a % 50; i++) {
|
||||
r ^= i;
|
||||
}
|
||||
if (r == 42) {
|
||||
System.err.println("MAGIC!");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean longLoop() {
|
||||
// To trigger compilation, 100-200 should be enough for C1 and 600-700 for C2
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
compiledMethod(i);
|
||||
if ((i & 0xf) == 0 && stopCmd.getCount() == 0) {
|
||||
System.err.println("==> Bailing out of compiledMethod() at iteration " + i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void runTestCode() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (longLoop()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Thread startDaemonThread(Runnable code) {
|
||||
Thread t = new Thread(code);
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
return t;
|
||||
}
|
||||
|
||||
static boolean waitUntil(BooleanSupplier condition) throws InterruptedException {
|
||||
for (int maxWait = TIMEOUT_SEC * 5; maxWait > 0; --maxWait) {
|
||||
if (condition.getAsBoolean()) {
|
||||
return true;
|
||||
}
|
||||
Thread.sleep(200);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, 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
|
||||
@ -73,16 +73,16 @@ public class ClearDirectivesFileStackTest extends AbstractTestBase {
|
||||
// skip invalid command
|
||||
command = Command.COMPILEONLY;
|
||||
}
|
||||
CompileCommand compileCommand = new CompileCommand(command,
|
||||
CompileCommand compileCommand = new CompileCommand(command, true,
|
||||
methodDescriptor, cmdGen.generateCompiler(),
|
||||
Scenario.Type.DIRECTIVE);
|
||||
builder.add(compileCommand);
|
||||
}
|
||||
// clear the stack
|
||||
builder.add(new JcmdCommand(Command.NONEXISTENT, null, null,
|
||||
builder.add(new JcmdCommand(Command.NONEXISTENT, true, null, null,
|
||||
Scenario.Type.JCMD, Scenario.JcmdType.CLEAR));
|
||||
// print all directives after the clear
|
||||
builder.add(new JcmdCommand(Command.NONEXISTENT, null, null,
|
||||
builder.add(new JcmdCommand(Command.NONEXISTENT, true, null, null,
|
||||
Scenario.Type.JCMD, Scenario.JcmdType.PRINT));
|
||||
Scenario scenario = builder.build();
|
||||
scenario.execute();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user