Skip to content

Commit db851d3

Browse files
committed
Update RecordMetadata option to take comma-separated key-value pairs
1 parent 1583ddd commit db851d3

File tree

2 files changed

+113
-23
lines changed

2 files changed

+113
-23
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/metadata/MetadataTracer.java

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@
2828
import java.nio.file.Files;
2929
import java.nio.file.Path;
3030
import java.nio.file.Paths;
31+
import java.util.HashMap;
32+
import java.util.LinkedHashSet;
3133
import java.util.List;
34+
import java.util.Locale;
35+
import java.util.Map;
36+
import java.util.Set;
3237

3338
import org.graalvm.nativeimage.ImageSingletons;
3439
import org.graalvm.nativeimage.hosted.Feature;
@@ -39,6 +44,7 @@
3944
import com.oracle.svm.configure.UnresolvedConfigurationCondition;
4045
import com.oracle.svm.configure.config.ConfigurationSet;
4146
import com.oracle.svm.configure.config.ConfigurationType;
47+
import com.oracle.svm.core.SubstrateUtil;
4248
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4349
import com.oracle.svm.core.feature.InternalFeature;
4450
import com.oracle.svm.core.jdk.RuntimeSupport;
@@ -55,34 +61,34 @@
5561
/**
5662
* Implements reachability metadata tracing during native image execution. Enabling
5763
* {@link Options#MetadataTracingSupport} at build time will generate code to trace all accesses of
58-
* reachability metadata. When {@link Options#RecordMetadata} is specified at run time, the image
59-
* will trace and emit metadata to the specified path.
64+
* reachability metadata, and then the run-time option {@link Options#RecordMetadata} enables
65+
* tracing.
6066
*/
6167
public final class MetadataTracer {
6268

6369
public static class Options {
64-
@Option(help = "Enables the run-time code to trace reachability metadata accesses in the produced native image by using -XX:RecordMetadata=<path>.")//
70+
@Option(help = "Generate an image that supports reachability metadata access tracing. " +
71+
"When tracing is supported, use the -XX:RecordMetadata option to enable tracing at run time.")//
6572
public static final HostedOptionKey<Boolean> MetadataTracingSupport = new HostedOptionKey<>(false);
6673

67-
@Option(help = "The path of the directory to write traced metadata to. Metadata tracing is enabled only when this option is provided.")//
68-
public static final RuntimeOptionKey<String> RecordMetadata = new RuntimeOptionKey<>("");
74+
@Option(help = "file:doc-files/RecordMetadataHelp.txt")//
75+
public static final RuntimeOptionKey<String> RecordMetadata = new RuntimeOptionKey<>(null);
6976
}
7077

78+
private RecordOptions options;
7179
private volatile ConfigurationSet config;
7280

73-
private Path recordMetadataPath;
74-
7581
@Fold
7682
public static MetadataTracer singleton() {
7783
return ImageSingletons.lookup(MetadataTracer.class);
7884
}
7985

8086
/**
81-
* Returns whether tracing is enabled at run time (using {@code -XX:RecordMetadata=path}).
87+
* Returns whether tracing is enabled at run time (using {@code -XX:RecordMetadata}).
8288
*/
8389
public boolean enabled() {
8490
VMError.guarantee(Options.MetadataTracingSupport.getValue());
85-
return recordMetadataPath != null;
91+
return options != null;
8692
}
8793

8894
/**
@@ -159,20 +165,21 @@ public void traceSerializationType(String className) {
159165
}
160166
}
161167

162-
private static void initialize() {
168+
private static void initialize(String recordMetadataValue) {
163169
assert Options.MetadataTracingSupport.getValue();
164-
MetadataTracer singleton = MetadataTracer.singleton();
165-
String recordMetadataValue = Options.RecordMetadata.getValue();
166-
if (recordMetadataValue.isEmpty()) {
167-
throw new IllegalArgumentException("Empty path provided for " + Options.RecordMetadata.getName() + ".");
168-
}
169-
Path recordMetadataPath = Paths.get(recordMetadataValue);
170+
171+
RecordOptions parsedOptions = RecordOptions.parse(recordMetadataValue);
170172
try {
171-
Files.createDirectories(recordMetadataPath);
173+
Files.createDirectories(parsedOptions.path());
172174
} catch (IOException ex) {
173-
throw new IllegalArgumentException("Exception occurred creating the output directory for tracing (" + recordMetadataPath + ")", ex);
175+
throw new IllegalArgumentException("Exception occurred creating the output directory for tracing (" + parsedOptions.path() + ")", ex);
174176
}
175-
singleton.recordMetadataPath = recordMetadataPath;
177+
if (parsedOptions.mode() != RecordMode.DEFAULT) {
178+
throw new IllegalArgumentException("Mode " + parsedOptions.mode() + " is not yet supported.");
179+
}
180+
181+
MetadataTracer singleton = MetadataTracer.singleton();
182+
singleton.options = parsedOptions;
176183
singleton.config = new ConfigurationSet();
177184
}
178185

@@ -183,10 +190,10 @@ private static void shutdown() {
183190
singleton.config = null; // clear config so that shutdown events are not traced.
184191
if (config != null) {
185192
try {
186-
config.writeConfiguration(configFile -> singleton.recordMetadataPath.resolve(configFile.getFileName()));
193+
config.writeConfiguration(configFile -> singleton.options.path().resolve(configFile.getFileName()));
187194
} catch (IOException ex) {
188195
Log log = Log.log();
189-
log.string("Failed to write out reachability metadata to directory ").string(singleton.recordMetadataPath.toString());
196+
log.string("Failed to write out reachability metadata to directory ").string(singleton.options.path().toString());
190197
log.string(":").string(ex.getMessage());
191198
log.newline();
192199
}
@@ -200,7 +207,7 @@ static RuntimeSupport.Hook initializeMetadataTracingHook() {
200207
}
201208
VMError.guarantee(Options.MetadataTracingSupport.getValue());
202209
if (Options.RecordMetadata.hasBeenSet()) {
203-
initialize();
210+
initialize(Options.RecordMetadata.getValue());
204211
}
205212
};
206213
}
@@ -230,12 +237,86 @@ static RuntimeSupport.Hook checkImproperOptionUsageHook() {
230237
throw new IllegalArgumentException(
231238
"The option " + Options.RecordMetadata.getName() + " can only be used if metadata tracing is enabled at build time (using " +
232239
hostedOptionCommandArgument + ").");
233-
234240
}
235241
};
236242
}
237243
}
238244

245+
enum RecordMode {
246+
DEFAULT,
247+
CONDITIONAL
248+
}
249+
250+
record RecordOptions(Path path, RecordMode mode) {
251+
252+
static RecordOptions parse(String recordMetadataValue) {
253+
if (recordMetadataValue.isEmpty()) {
254+
throw parseError("Option " + MetadataTracer.Options.RecordMetadata.getName() + " cannot be empty");
255+
}
256+
257+
Map<String, String> parsedArguments = new HashMap<>();
258+
Set<String> allArguments = new LinkedHashSet<>(List.of("path", "mode"));
259+
for (String argument : recordMetadataValue.split(",")) {
260+
String[] parts = SubstrateUtil.split(argument, "=", 2);
261+
if (parts.length != 2) {
262+
throw badArgumentError(argument, "Argument should be a key-value pair separated by '='");
263+
} else if (!allArguments.contains(parts[0])) {
264+
throw badArgumentError(argument, "Argument key should be one of " + allArguments);
265+
} else if (parsedArguments.containsKey(parts[0])) {
266+
throw badArgumentError(argument, "Argument '" + parts[0] + "' was already specified with value '" + parsedArguments.get(parts[0]) + "'");
267+
} else if (parts[1].isEmpty()) {
268+
throw badArgumentError(argument, "Argument '" + parts[0] + "' cannot be empty");
269+
}
270+
parsedArguments.put(parts[0], parts[1]);
271+
}
272+
273+
String path = requiredArgument(parsedArguments, "path");
274+
String mode = optionalArgument(parsedArguments, "mode", "default");
275+
return new RecordOptions(Paths.get(path), parseMode(mode));
276+
}
277+
278+
private static IllegalArgumentException parseError(String message) {
279+
return new IllegalArgumentException(message + ". Sample usage: -XX:" + MetadataTracer.Options.RecordMetadata.getName() + "=path=<trace-output-directory>[,mode=<mode>]");
280+
}
281+
282+
private static IllegalArgumentException badArgumentError(String option, String message) {
283+
throw parseError("Bad argument provided for " + MetadataTracer.Options.RecordMetadata.getName() + ": '" + option + "'. " + message);
284+
}
285+
286+
private static String requiredArgument(Map<String, String> arguments, String key) {
287+
if (arguments.containsKey(key)) {
288+
return arguments.get(key);
289+
}
290+
throw parseError(MetadataTracer.Options.RecordMetadata.getName() + " missing required argument '" + key + "'");
291+
}
292+
293+
private static String optionalArgument(Map<String, String> options, String key, String defaultValue) {
294+
if (options.containsKey(key)) {
295+
return options.get(key);
296+
}
297+
return defaultValue;
298+
}
299+
300+
private static RecordMode parseMode(String modeValue) {
301+
try {
302+
return RecordMode.valueOf(modeValue.toUpperCase(Locale.ROOT));
303+
} catch (IllegalArgumentException ex) {
304+
StringBuilder message = new StringBuilder("Mode should be one of [");
305+
boolean first = true;
306+
for (RecordMode mode : RecordMode.values()) {
307+
if (first) {
308+
first = false;
309+
} else {
310+
message.append(", ");
311+
}
312+
message.append(mode.toString().toLowerCase(Locale.ROOT));
313+
}
314+
message.append("]");
315+
throw badArgumentError("mode=" + modeValue, message.toString());
316+
}
317+
}
318+
}
319+
239320
@AutomaticallyRegisteredFeature
240321
class MetadataTracerFeature implements InternalFeature {
241322
@Override
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Enables metadata tracing at run time. This option is only supported if -H:MetadataTracingSupport is enabled when building the image.
2+
The value of this option is a comma-separated list of arguments specified as key-value pairs. The following arguments are supported:
3+
4+
- path=<trace-output-directory> (required): Specifies the directory to write traced metadata to.
5+
- mode=<tracing-mode> (optional): Specifies the tracing mode, which can be one of:
6+
- default: Traces without conditions.
7+
- conditional: Traces and infers conditions for each metadata element. An element (a type, resource, etc.) is only reachable in the image if the associated condition is satisfied, which can reduce image size and improve analysis results.
8+
9+
Example usage: -H:RecordMetadata=path=tracing_output,mode=default

0 commit comments

Comments
 (0)