Skip to content

Race condition when outputting config during parallel execution of tests in agent mode #194

@lazar-mitrovic

Description

@lazar-mitrovic

At the moment, when native-image-agent is injected into JVM processes that are spawned for test executions we are specifying its output directory as a fixed path.
In Maven plugin this is done here:

static String buildAgentArgument(String baseDir, Context context, List<String> agentOptions) {
List<String> options = new ArrayList<>(agentOptions);
options.add("config-output-dir=" + agentOutputDirectoryFor(baseDir, context));
return "-agentlib:native-image-agent=" + String.join(",", options);
}

In Gradle:
@Override
public Iterable<String> asArguments() {
if (getEnabled().get()) {
File outputDir = getOutputDirectory().getAsFile().get();
List<String> agentOptions = new ArrayList<>(getAgentOptions().getOrElse(Collections.emptyList()));
if (agentOptions.stream().map(s -> s.split("=")[0]).anyMatch(s -> s.contains("config-output-dir"))) {
throw new IllegalStateException("config-output-dir cannot be supplied as an agent option");
}
agentOptions.add("config-output-dir=" + outputDir.getAbsolutePath());
return Arrays.asList(
"-agentlib:native-image-agent=" + String.join(",", agentOptions),
"-Dorg.graalvm.nativeimage.imagecode=agent"
);
}
return Collections.emptyList();
}
}

An issue that arises from this approach is that each parallel test execution overwrites older configuration.

Trivial solution for this would be change the invocation parameter to config-merge-dir, but that would (AFAIK) require that tests are being run sequentially which would hinder performance significantly. Potentially we could hack the agent itself to wait on some kind of file lock if user opted-in to that, but that won't land in a release anytime soon.

Proper solution would be to use {pid} and {datetime} placeholders for output directory (as described in this section of the documentation)

export JAVA_TOOL_OPTIONS="-agentlib:native-image-agent=config-output-dir=/path/to/config-output-dir-{pid}-{datetime}/"

and then merge configuration using native-image-configure tool after test execution is done.

Unfortunately, as documentation points out:

Note: The Native Image Configure Tool is only available if native-image is built via mx. 
This configuration tool is not part of any GraalVM distribution by default.

We might be able to provide ConfigurationTool as a separate Maven artifact, or re-implement it entirely using off-the-shelf JSON libraries (like what we are doing in Gradle currently in order to filter out Gradle internal classes that are leaking into configuration).
cc: @peter-hofer

Another consideration is whether we should switch to JAVA_TOOL_OPTIONS environment variable for agent configuration or is the current approach sufficient (e.g. is every JVM process that we spawn configured correctly?). On pro side that would massively simplify Maven configuration (that is currently manipulating xml dom to inject nodes), but this option might (?) be Hotspot specific and could as such prevent things like running tests using Espresso further down the line.

cc: @melix @alvarosanchez @vjovanov

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggradle-pluginRelated to Gradle pluginmaven-pluginRelated to Maven plugin

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions