Skip to content

Commit a713c28

Browse files
committed
Support -H:+MetadataTracing with an empty configuration file
1 parent f99b2be commit a713c28

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.metadata;
26+
27+
import java.io.IOException;
28+
import java.nio.file.Files;
29+
import java.nio.file.Path;
30+
import java.nio.file.Paths;
31+
import java.util.List;
32+
33+
import org.graalvm.nativeimage.ImageSingletons;
34+
import org.graalvm.nativeimage.hosted.Feature;
35+
36+
import com.oracle.svm.configure.config.ConfigurationSet;
37+
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
38+
import com.oracle.svm.core.feature.InternalFeature;
39+
import com.oracle.svm.core.jdk.RuntimeSupport;
40+
import com.oracle.svm.core.jdk.RuntimeSupportFeature;
41+
import com.oracle.svm.core.log.Log;
42+
import com.oracle.svm.core.option.HostedOptionKey;
43+
import com.oracle.svm.core.option.RuntimeOptionKey;
44+
import com.oracle.svm.core.option.SubstrateOptionsParser;
45+
import com.oracle.svm.core.util.VMError;
46+
47+
import jdk.graal.compiler.api.replacements.Fold;
48+
import jdk.graal.compiler.options.Option;
49+
50+
/**
51+
* Implements reachability metadata tracing during native image execution. Enabling
52+
* {@link Options#MetadataTracingSupport} at build time will generate code to trace all accesses of
53+
* reachability metadata. When {@link Options#RecordMetadata} is specified at run time, the image
54+
* will trace and emit metadata to the specified path.
55+
*/
56+
public final class MetadataTracer {
57+
58+
public static class Options {
59+
@Option(help = "Enables the run-time code to trace reachability metadata accesses in the produced native image by using -XX:RecordMetadata=<path>.")//
60+
public static final HostedOptionKey<Boolean> MetadataTracingSupport = new HostedOptionKey<>(false);
61+
62+
@Option(help = "The path of the directory to write traced metadata to. Metadata tracing is enabled only when this option is provided.")//
63+
public static final RuntimeOptionKey<String> RecordMetadata = new RuntimeOptionKey<>("");
64+
}
65+
66+
private ConfigurationSet config;
67+
68+
private Path recordMetadataPath;
69+
70+
@Fold
71+
public static MetadataTracer singleton() {
72+
return ImageSingletons.lookup(MetadataTracer.class);
73+
}
74+
75+
private static void initialize() {
76+
assert Options.MetadataTracingSupport.getValue();
77+
MetadataTracer singleton = MetadataTracer.singleton();
78+
String recordMetadataValue = Options.RecordMetadata.getValue();
79+
if (recordMetadataValue.isEmpty()) {
80+
throw new IllegalArgumentException("Empty path provided for " + Options.RecordMetadata.getName() + ".");
81+
}
82+
Path recordMetadataPath = Paths.get(recordMetadataValue);
83+
try {
84+
Files.createDirectories(recordMetadataPath);
85+
} catch (IOException ex) {
86+
throw new IllegalArgumentException("Exception occurred creating the output directory for tracing (" + recordMetadataPath + ")", ex);
87+
}
88+
singleton.recordMetadataPath = recordMetadataPath;
89+
singleton.config = new ConfigurationSet();
90+
}
91+
92+
private static void shutdown() {
93+
assert Options.MetadataTracingSupport.getValue();
94+
MetadataTracer singleton = MetadataTracer.singleton();
95+
ConfigurationSet config = singleton.config;
96+
if (config != null) {
97+
try {
98+
config.writeConfiguration(configFile -> singleton.recordMetadataPath.resolve(configFile.getFileName()));
99+
} catch (IOException ex) {
100+
Log log = Log.log();
101+
log.string("Failed to write out reachability metadata to directory ").string(singleton.recordMetadataPath.toString());
102+
log.string(":").string(ex.getMessage());
103+
log.newline();
104+
}
105+
}
106+
}
107+
108+
static RuntimeSupport.Hook initializeMetadataTracingHook() {
109+
return isFirstIsolate -> {
110+
if (!isFirstIsolate) {
111+
return;
112+
}
113+
VMError.guarantee(Options.MetadataTracingSupport.getValue());
114+
if (Options.RecordMetadata.hasBeenSet()) {
115+
initialize();
116+
}
117+
};
118+
}
119+
120+
static RuntimeSupport.Hook shutDownMetadataTracingHook() {
121+
return isFirstIsolate -> {
122+
if (!isFirstIsolate) {
123+
return;
124+
}
125+
VMError.guarantee(Options.MetadataTracingSupport.getValue());
126+
if (Options.RecordMetadata.hasBeenSet()) {
127+
shutdown();
128+
}
129+
};
130+
}
131+
132+
static RuntimeSupport.Hook checkImproperOptionUsageHook() {
133+
// Compute argument at build time (hosted option should not be reached in image code)
134+
String hostedOptionCommandArgument = SubstrateOptionsParser.commandArgument(Options.MetadataTracingSupport, "+");
135+
136+
return isFirstIsolate -> {
137+
if (!isFirstIsolate) {
138+
return;
139+
}
140+
VMError.guarantee(!Options.MetadataTracingSupport.getValue());
141+
if (Options.RecordMetadata.hasBeenSet()) {
142+
throw new IllegalArgumentException(
143+
"The option " + Options.RecordMetadata.getName() + " can only be used if metadata tracing is enabled at build time (using " +
144+
hostedOptionCommandArgument + ").");
145+
146+
}
147+
};
148+
}
149+
}
150+
151+
@AutomaticallyRegisteredFeature
152+
class MetadataTracerFeature implements InternalFeature {
153+
@Override
154+
public List<Class<? extends Feature>> getRequiredFeatures() {
155+
return List.of(RuntimeSupportFeature.class);
156+
}
157+
158+
@Override
159+
public void beforeAnalysis(BeforeAnalysisAccess access) {
160+
if (MetadataTracer.Options.MetadataTracingSupport.getValue()) {
161+
ImageSingletons.add(MetadataTracer.class, new MetadataTracer());
162+
RuntimeSupport.getRuntimeSupport().addInitializationHook(MetadataTracer.initializeMetadataTracingHook());
163+
RuntimeSupport.getRuntimeSupport().addTearDownHook(MetadataTracer.shutDownMetadataTracingHook());
164+
} else {
165+
RuntimeSupport.getRuntimeSupport().addInitializationHook(MetadataTracer.checkImproperOptionUsageHook());
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)