Skip to content
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

Add maven support for agent #343

Merged
merged 27 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
46a4fc8
Add maven support for agent
dnestoro Oct 24, 2022
a53c71f
Refactor metadtaCopy mojo
dnestoro Nov 1, 2022
e2f9a00
Add agent modes support initial state
dnestoro Nov 16, 2022
6762611
Fix sample execution
dnestoro Nov 22, 2022
4aedfbe
Add abstract class for merge mojo and refactor test
dnestoro Nov 23, 2022
a06d0be
Provide mechanism for disabling stages and default output directory
dnestoro Nov 25, 2022
998f415
Fix few edge cases
dnestoro Nov 29, 2022
53deb62
Provide tests for maven agent support
dnestoro Nov 30, 2022
37a5a79
Fix style issues
dnestoro Dec 5, 2022
ef12742
Return accidentally deleted asciidoctor tags
dnestoro Dec 5, 2022
7314ba4
Add initial documentation for maven agent support
dnestoro Dec 6, 2022
a7627d8
Cover edge cases mainly for conditional mode
dnestoro Dec 13, 2022
242dc0a
Add root node null check
dnestoro Jan 12, 2023
ea12362
Fix edge case with disabled test stage
dnestoro Jan 12, 2023
33e5552
Fix bad configuration for direct mode
dnestoro Jan 17, 2023
a0351fd
Prevent merge mojo to be executed more than once
dnestoro Jan 17, 2023
33f2708
Cover missing default mode node edge case
dnestoro Jan 18, 2023
d077ac0
Rename commonOptions to options
dnestoro Jan 23, 2023
53ae5d3
Remove constructor and make AgentUtils class abstract
dnestoro Jan 23, 2023
45aae96
Remove deprecated tests
dnestoro Jan 23, 2023
2ffd010
Cover edge case when agent is enabled from command line but not confi…
dnestoro Jan 26, 2023
ca18336
Handle pom.xml parsing exceptions
dnestoro Jan 27, 2023
ef6faf2
Change error message for agent mode fail
dnestoro Jan 27, 2023
2c87527
Fix style error
dnestoro Jan 27, 2023
7ce317e
Make metadataCopy outputDirectory if not exists
dnestoro Feb 2, 2023
06890cb
Fix missing space between words
dnestoro Feb 2, 2023
bbc8b34
Add the documentation for the native agent
dnestoro Feb 2, 2023
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 @@ -49,21 +49,33 @@ public class AgentConfiguration implements Serializable {

private final Collection<String> callerFilterFiles;
private final Collection<String> accessFilterFiles;
private final boolean builtinCallerFilter;
private final boolean builtinHeuristicFilter;
private final boolean experimentalPredefinedClasses;
private final boolean experimentalUnsafeAllocationTracing;
private final boolean trackReflectionMetadata;
private final Boolean builtinCallerFilter;
private final Boolean builtinHeuristicFilter;
private final Boolean experimentalPredefinedClasses;
private final Boolean experimentalUnsafeAllocationTracing;
private final Boolean trackReflectionMetadata;

private final AgentMode agentMode;

// This constructor should be used only to specify that we have instance of agent that is disabled (to avoid using null for agent enable check)
public AgentConfiguration(AgentMode ...modes) {
this.callerFilterFiles = null;
this.accessFilterFiles = null;
this.builtinCallerFilter = null;
this.builtinHeuristicFilter = null;
this.experimentalPredefinedClasses = null;
this.experimentalUnsafeAllocationTracing = null;
this.trackReflectionMetadata = null;
this.agentMode = modes.length == 1 ? modes[0] : new DisabledAgentMode();
}

public AgentConfiguration(Collection<String> callerFilterFiles,
Collection<String> accessFilterFiles,
boolean builtinCallerFilter,
boolean builtinHeuristicFilter,
boolean experimentalPredefinedClasses,
boolean experimentalUnsafeAllocationTracing,
boolean trackReflectionMetadata,
Boolean builtinCallerFilter,
Boolean builtinHeuristicFilter,
Boolean experimentalPredefinedClasses,
Boolean experimentalUnsafeAllocationTracing,
Boolean trackReflectionMetadata,
AgentMode agentMode) {
this.callerFilterFiles = callerFilterFiles;
this.accessFilterFiles = accessFilterFiles;
Expand All @@ -79,11 +91,11 @@ public List<String> getAgentCommandLine() {
List<String> cmdLine = new ArrayList<>(agentMode.getAgentCommandLine());
appendOptionToValues("caller-filter-file=", callerFilterFiles, cmdLine);
appendOptionToValues("access-filter-file=", accessFilterFiles, cmdLine);
cmdLine.add("builtin-caller-filter=" + builtinCallerFilter);
cmdLine.add("builtin-heuristic-filter=" + builtinHeuristicFilter);
cmdLine.add("experimental-class-define-support=" + experimentalPredefinedClasses);
cmdLine.add("experimental-unsafe-allocation-support=" + experimentalUnsafeAllocationTracing);
cmdLine.add("track-reflection-metadata=" + trackReflectionMetadata);
addToCmd("builtin-caller-filter=", builtinCallerFilter, cmdLine);
addToCmd("builtin-heuristic-filter=", builtinHeuristicFilter, cmdLine);
addToCmd("experimental-class-define-support=", experimentalPredefinedClasses, cmdLine);
addToCmd("experimental-unsafe-allocation-support=", experimentalUnsafeAllocationTracing, cmdLine);
addToCmd("track-reflection-metadata=", trackReflectionMetadata, cmdLine);
return cmdLine;
}

Expand All @@ -100,10 +112,19 @@ public boolean isEnabled() {
}

public static void appendOptionToValues(String option, Collection<String> values, Collection<String> target) {
values.stream().map(value -> option + value).forEach(target::add);
if (values != null) {
values.stream().map(value -> option + value).forEach(target::add);
}
}

public AgentMode getAgentMode() {
return agentMode;
}

private void addToCmd(String option, Boolean value, List<String> cmdLine) {
if (value != null) {
cmdLine.add(option + value);
}
}

}
102 changes: 52 additions & 50 deletions docs/src/docs/asciidoc/maven-plugin.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ The Native Image Maven plugin simplifies generation of the required configuratio
injecting the agent automatically for you (this includes, but is not limited to the
reflection file).

The agent generates the native configuration files in a subdirectory of
The agent generates the native configuration files in subdirectories of
`target/native/agent-output`. Although those files will be automatically used if you run
your build with the agent enabled, you should consider reviewing the generated files and
adding them to your sources instead.
Expand Down Expand Up @@ -514,47 +514,56 @@ line by supplying the `-Dagent=false` flag.
=== Configuring agent options

If you would like to configure the options for the agent -- for example, to configure
experimental features such as `experimental-class-loader-support` or advanced features
agent mode or advanced features
such as
https://www.graalvm.org/reference-manual/native-image/Agent/#caller-based-filters[Caller-based Filters]
and https://www.graalvm.org/reference-manual/native-image/Agent/#access-filters[Access Filters]
-- you can include `<options>` within the `<agent>` block of the configuration of the
`native-maven-plugin` in your POM.

* You can supply multiple sets of `<options>`.
* You can declare an unnamed `<options>` element which will always be used whenever the
agent is enabled. This should be used to declare common options that will be used for
all executions with the agent.
* Additional `<options>` elements must declare a unique `name` attribute.
- To configure options for your application, use the name `main`.
- To configure options for your tests, use the name `test`.
- To configure additional sets of options, declare each with a unique name other than
`main` or `test`.
* The `main` options are enabled automatically whenever your application is run with the
agent.
* The `test` options are enabled automatically whenever your tests are run with the agent.
* To enable any other set of named `<options>`, supply `-DagentOptions=<NAME>` as a
command-line argument for Maven, where `<NAME>` corresponds to the `name` attribute of
the `<options>` element.

[WARNING]
====
The Native Image Maven plugin automatically configures the `config-output-dir` for the
agent. An attempt to configure a custom value for the `config-output-dir` option will
therefore result in a build failure.
====
https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/#caller-based-filters[Caller-based Filters]
and https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/#access-filters[Access Filters]
- you can specify them in your POM as described bellow.

[source,xml,indent=0]
include::../snippets/maven/pom.xml[tag=native-plugin-agent-configuration]

This example shows all possibilities you can use for native agent configuration. If you don't need some of the options,
just remove them.

The following example is likely more complex than anything you would do in your own
projects, but it demonstrates how to configure four sets of `<options>`.
Agent can be run in one of the following modes:

* The unnamed set is always active.
* The `main` set is automatically active for application execution.
* The `test` set is automatically active for test execution.
* The `periodic-config` set is never active by default, but it can be enabled via
`-DagentOptions=periodic-config` on the command line.
* `standard` - in this mode you run agent only with options provided in `options` section.
* `direct` - in this mode you can provide command line that you want to be executed as an agent configuration. In this
mode, user is fully responsible for agent configuration, and the rest of the agent configuration, provided in pom.xml file, will be ignored.
* `conditional` - in this mode you can provide additional files that can be used as a filter for the agent output. You
can read more about conditional mode https://www.graalvm.org/latest/reference-manual/native-image/metadata/ExperimentalAgentOptions/[here].

Each option and how you should use it is described bellow:

* `enabled` - a simple flag that specifies whether the agent is enabled or not. Can be set to true or false.
* `defaultMode` - agent mode switcher. Can be set to: standard, direct or conditional.
* `modes` - list of additional mode options, specific for certain mode type. Inside this tag, you can specify options for
direct or conditional modes. Standard mode doesn't have any specific options.
** in case of `direct` mode you can specify `<direct>` tag, with the agent command line as its value
** in case of `conditional` mode you can specify `<conditional>` tag, and set additional https://github.com/oracle/graalvm-reachability-metadata/blob/master/docs/CollectingMetadata.md[filter files]
inside `<userCodeFilterPath>` and `<extraCodeFilterPath>`. Also, you can set value for `<parallel>` tag. If the value is true, agent will create
partial-config file, and merge it with conditional merge, otherwise agent will generate same kind of output as other modes.
* `options` - list of options that can be specify independent from agent mode. More about common options can be found
https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/[here].


[[metadata-copy]]
==== Metadata copy
`metadataCopy` provides additional options for manipulating the agent output after agent finishes its job.

[source,xml,indent=0]
include::../../../../samples/java-application-with-reflection/pom.xml[tag=native-plugin-agent-options]
include::../snippets/maven/metadataCopy.xml[tag=native-plugin-agent-metadata-copy]

You can set values for the following tags:

* `<outputDirectory>` - where you want to copy agent output.
* `<merge>` - in case you already have some other config files inside `output directory`, you can choose whether you want to override those files or
merge new files with the existing once (set merge value to true).
* `<disabledStages>` - in case you don't want to copy output of the certain stage (main or test) you can disable them and metadataCopy will not look at
the agent output for that stage. For example, if you want to copy only config files generated in tests, you can disable main stage. Also, if you want
to copy only files generated in main phase, you can disable test stage. Therefore, if you skip both stages, metadataCopy will not be executed.


[[agent-support-running-tests]]
=== Running tests with the agent
Expand All @@ -572,13 +581,14 @@ When the `agent` system property is set to `true` (or when the agent is
to your Maven Surefire test execution, and the generated files can be found in the
`target/native/agent-output/test` directory.

To run your tests with custom agent options, supply the `-DagentOptions=<NAME>`
command-line argument to Maven as follows. See the documentation for
<<agent-support-configuring-options, agent options>> for details.

[TIP]
====
If you want to run metadataCopy as well, first define its configuration as described <<metadata-copy, above>> and add `native:metadata-copy`
at the end of the agent invocation command. Example:
```bash
mvn -Pnative -Dagent=true -DagentOptions=periodic-config test
mvn -Pnative -Dagent=true test native:metadata-copy
```
====

[[agent-support-running-application]]
=== Running your application with the agent
Expand All @@ -597,14 +607,6 @@ Then you can execute your application with the agent by running:
mvn -Pnative -Dagent=true -DskipTests -DskipNativeBuild=true package exec:exec@java-agent
```

To execute your application with custom agent options, supply the `-DagentOptions=<NAME>`
command-line argument to Maven as follows. See the documentation for
<<agent-support-configuring-options, agent options>> for details.

```bash
mvn -Pnative -Dagent=true -DagentOptions=periodic-config -DskipTests -DskipNativeBuild=true package exec:exec@java-agent
```

Both of the above commands will generate configuration files in the
`target/native/agent-output/main` directory. If you want to run your native application
with those configuration files, you then need to execute the following command:
Expand Down
9 changes: 9 additions & 0 deletions docs/src/docs/snippets/maven/metadataCopy.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- tag::native-plugin-agent-metadata-copy[] -->
<metadataCopy>
<disabledStages>
<stage>main</stage>
</disabledStages>
<merge>true</merge>
<outputDirectory>/tmp/test-output-dir</outputDirectory>
</metadataCopy>
<!-- end::native-plugin-agent-metadata-copy[] -->
36 changes: 36 additions & 0 deletions docs/src/docs/snippets/maven/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!-- tag::native-plugin-agent-configuration[] -->
<configuration>
<agent>
<enabled>true</enabled>
<defaultMode>Standard</defaultMode>
<modes>
<direct>config-output-dir=${project.build.directory}/native/agent-output</direct>
<conditional>
<userCodeFilterPath>user-code-filter.json</userCodeFilterPath>
<extraFilterPath>extra-filter.json</extraFilterPath>
<parallel>true</parallel>
</conditional>
</modes>
<options>
<callerFilterFiles>
<filterFile>caller-filter-file1.json</filterFile>
<filterFile>caller-filter-file2.json</filterFile>
</callerFilterFiles>
<accessFilterFiles>
<filterFile>access-filter-file1.json</filterFile>
<filterFile>access-filter-file2.json</filterFile>
</accessFilterFiles>
<builtinCallerFilter>true</builtinCallerFilter>
<builtinHeuristicFilter>true</builtinHeuristicFilter>
<enableExperimentalPredefinedClasses>true</enableExperimentalPredefinedClasses>
<enableExperimentalUnsafeAllocationTracing>
true
</enableExperimentalUnsafeAllocationTracing>
<trackReflectionMetadata>true</trackReflectionMetadata>
</options>
<metadataCopy>
<!-- you can specify metadataCopy configuration here -->
</metadataCopy>
</agent>
</configuration>
<!-- end::native-plugin-agent-configuration[] -->
Loading