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 premain for static agent support #8988

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ziyilin
Copy link
Contributor

@ziyilin ziyilin commented May 29, 2024

Java agent always has a premain method to initialize the agent. SVM needs the premain as well to support agent in native image.

At compile time, -H:PremainClasses= option is used to set the premain classes.
At runtime, premain runtime options are set along with main class' arguments in the format of -XX-premain:[class]:[options]

This PR is part of #8077

@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label May 29, 2024

private static final String PREMAIN_OPTION_PREFIX = "-XX-premain:";

class PremainMethod {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a record, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be static if it is a class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a record, right?
Yes, in my current implementation it's recorded by native-image-agent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a record, right?

Yes, in my current implementation it's recorded by native-image-agent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I say a record I mean a java record.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Yes, this is a java record. I have fixed it.

// premain method must be static
premainMethod.method.invoke(null, args);
} catch (Throwable t) {
VMError.shouldNotReachHere("Fail to execute " + premainMethod.className + ".premain", t);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a user-facing error, right? User code can throw, so I don't see this as something that is a VMError. What does the JVM do in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In JVM, if the premain method fails, the JVM starting process shall be terminated. See https://github.com/openjdk/jdk21u/blob/2971cb5769121b47ac8c8db1078d67680a19341f/src/java.instrument/share/native/libinstrument/InvocationAdapter.c#L623-L629

I use VMError because it's a fatal error during VM starts up. But the cause is indeed an user error.

* registered premain method's second parameter. At native image runtime, no actual
* instrumentation work can do. So all the methods here are empty.
*/
public static class SVMRuntimeInstrumentImpl implements Instrumentation {
Copy link
Member

@vjovanov vjovanov Jun 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe NativeImageNoOpRuntimeInstrumentation? SVM is an internal name which we should not use. I also feel we should mention it is a noop and that it happens at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}
preMainSupport.registerPremainMethod(premainClass, premain, args.toArray(new Object[0]));
} catch (ClassNotFoundException e) {
VMError.shouldNotReachHere("Can't register agent premain method, because the declaring class " + premainClass + " is not found", e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a UserError as the user can make a mistake. We should also add a sentence on how to recover from that mistake to the error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

* <ul>
* <li>Isolate code by checking current runtime. For example: <code>
* <pre>
* String vm = System.getProperty("java.vm.name");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should guide the users to use the properties from ImageInfo. Then they can see if the agent is applied at build time or at runtime. We also need to make sure that this property is set before premain is called for the Native Image builder.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. This property can be obtained in premain.

* Keep premain options and return the rest args as main args.
* The premain options format:
* <br>
* -XX-premain:[full.qualified.premain.class]:[premain options]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this should be our final format for the flag. I think we can use it until we complete the feature and decide what is the user interface in the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I added a //todo for future changing.


public static class Options {
@Option(help = "Specify premain-class list. Multiple classes are separated by comma, and order matters.")//
public static final HostedOptionKey<LocatableMultiOptionValue.Strings> PremainClasses = new HostedOptionKey<>(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also an internal option. In the end we should probably pick the premain functions from the agent JARs.

@Override
public void afterRegistration(AfterRegistrationAccess access) {
FeatureImpl.AfterRegistrationAccessImpl a = (FeatureImpl.AfterRegistrationAccessImpl) access;
cl = a.getImageClassLoader().getClassLoader();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is still required, but I had to provide an agent JAR twice as -J-javaagent and -cp for NI build configurations in agent JARs to be picked up by the NI builder. So just checking, users can provide an agent JAR only once as -J-javaagent for their premain classes to be registered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this PR, we are not automatically picking agent from -cp or -J-javaagent. It will be handled in the following PRs. But I'm sure user doesn't need to provide agent jar multiple times.

@ziyilin ziyilin force-pushed the staticInstrument-premain branch 2 times, most recently from 8e2bd2f to c9d5652 Compare June 14, 2024 09:39
}
preMainSupport.registerPremainMethod(premainClass, premain, args.toArray(new Object[0]));
} catch (ClassNotFoundException e) {
UserError.abort(e,"Can't register agent premain method, because the given class %s is not found. Please check your -H:%s setting.", premainClass, Options.PremainClasses.getName());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to use SubstrateOptionsParser.commandArgument to print the option name. This keeps the correct behavior when options become public or change the type.

@vjovanov
Copy link
Member

vjovanov commented Jun 17, 2024

To keep this feature working as expected we need to add a few tests into: com.oracle.svm.test

The tests can be executed from similarly to the tests for class initialization.

@ziyilin
Copy link
Contributor Author

ziyilin commented Jun 19, 2024

To keep this feature working as expected we need to add a few tests into: com.oracle.svm.test

The tests can be executed from similarly to the tests for class initialization.

I have added a test task agenttest. It now only checks the premain method, doesn't do any class transformation yet.

@ziyilin ziyilin force-pushed the staticInstrument-premain branch 2 times, most recently from 5671706 to 510b031 Compare June 26, 2024 07:36
Java agent always has a premain method to initialize the agent.
SVM needs the premain as well to support agent in native image.

At compile time, -H:PremainClasses= option is used to set the premain
classes.
At runtime, premain runtime options are set along with main class'
arguments in the format of -XX-premain:[class]:[options]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
OCA Verified All contributors have signed the Oracle Contributor Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants