Skip to content

Commit bfa9492

Browse files
committed
[GR-63495] Add support for general heap dumping to the compiler.
PullRequest: graal/20414
2 parents 78c54ff + 4e1b1dc commit bfa9492

File tree

15 files changed

+505
-189
lines changed

15 files changed

+505
-189
lines changed

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalSupportImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package jdk.graal.compiler.libgraal;
2626

2727
import java.io.Closeable;
28+
import java.io.IOException;
2829
import java.io.PrintStream;
2930
import java.lang.invoke.MethodHandle;
3031
import java.lang.invoke.MethodHandles;
@@ -160,6 +161,11 @@ public void processReferences() {
160161
LibGraalRuntime.processReferences();
161162
}
162163

164+
@Override
165+
public void dumpHeap(String outputFile, boolean live) throws IOException {
166+
VMRuntime.dumpHeap(outputFile, live);
167+
}
168+
163169
@Override
164170
public long getIsolateAddress() {
165171
return CurrentIsolate.getIsolate().rawValue();

compiler/src/jdk.graal.compiler.management/src/jdk/graal/compiler/management/JMXServiceProvider.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,22 @@
2626

2727
import static jdk.graal.compiler.serviceprovider.GraalServices.getCurrentThreadId;
2828

29+
import java.io.IOException;
2930
import java.lang.management.ManagementFactory;
31+
import java.nio.file.Files;
32+
import java.nio.file.Path;
3033
import java.util.List;
3134

35+
import com.sun.management.HotSpotDiagnosticMXBean;
3236
import jdk.graal.compiler.serviceprovider.JMXService;
3337
import jdk.graal.compiler.serviceprovider.ServiceProvider;
3438

3539
import com.sun.management.ThreadMXBean;
3640

41+
import javax.management.MBeanServer;
42+
3743
/**
38-
* Implementation of {@link JMXService} for JDK 11+.
44+
* Implementation of {@link JMXService}.
3945
*/
4046
@ServiceProvider(JMXService.class)
4147
public class JMXServiceProvider extends JMXService {
@@ -66,4 +72,49 @@ protected boolean isCurrentThreadCpuTimeSupported() {
6672
protected List<String> getInputArguments() {
6773
return ManagementFactory.getRuntimeMXBean().getInputArguments();
6874
}
75+
76+
/**
77+
* Name of the HotSpot Diagnostic MBean.
78+
*/
79+
private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
80+
81+
private volatile HotSpotDiagnosticMXBean hotspotMXBean;
82+
83+
@Override
84+
protected void dumpHeap(String outputFile, boolean live) throws IOException {
85+
initHotSpotMXBean();
86+
try {
87+
Path path = Path.of(outputFile);
88+
if (Files.exists(path) && Files.size(path) == 0) {
89+
Files.delete(path);
90+
}
91+
hotspotMXBean.dumpHeap(outputFile, live);
92+
} catch (RuntimeException re) {
93+
throw re;
94+
} catch (Exception exp) {
95+
throw new RuntimeException(exp);
96+
}
97+
}
98+
99+
private void initHotSpotMXBean() {
100+
if (hotspotMXBean == null) {
101+
synchronized (this) {
102+
if (hotspotMXBean == null) {
103+
hotspotMXBean = getHotSpotMXBean();
104+
}
105+
}
106+
}
107+
}
108+
109+
private static HotSpotDiagnosticMXBean getHotSpotMXBean() {
110+
try {
111+
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
112+
return ManagementFactory.newPlatformMXBeanProxy(server,
113+
HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class);
114+
} catch (RuntimeException re) {
115+
throw re;
116+
} catch (Exception exp) {
117+
throw new RuntimeException(exp);
118+
}
119+
}
69120
}

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/DumpPathTest.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
import java.nio.file.DirectoryStream;
2929
import java.nio.file.Files;
3030
import java.nio.file.Path;
31+
import java.util.ArrayList;
32+
import java.util.List;
3133

34+
import jdk.graal.compiler.core.GraalCompilerOptions;
3235
import org.graalvm.collections.EconomicMap;
3336
import org.junit.Test;
3437

@@ -53,33 +56,49 @@ public static Object snippet() {
5356
public void testDump() throws Exception {
5457
assumeManagementLibraryIsLoadable();
5558
try (TemporaryDirectory temp = new TemporaryDirectory("DumpPathTest")) {
56-
String[] extensions = new String[]{".cfg", ".bgv", ".graph-strings"};
59+
String[] extensions = {".cfg", ".bgv", ".graph-strings"};
5760
EconomicMap<OptionKey<?>, Object> overrides = OptionValues.newOptionMap();
5861
overrides.put(DebugOptions.DumpPath, temp.toString());
5962
overrides.put(DebugOptions.ShowDumpFiles, false);
6063
overrides.put(DebugOptions.PrintBackendCFG, true);
6164
overrides.put(DebugOptions.PrintGraph, PrintGraphTarget.File);
6265
overrides.put(DebugOptions.PrintCanonicalGraphStrings, true);
6366
overrides.put(DebugOptions.Dump, "*");
67+
overrides.put(GraalCompilerOptions.DumpHeapAfter, "<compilation>:Schedule");
6468
overrides.put(DebugOptions.MethodFilter, null);
6569

6670
try (AutoCloseable c = new TTY.Filter()) {
6771
// Generate dump files.
6872
test(new OptionValues(getInitialOptions(), overrides), "snippet");
6973
}
7074
// Check that IGV files got created, in the right place.
71-
checkForFiles(temp.path, extensions);
75+
List<Path> paths = checkForFiles(temp.path, extensions);
76+
List<Path> compilationHeapDumps = new ArrayList<>();
77+
List<Path> phaseHeapDumps = new ArrayList<>();
78+
for (Path path : paths) {
79+
String name = path.toString();
80+
if (name.endsWith(".compilation.hprof")) {
81+
compilationHeapDumps.add(path);
82+
} else if (name.endsWith(".hprof")) {
83+
phaseHeapDumps.add(path);
84+
}
85+
}
86+
87+
assertTrue(!compilationHeapDumps.isEmpty());
88+
assertTrue(!phaseHeapDumps.isEmpty());
7289
}
7390
}
7491

7592
/**
7693
* Check that the given directory contains file or directory names with all the given
7794
* extensions.
7895
*/
79-
private static void checkForFiles(Path directoryPath, String[] extensions) throws IOException {
96+
private static List<Path> checkForFiles(Path directoryPath, String[] extensions) throws IOException {
8097
String[] paths = new String[extensions.length];
98+
List<Path> result = new ArrayList<>();
8199
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath)) {
82100
for (Path filePath : stream) {
101+
result.add(filePath);
83102
String fileName = filePath.getFileName().toString();
84103
for (int i = 0; i < extensions.length; i++) {
85104
String extension = extensions[i];
@@ -97,5 +116,6 @@ private static void checkForFiles(Path directoryPath, String[] extensions) throw
97116
for (int i = 1; i < paths.length; i++) {
98117
assertTrue(paths[0].equals(paths[i]), paths[0] + " != " + paths[i]);
99118
}
119+
return result;
100120
}
101121
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/GraalCompiler.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package jdk.graal.compiler.core;
2626

27+
import java.io.IOException;
2728
import java.util.concurrent.TimeUnit;
2829

2930
import jdk.graal.compiler.code.CompilationResult;
@@ -35,8 +36,8 @@
3536
import jdk.graal.compiler.debug.DebugContext;
3637
import jdk.graal.compiler.debug.DebugContext.CompilerPhaseScope;
3738
import jdk.graal.compiler.debug.DebugOptions;
39+
import jdk.graal.compiler.debug.GraphFilter;
3840
import jdk.graal.compiler.debug.MemUseTrackerKey;
39-
import jdk.graal.compiler.debug.MethodFilter;
4041
import jdk.graal.compiler.debug.TTY;
4142
import jdk.graal.compiler.debug.TimerKey;
4243
import jdk.graal.compiler.lir.asm.CompilationResultBuilderFactory;
@@ -53,6 +54,7 @@
5354
import jdk.graal.compiler.phases.tiers.Suites;
5455
import jdk.graal.compiler.phases.tiers.TargetProvider;
5556
import jdk.graal.compiler.phases.util.Providers;
57+
import jdk.graal.compiler.serviceprovider.GraalServices;
5658
import jdk.vm.ci.meta.ProfilingInfo;
5759
import jdk.vm.ci.meta.ResolvedJavaMethod;
5860

@@ -157,10 +159,28 @@ public static <T extends CompilationResult> T compile(Request<T> r) {
157159
throw debug.handle(e);
158160
}
159161
checkForRequestedDelay(r.graph);
162+
163+
checkForHeapDump(r, debug);
164+
160165
return r.compilationResult;
161166
}
162167
}
163168

169+
/**
170+
* Checks if {@link GraalCompilerOptions#DumpHeapAfter} is enabled for the compilation in
171+
* {@code request} and if so, dumps the heap to a file specified by the debug context.
172+
*/
173+
private static <T extends CompilationResult> void checkForHeapDump(Request<T> request, DebugContext debug) {
174+
if (GraalCompilerOptions.DumpHeapAfter.matches(debug.getOptions(), null, request.graph)) {
175+
try {
176+
final String path = debug.getDumpPath(".compilation.hprof", false);
177+
GraalServices.dumpHeap(path, false);
178+
} catch (IOException e) {
179+
e.printStackTrace(System.out);
180+
}
181+
}
182+
}
183+
164184
/**
165185
* Support for extra processing of a crash triggered by {@link GraalCompilerOptions#CrashAt}.
166186
*/
@@ -239,18 +259,7 @@ private static String match(StructuredGraph graph, String methodPattern) {
239259
// Absence of methodPattern means match everything
240260
return graph.name != null ? graph.name : graph.method().format("%H.%n(%p)");
241261
}
242-
String label = null;
243-
if (graph.name != null && graph.name.contains(methodPattern)) {
244-
label = graph.name;
245-
}
246-
if (label == null) {
247-
ResolvedJavaMethod method = graph.method();
248-
MethodFilter filter = MethodFilter.parse(methodPattern);
249-
if (filter.matches(method)) {
250-
label = method.format("%H.%n(%p)");
251-
}
252-
}
253-
return label;
262+
return new GraphFilter(methodPattern).matchedLabel(graph);
254263
}
255264

256265
/**

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/GraalCompilerOptions.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import jdk.graal.compiler.options.OptionKey;
3131
import jdk.graal.compiler.options.OptionStability;
3232
import jdk.graal.compiler.options.OptionType;
33+
import jdk.graal.compiler.phases.PhaseFilterKey;
3334

3435
/**
3536
* Options related to {@link GraalCompiler}.
@@ -46,6 +47,15 @@ Pattern for method(s) that will trigger an exception when compiled.
4647
suffix will raise a bailout exception and a ':PermanentBailout'
4748
suffix will raise a permanent bailout exception.""", type = OptionType.Debug)
4849
public static final OptionKey<String> CrashAt = new OptionKey<>(null);
50+
@Option(help = """
51+
Emit a heap dump after each phase matching the given phase filter(s).
52+
53+
Use DumpPath or ShowDumpFiles to set or see where dumps are written.
54+
55+
The special phase name "<compilation>" means dump after compilation
56+
instead of after any specific phase.
57+
""" + PhaseFilterKey.HELP, type = OptionType.Debug)//
58+
public static final PhaseFilterKey DumpHeapAfter = new PhaseFilterKey(null, "<compilation>");
4959
@Option(help = "Treats compilation bailouts as compilation failures.", type = OptionType.User, stability = OptionStability.STABLE)
5060
public static final OptionKey<Boolean> CompilationBailoutAsFailure = new OptionKey<>(false);
5161
@Option(help = """

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/LibGraalSupport.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.graalvm.collections.EconomicMap;
2828

29+
import java.io.IOException;
2930
import java.io.PrintStream;
3031
import java.lang.annotation.ElementType;
3132
import java.lang.annotation.Retention;
@@ -112,6 +113,15 @@ public interface LibGraalSupport {
112113
*/
113114
void processReferences();
114115

116+
/**
117+
* Dumps the heap to {@code outputFile} in hprof format.
118+
*
119+
* @param live if true, performs a full GC first so that only live objects are dumped
120+
* @throws IOException if an IO error occurred dyring dumping
121+
* @throws UnsupportedOperationException if this operation is not supported.
122+
*/
123+
void dumpHeap(String outputFile, boolean live) throws IOException;
124+
115125
/**
116126
* Gets the address of the current isolate.
117127
*/

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/debug/DebugOptions.java

Lines changed: 1 addition & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -182,78 +182,7 @@ public enum OptimizationLogTarget {
182182
@Option(help = "Pattern for specifying scopes in which logging is enabled. " +
183183
"See the Dump option for the pattern syntax.", type = OptionType.Debug)
184184
public static final OptionKey<String> Log = new OptionKey<>(null);
185-
@Option(help = """
186-
Pattern for matching methods.
187-
The syntax for a pattern is:
188-
189-
SourcePatterns = SourcePattern ["," SourcePatterns] .
190-
SourcePattern = [ "~" ] [ Class "." ] method [ "(" [ Parameter { ";" Parameter } ] ")" ] .
191-
Parameter = Class | "int" | "long" | "float" | "double" | "short" | "char" | "boolean" .
192-
Class = { package "." } class .
193-
194-
Glob pattern matching (*, ?) is allowed in all parts of the source pattern.
195-
The "~" prefix negates the pattern.
196-
197-
Positive patterns are joined by an "or" operator: "A,B" matches anything
198-
matched by "A" or "B". Negative patterns are joined by "and not": "~A,~B"
199-
matches anything not matched by "A" and not matched by "B". "A,~B,~C,D"
200-
matches anything matched by "A" or "D" and not matched by "B" and not
201-
matched by "C".
202-
203-
A set of patterns containing negative patterns but no positive ones contains
204-
an implicit positive "*" pattern: "~A,~B" is equivalent to "*,~A,~B".
205-
206-
Examples of method filters:
207-
---------
208-
*
209-
210-
Matches all methods in all classes.
211-
---------
212-
canonical(CanonicalizerTool;LogicNode;LogicNode)
213-
214-
Matches all methods named "canonical", with the first parameter of type
215-
"CanonicalizerTool", and the second and third parameters of type
216-
"LogicNode".
217-
The packages of the parameter types are irrelevant.
218-
---------
219-
arraycopy(Object;;;;)
220-
221-
Matches all methods named "arraycopy", with the first parameter
222-
of type "Object", and four more parameters of any type. The
223-
packages of the parameter types are irrelevant.
224-
---------
225-
List.set
226-
227-
Matches all methods named "set" in a class whose simple name is "List".
228-
---------
229-
*List.set
230-
231-
Matches all methods named "set" in a class whose simple name ends with "List".
232-
---------
233-
jdk.graal.compiler.nodes.PhiNode.*
234-
235-
Matches all methods in the class "jdk.graal.compiler.nodes.PhiNode".
236-
---------
237-
jdk.graal.compiler.nodes.*.canonical
238-
239-
Matches all methods named "canonical" in classes in the package
240-
"jdk.graal.compiler.nodes".
241-
---------
242-
arraycopy,toString
243-
244-
Matches all methods named "arraycopy" or "toString", meaning that ',' acts
245-
as an "or" operator.
246-
---------
247-
java.util.*.*.,~java.util.*Array*.*
248-
java.util.*.*.,~*Array*.*
249-
250-
These patterns are equivalent and match all methods in the package
251-
"java.util" except for classes that have "Array" in their name.
252-
---------
253-
~java.util.*.*
254-
255-
Matches all methods in all classes in all packages except for anything in
256-
the "java.util" package.""")
185+
@Option(help = jdk.graal.compiler.debug.MethodFilter.HELP)
257186
public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
258187
@Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug)
259188
public static final OptionKey<Boolean> MethodFilterRootOnly = new OptionKey<>(false);

0 commit comments

Comments
 (0)