Skip to content

Commit 7e4ac57

Browse files
author
duke
committedMay 8, 2024
Automatic merge of jdk:master into master
2 parents b3fbec5 + ad78b7f commit 7e4ac57

7 files changed

+154
-51
lines changed
 

‎src/hotspot/share/compiler/compilationMemoryStatistic.cpp

+28-6
Original file line numberDiff line numberDiff line change
@@ -181,25 +181,32 @@ class MemStatEntry : public CHeapObj<mtInternal> {
181181
int _num_recomp;
182182
// Compiling thread. Only for diagnostic purposes. Thread may not be alive anymore.
183183
const Thread* _thread;
184+
// active limit for this compilation, if any
185+
size_t _limit;
184186

187+
// peak usage, bytes, over all arenas
185188
size_t _total;
189+
// usage in node arena when total peaked
186190
size_t _na_at_peak;
191+
// usage in resource area when total peaked
187192
size_t _ra_at_peak;
193+
// number of nodes (c2 only) when total peaked
188194
unsigned _live_nodes_at_peak;
189195
const char* _result;
190196

191197
public:
192198

193199
MemStatEntry(FullMethodName method)
194200
: _method(method), _comptype(compiler_c1),
195-
_time(0), _num_recomp(0), _thread(nullptr),
201+
_time(0), _num_recomp(0), _thread(nullptr), _limit(0),
196202
_total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0),
197203
_result(nullptr) {
198204
}
199205

200206
void set_comptype(CompilerType comptype) { _comptype = comptype; }
201207
void set_current_time() { _time = os::elapsedTime(); }
202208
void set_current_thread() { _thread = Thread::current(); }
209+
void set_limit(size_t limit) { _limit = limit; }
203210
void inc_recompilation() { _num_recomp++; }
204211

205212
void set_total(size_t n) { _total = n; }
@@ -218,14 +225,15 @@ class MemStatEntry : public CHeapObj<mtInternal> {
218225
st->print_cr(" RA : ...how much in resource areas");
219226
st->print_cr(" result : Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed");
220227
st->print_cr(" #nodes : ...how many nodes (c2 only)");
228+
st->print_cr(" limit : memory limit, if set");
221229
st->print_cr(" time : time of last compilation (sec)");
222230
st->print_cr(" type : compiler type");
223231
st->print_cr(" #rc : how often recompiled");
224232
st->print_cr(" thread : compiler thread");
225233
}
226234

227235
static void print_header(outputStream* st) {
228-
st->print_cr("total NA RA result #nodes time type #rc thread method");
236+
st->print_cr("total NA RA result #nodes limit time type #rc thread method");
229237
}
230238

