Skip to content

[GR-61098] Make TrackDynamicAccess experimental and make Dynamic Access data structures public #11349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,7 @@ public static boolean hasDumpRuntimeCompiledMethodsSupport() {
return !Platform.includedIn(Platform.WINDOWS.class) && ConcealedOptions.DumpRuntimeCompiledMethods.getValue();
}

@Option(help = "file:doc-files/TrackDynamicAccessHelp.txt")//
@Option(help = "file:doc-files/TrackDynamicAccessHelp.txt", stability = OptionStability.EXPERIMENTAL)//
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> TrackDynamicAccess = new HostedOptionKey<>(
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ The analysis is limited to the provided comma-separated list of class-path entri
If any dynamic access calls are found, a "dynamic-access" directory is created in the native image output,
and the calls are serialized in "dynamic-access/<entry-name>/[reflection-calls.json][resource-calls.json]".

Usage: -H:TrackDynamicAccess=[all|none|to-console|path=<cp-entry>|module=<module>|package=<package>][,...]
Usage: -H:TrackDynamicAccess=[all|none|to-console|no-dump|path=<cp-entry>|module=<module>|package=<package>][,...]

The flag can be used in following ways:
1. -H:TrackDynamicAccess=all reports all dynamic access calls made across the entire project
Expand All @@ -12,7 +12,8 @@ The flag can be used in following ways:
4. -H:TrackDynamicAccess=package=<package> reports all dynamic access calls made from the specified package
5. -H:TrackDynamicAccess=none disables all previous selections for dynamic access detection
6. -H:TrackDynamicAccess=to-console outputs all detected dynamic access calls to the console
7. A comma-separated list of the previous cases. For example, -H:TrackDynamicAccess=path=<cp-entry>,module=<module>,package=<package>
7. -H:TrackDynamicAccess=no-dump disables the serialization of detected dynamic access calls
8. A comma-separated list of the previous cases. For example, -H:TrackDynamicAccess=path=<cp-entry>,module=<module>,package=<package>

Example of the option usage:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
@AutomaticallyRegisteredFeature
public final class DynamicAccessDetectionFeature implements InternalFeature {

private record MethodsByAccessKind(Map<DynamicAccessDetectionPhase.DynamicAccessKind, CallLocationsByMethod> methodsByAccessKind) {
public record MethodsByAccessKind(Map<DynamicAccessDetectionPhase.DynamicAccessKind, CallLocationsByMethod> methodsByAccessKind) {
MethodsByAccessKind() {
this(new ConcurrentSkipListMap<>());
}
Expand All @@ -85,7 +85,7 @@ public CallLocationsByMethod getCallLocationsByMethod(DynamicAccessDetectionPhas
}
}

private record CallLocationsByMethod(Map<String, ConcurrentLinkedQueue<String>> callLocationsByMethod) {
public record CallLocationsByMethod(Map<String, ConcurrentLinkedQueue<String>> callLocationsByMethod) {
CallLocationsByMethod() {
this(new ConcurrentSkipListMap<>());
}
Expand Down Expand Up @@ -113,6 +113,7 @@ public ConcurrentLinkedQueue<String> getMethodCallLocations(String methodName) {
private static final String OUTPUT_DIR_NAME = "dynamic-access";
private static final String TRACK_NONE = "none";
private static final String TO_CONSOLE = "to-console";
private static final String NO_DUMP = "no-dump";

private UnmodifiableEconomicSet<String> sourceEntries; // Class path entries and module or
// package names
Expand All @@ -122,6 +123,7 @@ public ConcurrentLinkedQueue<String> getMethodCallLocations(String methodName) {
private final OptionValues hostedOptionValues = HostedOptionValues.singleton();

private boolean printToConsole;
private boolean dumpJsonFiles = true;

public DynamicAccessDetectionFeature() {
callsBySourceEntry = new ConcurrentSkipListMap<>();
Expand Down Expand Up @@ -169,7 +171,7 @@ private void printReportForEntry(String entry) {
}
}

public static Path getOrCreateDirectory(Path directory) throws IOException {
private static Path getOrCreateDirectory(Path directory) throws IOException {
if (Files.exists(directory)) {
if (!Files.isDirectory(directory)) {
throw new NoSuchFileException(directory.toString(), null,
Expand Down Expand Up @@ -225,7 +227,9 @@ private static String toMethodJson(DynamicAccessDetectionPhase.DynamicAccessKind
public void reportDynamicAccess() {
for (String entry : sourceEntries) {
if (callsBySourceEntry.containsKey(entry)) {
dumpReportForEntry(entry);
if (dumpJsonFiles) {
dumpReportForEntry(entry);
}
if (printToConsole) {
printReportForEntry(entry);
}
Expand Down Expand Up @@ -299,10 +303,13 @@ public void afterRegistration(AfterRegistrationAccess access) {

AccumulatingLocatableMultiOptionValue.Strings options = SubstrateOptions.TrackDynamicAccess.getValue();
for (String optionValue : options.values()) {
if (optionValue.equals(TO_CONSOLE)) {
printToConsole = true;
} else if (optionValue.equals(TRACK_NONE)) {
printToConsole = false;
switch (optionValue) {
case TO_CONSOLE -> printToConsole = true;
case NO_DUMP -> dumpJsonFiles = false;
case TRACK_NONE -> {
printToConsole = false;
dumpJsonFiles = true;
}
}
}

Expand All @@ -324,8 +331,8 @@ public boolean isInConfiguration(IsInConfigurationAccess access) {
}

private static String dynamicAccessPossibleOptions() {
return String.format("[%s, %s, %s, %s]",
TRACK_ALL, TRACK_NONE, TO_CONSOLE, IncludeOptionsSupport.possibleExtendedOptions());
return String.format("[%s, %s, %s, %s, %s]",
TRACK_ALL, TRACK_NONE, TO_CONSOLE, NO_DUMP, IncludeOptionsSupport.possibleExtendedOptions());
}

public static void parseDynamicAccessOptions(EconomicMap<OptionKey<?>, Object> hostedValues, NativeImageClassLoaderSupport classLoaderSupport) {
Expand All @@ -340,8 +347,8 @@ public static void parseDynamicAccessOptions(EconomicMap<OptionKey<?>, Object> h
switch (option) {
case TRACK_ALL -> classLoaderSupport.setTrackAllDynamicAccess(valueWithOrigin);
case TRACK_NONE -> classLoaderSupport.clearDynamicAccessSelectors();
case TO_CONSOLE -> {
// This option is parsed later in the afterRegistration hook
case TO_CONSOLE, NO_DUMP -> {
// These options are parsed later in the afterRegistration hook
}
default -> parseIncludeSelector(optionArgument, valueWithOrigin, classLoaderSupport.getDynamicAccessSelectors(), IncludeOptionsSupport.ExtendedOption.parse(option),
dynamicAccessPossibleOptions());
Expand Down
Loading