231239
void print_on(outputStream* st, bool human_readable) const {
@@ -260,7 +268,19 @@ class MemStatEntry : public CHeapObj<mtInternal> {
260268
col += 8; st->fill_to(col);
261269

262270
// Number of Nodes when memory peaked
263-
st->print("%u ", _live_nodes_at_peak);
271+
if (_live_nodes_at_peak > 0) {
272+
st->print("%u ", _live_nodes_at_peak);
273+
} else {
274+
st->print("-");
275+
}
276+
col += 8; st->fill_to(col);
277+
278+
// Limit
279+
if (_limit > 0) {
280+
st->print(PROPERFMT " ", PROPERFMTARGS(_limit));
281+
} else {
282+
st->print("-");
283+
}
264284
col += 8; st->fill_to(col);
265285

266286
// TimeStamp
@@ -322,7 +342,7 @@ class MemStatTable :
322342

323343
void add(const FullMethodName& fmn, CompilerType comptype,
324344
size_t total, size_t na_at_peak, size_t ra_at_peak,
325-
unsigned live_nodes_at_peak, const char* result) {
345+
unsigned live_nodes_at_peak, size_t limit, const char* result) {
326346
assert_lock_strong(NMTCompilationCostHistory_lock);
327347
MemStatTableKey key(fmn, comptype);
328348
MemStatEntry** pe = get(key);
@@ -343,6 +363,7 @@ class MemStatTable :
343363
e->set_na_at_peak(na_at_peak);
344364
e->set_ra_at_peak(ra_at_peak);
345365
e->set_live_nodes_at_peak(live_nodes_at_peak);
366+
e->set_limit(limit);
346367
e->set_result(result);
347368
}
348369

@@ -430,6 +451,7 @@ void CompilationMemoryStatistic::on_end_compilation() {
430451
arena_stat->na_at_peak(),
431452
arena_stat->ra_at_peak(),
432453
arena_stat->live_nodes_at_peak(),
454+
arena_stat->limit(),
433455
result);
434456
}
435457
if (print) {
@@ -521,8 +543,8 @@ void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* aren
521543
if (ct != compiler_none && name[0] != '\0') {
522544
ss.print("%s %s: ", compilertype2name(ct), name);
523545
}
524-
ss.print("Hit MemLimit %s (limit: %zu now: %zu)",
525-
(hit_limit_before ? "again" : ""),
546+
ss.print("Hit MemLimit %s(limit: %zu now: %zu)",
547+
(hit_limit_before ? "again " : ""),
526548
arena_stat->limit(), arena_stat->peak_since_start());
527549
}
528550

‎src/hotspot/share/compiler/compilerOracle.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@
4242
#include "runtime/os.hpp"
4343
#include "utilities/parseInteger.hpp"
4444

45+
// Default compile commands, if defined, are parsed before any of the
46+
// explicitly defined compile commands. Thus, explicitly defined compile
47+
// commands take precedence over default compile commands. The effect is
48+
// as if the default compile commands had been specified at the start of
49+
// the command line.
50+
static const char* const default_compile_commands[] = {
51+
#ifdef ASSERT
52+
// In debug builds, impose a (generous) per-compilation memory limit
53+
// to catch pathological compilations during testing. The suboption
54+
// "crash" will cause the JVM to assert.
55+
//
56+
// Note: to disable the default limit at the command line,
57+
// set a limit of 0 (e.g. -XX:CompileCommand=MemLimit,*.*,0).
58+
"MemLimit,*.*,1G~crash",
59+
#endif
60+
nullptr };
61+
4562
static const char* optiontype_names[] = {
4663
#define enum_of_types(type, name) name,
4764
OPTION_TYPES(enum_of_types)
@@ -905,6 +922,14 @@ class LineCopy : StackObj {
905922
}
906923
};
907924

925+
bool CompilerOracle::parse_from_line_quietly(char* line) {
926+
const bool quiet0 = _quiet;
927+
_quiet = true;
928+
const bool result = parse_from_line(line);
929+
_quiet = quiet0;
930+
return result;
931+
}
932+
908933
bool CompilerOracle::parse_from_line(char* line) {
909934
if ((line[0] == '\0') || (line[0] == '#')) {
910935
return true;
@@ -1107,6 +1132,14 @@ bool CompilerOracle::parse_from_string(const char* str, bool (*parse_line)(char*
11071132

11081133
bool compilerOracle_init() {
11091134
bool success = true;
1135+
// Register default compile commands first - any commands specified via CompileCommand will
1136+
// supersede these default commands.
1137+
for (int i = 0; default_compile_commands[i] != nullptr; i ++) {
1138+
char* s = os::strdup(default_compile_commands[i]);
1139+
success = CompilerOracle::parse_from_line_quietly(s);
1140+
os::free(s);
1141+
assert(success, "default compile command \"%s\" failed to parse", default_compile_commands[i]);
1142+
}
11101143
if (!CompilerOracle::parse_from_string(CompileCommand, CompilerOracle::parse_from_line)) {
11111144
success = false;
11121145
}

‎src/hotspot/share/compiler/compilerOracle.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ class CompilerOracle : AllStatic {
179179
// Reads from string instead of file
180180
static bool parse_from_string(const char* option_string, bool (*parser)(char*));
181181
static bool parse_from_line(char* line);
182+
static bool parse_from_line_quietly(char* line);
182183
static bool parse_compile_only(char* line);
183184

184185
// Fast check if there is any option set that compile control needs to know about

‎test/hotspot/jtreg/compiler/c2/TestFindNode.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@
2727
* @requires vm.debug == true & vm.flavor == "server"
2828
* @summary Test which uses some special flags in order to test Node::find() in debug builds which could result in an endless loop or a stack overflow crash.
2929
*
30-
* @run main/othervm -Xbatch -XX:CompileCommand=option,*::*,bool,Vectorize,true -XX:+PrintOpto -XX:+TraceLoopOpts compiler.c2.TestFindNode
30+
* @run main/othervm -Xbatch -XX:CompileCommand=option,*::*,bool,Vectorize,true -XX:CompileCommand=memlimit,compiler.c2.TestFindNode::*,0
31+
* -XX:+PrintOpto -XX:+TraceLoopOpts compiler.c2.TestFindNode
3132
*/
3233
package compiler.c2;
3334

35+
// Note; we disable the implicit memory limit of 1G in debug JVMs until JDK-8331283 is fixed
36+
3437
public class TestFindNode {
3538
static volatile int[] iArr;
3639
static volatile int x;

‎test/hotspot/jtreg/compiler/loopopts/TestDeepGraphVerifyIterativeGVN.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,11 +28,14 @@
2828
* @summary Test which causes a stack overflow segmentation fault with -XX:VerifyIterativeGVN=1 due to a too deep recursion in Node::verify_recur().
2929
*
3030
* @run main/othervm/timeout=600 -Xcomp -XX:VerifyIterativeGVN=1 -XX:CompileCommand=compileonly,compiler.loopopts.TestDeepGraphVerifyIterativeGVN::*
31+
* -XX:CompileCommand=memlimit,compiler.loopopts.TestDeepGraphVerifyIterativeGVN::*,0
3132
* compiler.loopopts.TestDeepGraphVerifyIterativeGVN
3233
*/
3334

3435
package compiler.loopopts;
3536

37+
// Note; we disable the implicit memory limit of 1G in debug JVMs until JDK-8331295 is fixed
38+
3639
public class TestDeepGraphVerifyIterativeGVN
3740
{
3841
static volatile int[] iArr;

‎test/hotspot/jtreg/compiler/print/CompileCommandMemLimit.java

+64-34
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2024, Red Hat, Inc. All rights reserved.
3+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
34
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
45
*
56
* This code is free software; you can redistribute it and/or modify it
@@ -55,6 +56,7 @@
5556

5657
package compiler.print;
5758

59+
import jdk.test.lib.Platform;
5860
import jdk.test.lib.process.OutputAnalyzer;
5961
import jdk.test.lib.process.ProcessTools;
6062

@@ -64,8 +66,12 @@
6466

6567
public class CompileCommandMemLimit {
6668

69+
// Method we don't specify; default memlimit should apply
6770
final static String METHOD1 = "method1";
71+
// Method we explicitly limit to 4K limit
6872
final static String METHOD2 = "method2";
73+
// Method for which we explicitly disable a limit on the command line.
74+
final static String METHOD3 = "method3";
6975

7076
static boolean c2;
7177
static boolean test_crash;
@@ -77,27 +83,22 @@ public static void main(String[] args) throws Exception {
7783
default: throw new RuntimeException("invalid argument");
7884
}
7985
c2 = Boolean.parseBoolean(args[1]);
80-
test(METHOD1, METHOD2);
81-
test(METHOD2, METHOD1);
82-
}
83-
84-
private static void test(String include, String exclude) throws Exception {
85-
86-
// A method that is known to cost compilers a bit of memory to compile
8786

8887
List<String> options = new ArrayList<String>();
8988
options.add("-Xcomp");
9089
options.add("-XX:-Inline");
9190
options.add("-Xmx100m");
91+
options.add("-XX:-CreateCoredumpOnCrash");
9292
options.add("-XX:CompileCommand=compileonly," + getTestClass() + "::*");
93-
// We pass a very small size to guarantee the crash
94-
options.add("-XX:CompileCommand=MemStat," + getTestMethod(include) + ",print");
95-
if (test_crash) {
96-
options.add("-XX:CompileCommand=MemLimit," + getTestMethod(include) + ",4k~crash");
97-
options.add("-XX:-CreateCoredumpOnCrash");
98-
} else {
99-
options.add("-XX:CompileCommand=MemLimit," + getTestMethod(include) + ",4k");
100-
}
93+
94+
// We want a final report
95+
options.add("-XX:CompileCommand=MemStat,*.*,print");
96+
97+
// We limit method 2 to a very small limit that is guaranteed to trigger
98+
options.add("-XX:CompileCommand=MemLimit," + getTestMethod(METHOD2) + ",4k" + (test_crash ? "~crash" : ""));
99+
100+
// We disable any limit set on method 3
101+
options.add("-XX:CompileCommand=MemLimit," + getTestMethod(METHOD3) + ",0");
101102

102103
if (c2) {
103104
options.add("-XX:-TieredCompilation");
@@ -110,20 +111,22 @@ private static void test(String include, String exclude) throws Exception {
110111

111112
oa.reportDiagnosticSummary();
112113

113-
String expectedNameIncl = getTestMethod(include)
114-
.replace('.', '/')
115-
.replace("$", "\\$");
116-
String expectedNameExcl = getTestMethod(exclude)
117-
.replace('.', '/')
118-
.replace("$", "\\$");
119-
114+
String method1regex = testMethodNameForRegex(getTestMethod(METHOD1));
115+
String method2regex = testMethodNameForRegex(getTestMethod(METHOD2));
116+
String method3regex = testMethodNameForRegex(getTestMethod(METHOD3));
120117
String ct = c2 ? "c2" : "c1";
121118

122119
if (test_crash) {
123120
oa.shouldNotHaveExitValue(0);
124121
oa.shouldMatch("# *Internal Error.*");
125-
oa.shouldMatch("# *fatal error: " + ct + " *" + expectedNameIncl + ".*: Hit MemLimit .*limit: 4096.*");
126-
oa.shouldNotMatch(".*" + expectedNameExcl + ".*");
122+
123+
// method 2 should have hit its tiny limit
124+
oa.shouldMatch("# *fatal error: " + ct + " *" + method2regex + ".*: Hit MemLimit .*limit: 4096.*");
125+
126+
// none of the other ones should have hit a limit
127+
oa.shouldNotMatch(method1regex + ".*Hit MemLimit");
128+
oa.shouldNotMatch(method3regex + ".*Hit MemLimit");
129+
127130
// Make sure we get a non-zero-sized replay file (JDK-8331314)
128131
oa.shouldContain("# Compiler replay data is saved as:");
129132
String replayfile = oa.firstMatch("# (\\S+replay_pid\\d+\\.log)", 1);
@@ -137,18 +140,38 @@ private static void test(String include, String exclude) throws Exception {
137140
if (f.length() == 0) {
138141
throw new RuntimeException("Replayfile " + replayfile + " has size 0");
139142
}
140-
141143
} else {
142-
// Should see trace output when methods are compiled
143-
oa.shouldHaveExitValue(0)
144-
.shouldMatch(".*" + expectedNameIncl + ".*")
145-
.shouldNotMatch(".*" + expectedNameExcl + ".*");
144+
oa.shouldHaveExitValue(0);
145+
146+
// In debug builds we have an inbuilt MemLimit. It is very high, so we don't expect it to fire in this test.
147+
// But it will still show up in the final report.
148+
String implicitMemoryLimit = Platform.isDebugBuild() ? "1024M" : "-";
149+
150+
// With C2, we print number of nodes, with C1 we don't
151+
String numberNodesRegex = c2 ? "\\d+" : "-";
146152

147-
// Expect this log line
148-
oa.shouldMatch(".*" + expectedNameIncl + ".*Hit MemLimit.*");
153+
// method 2 should have hit its tiny limit
154+
oa.shouldMatch(ct + " " + method2regex + ".*: Hit MemLimit \\(limit: 4096 now: \\d+\\)");
149155

150-
// Expect final output to contain "oom"
151-
oa.shouldMatch(".*oom.*" + expectedNameIncl + ".*");
156+
// neither of the other ones should have hit a limit
157+
oa.shouldNotMatch(method1regex + ".*Hit MemLimit");
158+
oa.shouldNotMatch(method3regex + ".*Hit MemLimit");
159+
160+
// Final report:
161+
// Method 1 should show up as "ok" and with the default limit, e.g.
162+
// total NA RA result #nodes limit time type #rc thread method
163+
// 32728 0 32728 ok - 1024M 0.045 c1 1 0x000000011b019c10 compiler/print/CompileCommandMemLimit$TestMain::method1(()J)
164+
oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +" + numberNodesRegex + " +" + implicitMemoryLimit + " +.* +" + method1regex);
165+
166+
// Method 2 should show up as "oom" and with its tiny limit, e.g.
167+
// total NA RA result #nodes limit time type #rc thread method
168+
// 32728 0 32728 oom - 4096B 0.045 c1 1 0x000000011b019c10 compiler/print/CompileCommandMemLimit$TestMain::method1(()J)
169+
oa.shouldMatch("\\d+ +\\d+ +\\d+ +oom +" + numberNodesRegex + " +4096B +.* +" + method2regex);
170+
171+
// Method 3 should show up as "ok", and with no limit, even in debug builds, e.g.
172+
// total NA RA result #nodes limit time type #rc thread method
173+
// 32728 0 32728 ok - - 0.045 c1 1 0x000000011b019c10 compiler/print/CompileCommandMemLimit$TestMain::method1(()J)
174+
oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +" + numberNodesRegex + " +- +.* +" + method3regex);
152175
}
153176
}
154177

@@ -161,16 +184,23 @@ public static String getTestMethod(String method) {
161184
return getTestClass() + "::" + method;
162185
}
163186

187+
private static String testMethodNameForRegex(String m) {
188+
return m.replace('.', '/')
189+
.replace("$", "\\$");
190+
}
191+
164192
public static class TestMain {
165193
public static void main(String[] args) {
166194
method1();
167195
method2();
196+
method3();
168197
}
169198

170199
static long method1() {
171200
return System.currentTimeMillis();
172201
}
173202
static void method2() {}
203+
static void method3() {}
174204
}
175205
}
176206

0 commit comments

Comments
 (0)
Failed to load comments.