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

Weaver should return null instead of the original bytes for unwoven classes #277

Closed
KimmingLau opened this issue Feb 5, 2024 · 21 comments · Fixed by #280
Closed

Weaver should return null instead of the original bytes for unwoven classes #277

KimmingLau opened this issue Feb 5, 2024 · 21 comments · Fixed by #280
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@KimmingLau
Copy link
Contributor

KimmingLau commented Feb 5, 2024

We try to experience dynamic AppCDS which is a new feature from JDK19 AppCDS with LTW javaagent.
But we found that jdk core classes cannot be load from the default archive, because when Aj realize a class is loaded by boostrap classloader, it will return the original bytes. This will cause the JVM to think that the class has been modified by ClassFileTransformer, and do not load the class from default CDS archive.

According to ClassFileTransformer javadoc , it refer to that return null if no transform is performed. So I think when a class isn't been weaved, Aj should return null instead of the original bytes.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 6, 2024

Hello. Thanks for the report. What you say makes sense at first glance, but I would have to check if any consumers of transformed classes might expect non-null results for whatever reasons. Because I am unfamiliar with CDS archives, please provide a full, minimal example with exact steps to reproduce the problem. I want to see for myself what you are doing. Please also explain why you are doing it. I wonder why AspectJ firstly does not skip JDK classes when weaving and secondly tries to instrument code that is not visible to its class loader. There are too many open questions without a reproducer. Currently, I would not even know how to write a regression test for it.

Besides, is there any connection between this issue and #278? It strikes me as a strange coincidence, that two issues concerning class-loading are opened within just 10 minutes.

@KimmingLau
Copy link
Contributor Author

KimmingLau commented Feb 6, 2024

AppCdsDemo-1.0-SNAPSHOT-zip.zip
Hi. Thanks for your replies. Here is a reproducible demo, unzip it and run the following command with jdk21 runtime
java -XX:+UnlockDiagnosticVMOptions -XX:+AllowArchivingWithJavaAgent -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=demo.jsa -javaagent:aspectjweaver-1.9.5.jar -cp AppCdsDemo-1.0-SNAPSHOT.jar com.vip.demo.Demo
You will see a crash when the JVM exits because it try to dump a CDS archive but validation failed. After Spending a lot time reading JVM source code, I found the root cause was that the AspectJ agent returned the original bytes when processing classes loaded by bootstrap classloader, but at the JVM level, as long as the return value of ClassFileTransformer is not null, it will be considered that the class has been modified and the returned byte array will be copied to new memory with a new pointer pointing to it.
You would say this is a JVM bug about CDS, yes, I think so too. But this phenomenon makes me have to think about that - is Aj#preProcess() method better to return null instead of the original bytes for classes loaded by bootstrap? Even for all classes that do not need to be woven.

@KimmingLau
Copy link
Contributor Author

By the way, we use CDS to improve the startup speed of the application, and most of us use AspectJ for monitoring and tracking. That's why we meet this situation.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 6, 2024

  1. When I talked about a reproducer, I actually meant a Maven project I can build from scratch.
  2. I do not have your lib directory and have no idea what is in there.
  3. You did not say which error you see on the console and what you expect to happen instead. But I think I found out, see below.
  4. What is the reason for using an old AspectJ version instead of 1.9.21? Probably this is not so relevant to the question, 1.9.21 yields an error, too. But I would like to know anyway.

For reference, this is what I see on the console:

[0.398s][warning][cds] This archive was created with AllowArchivingWithJavaAgent. It should be used for testing purposes only and should not be used in a production environment
[0.398s][warning][cds] Skipping org/aspectj/weaver/ResolvedType$Missing: Unsupported location
[0.398s][warning][cds] Skipping java/util/HashMap$HashMapSpliterator: Unsupported location
[0.399s][warning][cds] Skipping sun/management/VMManagement: Unsupported location
[0.399s][warning][cds] Skipping java/lang/invoke/GenerateJLIClassesHelper$HolderClassBuilder: Unsupported location
[0.399s][warning][cds] Skipping org/aspectj/apache/bcel/generic/Type$2: Unsupported location
[0.399s][warning][cds] Skipping org/aspectj/apache/bcel/classfile/ClassFormatException: Unsupported location
[0.401s][warning][cds] Skipping java/lang/management/ManagementFactory: Unsupported location
...
[0.402s][warning][cds] Skipping sun/management/VMManagementImpl: Unsupported location
[0.402s][warning][cds] Skipping java/util/TreeMap: Unsupported location
[0.402s][warning][cds] Skipping org/aspectj/apache/bcel/generic/InvokeDynamic: Unsupported location
[0.402s][warning][cds] Skipping java/lang/invoke/GenerateJLIClassesHelper: Unsupported location
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (systemDictionaryShared.cpp:575), pid=17044, tid=8792
#  guarantee(!k->is_shared_unregistered_class()) failed: Class loader type must be set for BUILTIN class java/lang/invoke/BoundMethodHandle$Species_LLJ
#
# JRE version: Java(TM) SE Runtime Environment (21.0+35) (build 21+35-LTS-2513)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (21+35-LTS-2513, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\Users\alexa\Documents\java-src\AJ_GH-277\hs_err_pid17044.log
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
#

And this is the written error dump:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (systemDictionaryShared.cpp:575), pid=17044, tid=8792
#  guarantee(!k->is_shared_unregistered_class()) failed: Class loader type must be set for BUILTIN class java/lang/invoke/BoundMethodHandle$Species_LLJ
#
# JRE version: Java(TM) SE Runtime Environment (21.0+35) (build 21+35-LTS-2513)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (21+35-LTS-2513, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
#

---------------  S U M M A R Y ------------

Command Line: -XX:+UnlockDiagnosticVMOptions -XX:+AllowArchivingWithJavaAgent -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=demo.jsa -javaagent:aspectjweaver-1.9.21.jar com.vip.demo.Demo

...

Environment Variables:
JAVA_HOME=c:\Program Files\Java\jdk-21
...

---------------  S Y S T E M  ---------------

OS:
 Windows 10 , 64 bit Build 19041 (10.0.19041.3636)
OS uptime: 2 days 20:27 hours
Hyper-V role detected

CPU: total 4 (initial active 4) (2 cores per cpu, 2 threads per core) family 6 model 142 stepping 9 microcode 0xec, cx8, cmov, fxsr, ht, mmx, 3dnowpref, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, lzcnt, tsc, tscinvbit, avx, avx2, aes, erms, clmul, bmi1, bmi2, adx, fma, vzeroupper, clflush, clflushopt, hv, rdtscp, f16c
Processor Information for all 4 processors :
  Max Mhz: 2904, Current Mhz: 2703, Mhz Limit: 2700

Memory: 4k page, system-wide physical 16234M (5319M free)
TotalPageFile size 32469M (AvailPageFile size 15609M)
current process WorkingSet (physical memory assigned to process): 60M, peak: 60M
current process commit charge ("private bytes"): 342M, peak: 342M

vm_info: Java HotSpot(TM) 64-Bit Server VM (21+35-LTS-2513) for windows-amd64 JRE (21+35-LTS-2513), built on 2023-08-09T20:25:10Z by "mach5one" with MS VC++ 17.1 (VS2022)

END.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 6, 2024

One more question: Do you have a working counter-example with a Java agent transforming your demo class, but returning null for the others? I.e. a proof of concept that in that case, it actually works as expected? Also with source code, please.

@KimmingLau
Copy link
Contributor Author

Oh sorry, there is a typo in the command, I've fixed it on the comment above.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 6, 2024

I have never used CDS before, so please also tell me how to run the application after creating the archive. After I changed org.aspectj.weaver.loadtime.Aj::preProcess to always return null where in previously it returned bytes, I tried this:

$ java -XX:+UnlockDiagnosticVMOptions -XX:+AllowArchivingWithJavaAgent -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=demo.jsa -javaagent:aspectjweaver-1.9.21.1-SNAPSHOT.jar -cp AppCdsDemo-1.0-SNAPSHOT.jar com.vip.demo.Demo

[0.412s][warning][cds] This archive was created with AllowArchivingWithJavaAgent. It should be used for testing purposes only and should not be used in a production environment
[0.412s][warning][cds] Skipping org/aspectj/weaver/tools/TraceFactory: Unsupported location
[0.412s][warning][cds] Skipping org/aspectj/weaver/ArrayReferenceType: Unsupported location
[0.412s][warning][cds] Skipping org/aspectj/apache/bcel/classfile/LocalVariableTable: Unsupported location
...
[0.413s][warning][cds] Skipping org/aspectj/apache/bcel/classfile/LineNumberTable: Unsupported location
[0.413s][warning][cds] Skipping aj/org/objectweb/asm/ClassVisitor: Unsupported location

$ ls -l
...
-r--r--r-- 1 alexa 197609 1048576 Feb  6 14:25 demo.jsa

$ java -XX:+UnlockDiagnosticVMOptions -XX:+AllowArchivingWithJavaAgent -XX:SharedArchiveFile=demo.jsa -cp AppCdsDemo-1.0-SNAPSHOT.jar com.vip.demo.Demo -javaagent:aspectjweaver-1.9.21.1-SNAPSHOT.jar
[0.012s][warning][cds] This archive was created with AllowArchivingWithJavaAgent. It should be used for testing purposes only and should not be used in a production environment

Is that what you would expect? And did I call the program correctly, again with -javaagent, or is the result expected to contain the woven files already? Unfortunately, your program does not print anything, no aspects are involved and I do not see, if aspects have been applied correctly.

Decompiling your demo class yields:

package com.vip.demo;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.List;

public class Demo {
  public Demo() {
  }

  public static void main(String[] args) {
    List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
  }
}

That is not much. A more meaningful reproducer would be helpful. I do not want to do all the work from scratch that you obviously have done already.

@KimmingLau
Copy link
Contributor Author

KimmingLau commented Feb 6, 2024

That's it, I met the same core dump error like yours and I've try another version of aspectjweaver but it was failed too.
I'm sorry I don't have the POC you expected, but you can try to modify the method public byte[] preProcess(String className, byte[] bytes, ClassLoader loader, ProtectionDomain protectionDomain) in Aj.java, and let it return null when the class loaded by bootstrap classloader(i.e. loader == null). Using the new building aspectweaver.jar to replace that in demo, you will find that CDS works as exepected, which means the demo.jsa file generated successfully.
Here is my source code you can build from scratch.
AppCdsDemo.zip

@KimmingLau
Copy link
Contributor Author

Hi, after generating .jsa file, it means CDS dumping works, and you can the same command to run the application again, and JVM will use .jsa file to load application classes. You can add JVM option -Xshare:on to validate the jsa file. The complete command is in the following:
java -XX:+UnlockDiagnosticVMOptions -Xshare:on -XX:+AllowArchivingWithJavaAgent -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=demo.jsa -javaagent:aspectjweaver-1.9.5.jar -cp AppCdsDemo-1.0-SNAPSHOT.jar com.vip.demo.Demo

And here are some information about CDS (the first one would be the quick start):
https://docs.oracle.com/en/java/javase/19/docs/specs/man/java.html#application-class-data-sharing
https://openjdk.org/jeps/310
https://openjdk.org/jeps/341
https://openjdk.org/jeps/350
https://dev.java/learn/jvm/cds-appcds

@kriegaex
Copy link
Contributor

kriegaex commented Feb 6, 2024

Hm, what is the point of creating those optimised files for faster start-up, if then the load-time weaver needs to do its work again? I would have expected you want to create such an archive, because when starting the application again the classes are woven already. Is there any performance gain in doing it like you described?

@kriegaex
Copy link
Contributor

kriegaex commented Feb 6, 2024

BTW, java agents can in principle also instrument JDK bootstrap classes, albeit with certain structural limitations. AspectJ happens not to do that, but it does not mean that it never will in the future. What would you expect to happen in such a case when running the java agent again on the CDS archive?

Sorry for the many questions, but I do not want to blindly change some code, but also understand what the whole purpose is. That has an influence on both my motivation and the prioritisation of this issue. As the saying goes, if you want to pitch an idea or make someone do something for you, you need to be able to sell it. Let us not forget, that I am doing this work for free, unless you decide to hire me or otherwise sponsor the change.

@KimmingLau
Copy link
Contributor Author

KimmingLau commented Feb 6, 2024

Hm, what is the point of creating those optimised files for faster start-up, if then the load-time weaver needs to do its work again? I would have expected you want to create such an archive, because when starting the application again the classes are woven already. Is there any performance gain in doing it like you described?

I think it is a very good question

I can understand your position very well. As CDS is a new feature that few people use. It will inevitably be risky to modify the originally stable code in order to support it. Appreciate for your reply again.

Best wishes!

@kriegaex
Copy link
Contributor

kriegaex commented Feb 7, 2024

I am working on this, because independently of CDS it makes sense to return null, if the candidate class is not transformed. But for now, you can just

  • either create the CDS archive without -javaagent and only use that parameter later during runtime, when you really need it, because the archived classes are not the woven ones anyway,
  • or, if you want the aspect code to be archived, use compile-time weaving (CTW) first and then run create the CDS archive on the woven application.

The latter option might be right, if you use production aspects that always should be in place when running the application. If your aspects are rather used temporarily, e.g. for debugging or tracing, and should not be in the byte code permanently, the former option might is better. A hybrid solution would be CTW and aspects with activation flag, e.g. using if() pointcuts.

An important open question for me as someone who has never used CDS before, which I kindly request you to answer: Is there a way to dump classes transformed by a Java agent into a CDS file? If there is such a way and you are using it, please let me know and explain how to activate it, so I can test it. JDK-8213813 (Add the AllowArchivingWithJavaAgent diagnostic vm option to allow the use of the -javaagent option during CDS dumping) does not explain anything about it.

@kriegaex kriegaex changed the title return null instead of the original bytes when a class isn't been weaved Weaver should return null instead of the original bytes for unwoven classes Feb 7, 2024
@kriegaex kriegaex self-assigned this Feb 7, 2024
@kriegaex kriegaex added the enhancement New feature or request label Feb 7, 2024
@kriegaex kriegaex added this to the 1.9.21.1 milestone Feb 7, 2024
@kriegaex
Copy link
Contributor

kriegaex commented Feb 7, 2024

Is there a way to dump classes transformed by a Java agent into a CDS file?

OK, I think I have found out. My experimental findings, both as a note to myself and information to the audience:

When creating the archive with the agent on the command line, i.e. when using -javaagent:aspectjweaver.jar -XX:+AllowArchivingWithJavaAgent, the agent also must be on the classpath, otherwise during the CDS dump yields lots of messages like

...
[0.877s][warning][cds] Skipping org/aspectj/weaver/IWeaveRequestor: Unsupported location
[0.877s][warning][cds] Skipping org/aspectj/apache/bcel/generic/ReturnaddressType: Unsupported location
[0.877s][warning][cds] Skipping org/aspectj/weaver/loadtime/DefaultWeavingContext: Unsupported location
...

The dump file is also much smaller than when doing the same with the agent on the classpath, in my test case 2.4M without aspectjweaver on the CP versus 6.9M with. Example:

$ rm -f *.jsa

$ java -XX:+UnlockDiagnosticVMOptions -Xshare:on -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=demo.jsa -cp "AppCdsDemo-1.0-SNAPSHOT.jar;aspectjweaver-1.9.21.1-SNAPSHOT.jar" -XX:+AllowArchivingWithJavaAgent -javaagent:aspectjweaver-1.9.21.1-SNAPSHOT.jar com.vip.demo.Demo

[AppClassLoader@5a07e868] weaveinfo Join point 'method-execution(void com.vip.demo.Demo.main(java.lang.String[]))' in Type 'com.vip.demo.Demo' (Demo.java:12) advised by before advice from 'com.vip.demo.MyAspect' (MyAspect.java)
execution(void com.vip.demo.Demo.main(String[]))
Hello world
[0.936s][warning][cds] This archive was created with AllowArchivingWithJavaAgent. It should be used for testing purposes only and should not be used in a production environment
[0.936s][warning][cds] Skipping aj/org/objectweb/asm/Type: Old class has been linked
[0.936s][warning][cds] Skipping org/aspectj/weaver/bcel/asm/StackMapAdder$AspectJClassVisitor: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/MethodWriter: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/commons/ClassRemapper: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/Label: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/ClassReader: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/Attribute: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/Frame: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/CurrentFrame: super class aj/org/objectweb/asm/Frame is excluded
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/ClassWriter: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/ClassVisitor: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/Context: Old class has been linked
[0.937s][warning][cds] Skipping org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor$MethodAndConstructorRemover: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/MethodVisitor: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/commons/MethodRemapper: super class aj/org/objectweb/asm/MethodVisitor is excluded
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/ByteVector: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/AnnotationVisitor: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/commons/AnnotationRemapper: super class aj/org/objectweb/asm/AnnotationVisitor is excluded
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/commons/FieldRemapper: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/FieldVisitor: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/FieldWriter: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/Edge: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/SymbolTable$Entry: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/SymbolTable: Old class has been linked
[0.937s][warning][cds] Skipping org/aspectj/weaver/bcel/asm/StackMapAdder$AspectJConnectClassWriter: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/commons/Remapper: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/Symbol: Old class has been linked
[0.937s][warning][cds] Skipping org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor$ClassNameRemapper: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/Handler: Old class has been linked
[0.937s][warning][cds] Skipping aj/org/objectweb/asm/AnnotationWriter: Old class has been linked
[0.937s][warning][cds] Skipping org/aspectj/weaver/bcel/asm/StackMapAdder$AspectJClassVisitor$AJMethodVisitor: Old class has been linked

alexa@Xander-UB MINGW64 ~/Documents/java-src/AJ_GH-277
$ ls -lh demo.jsa
-r--r--r-- 1 alexa 197609 6.9M Feb  7 09:17 demo.jsa

See? No more Skipping ... Unsupported location warnings anymore, only a few Old class has been linked ones, which seem to be OK.

Now let us run the application again with the identical command line:

$ java -XX:+UnlockDiagnosticVMOptions -Xshare:on -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=demo.jsa -cp "AppCdsDemo-1.0-SNAPSHOT.jar;aspectjweaver-1.9.21.1-SNAPSHOT.jar" -XX:+AllowArchivingWithJavaAgent -javaagent:aspectjweaver-1.9.21.1-SNAPSHOT.jar com.vip.demo.Demo

[0.014s][warning][cds] This archive was created with AllowArchivingWithJavaAgent. It should be used for testing purposes only and should not be used in a production environment
[AppClassLoader@2626b418] weaveinfo Join point 'method-execution(void com.vip.demo.Demo.main(java.lang.String[]))' in Type 'com.vip.demo.Demo' (Demo.java:12) advised by before advice from 'com.vip.demo.MyAspect' (MyAspect.java)
execution(void com.vip.demo.Demo.main(String[]))
Hello world

Please note above:

  • The weaver prints its weaveinfo Join point message.
  • The sample aspect logs execution(void com.vip.demo.Demo.main(String[])) as expected.

Now, let us go one step further and remove the -javaagent option from the command line:

$ java -XX:+UnlockDiagnosticVMOptions -Xshare:on -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=demo.jsa -cp "AppCdsDemo-1.0-SNAPSHOT.jar;aspectjweaver-1.9.21.1-SNAPSHOT.jar" -XX:+AllowArchivingWithJavaAgent com.vip.demo.Demo

[0.012s][warning][cds] This archive was created with AllowArchivingWithJavaAgent. It should be used for testing purposes only and should not be used in a production environment
execution(void com.vip.demo.Demo.main(String[]))
Hello world

Very interesting! There is no more weaver message, but still the aspect does its job, because obviously the application class has been archived in its woven state. I.e., the effect is similar to a "poor man's compiler-time weaving". The same log output we would probably see, if we would just build using compile-time weaving and creating the CDS archive while running the woven application with just aspectjrt.jar on the classpath. That would yield a smaller CDS archive, because the weaver classes would not be necessary, only the runtime classes. Anyway, it works with the weaver scenario, too, after I locally changed the code to return null for unwoven classes.

kriegaex added a commit that referenced this issue Feb 7, 2024
This change makes sense independently of #277, but also enables using

  - cp "my.jar;aspectjweaver.jar"
  -XX:+AllowArchivingWithJavaAgent
  -javaagent:aspectjweaver.jar

while creating a CDS archive. Afterward, the application can be run in
its woven state from the CDS archive even without '-javaagent', because
the byte code was archived in its woven state ("poor man's AJC"). See
#277 (comment)
for details.

Fixes #277.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
kriegaex added a commit that referenced this issue Feb 7, 2024
This change makes sense independently of #277, but also enables using

  - cp "my.jar;aspectjweaver.jar"
  -XX:+AllowArchivingWithJavaAgent
  -javaagent:aspectjweaver.jar

while creating a CDS archive. Afterward, the application can be run in
its woven state from the CDS archive even without '-javaagent', because
the byte code was archived in its woven state ("poor man's AJC"). See
#277 (comment)
for details.

Fixes #277.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
@KimmingLau
Copy link
Contributor Author

Good morning kriegaex, I'm so excited that you are so interested in this problem. I was confused by Skipping ... Unsupported location warning logs before, and you solved the problem by putting the agent jar on the classpath, amazing!

@kriegaex
Copy link
Contributor

kriegaex commented Feb 7, 2024

@KimmingLau, yes, CDS seems to need a bit of assistance there when creating the archive. For simple weaving, this is unnecessary, but obviously CDS disregards (or rather cannot find) agent classes, if it cannot find them on the CP additionally. According to the JDK-8213813 description and the warning "[cds] This archive was created with AllowArchivingWithJavaAgent. It should be used for testing purposes only and should not be used in a production environment", running the CDS creator with an agent is not meant to modify byte code for production, i.e. the missing support for adding agent JAR classes to the CP automatically probably cannot be termed a bug. The OpenJDK team would probably reject it, even though it would be convenient to have this automatism. However, I can imagine cases in which the classes are not wanted in the CDS archive, which is why I guess the opt-in solution is acceptable.

kriegaex added a commit that referenced this issue Feb 7, 2024
This change makes sense independently of #277, but also enables using

  - cp "my.jar;aspectjweaver.jar"
  -XX:+AllowArchivingWithJavaAgent
  -javaagent:aspectjweaver.jar

while creating a CDS archive. Afterward, the application can be run in
its woven state from the CDS archive even without '-javaagent', because
the byte code was archived in its woven state ("poor man's AJC"). See
#277 (comment)
for details.

Fixes #277.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
kriegaex added a commit that referenced this issue Feb 7, 2024
This change makes sense independently of #277, but also enables using

  - cp "my.jar;aspectjweaver.jar"
  -XX:+AllowArchivingWithJavaAgent
  -javaagent:aspectjweaver.jar

while creating a CDS archive. Afterward, the application can be run in
its woven state from the CDS archive even without '-javaagent', because
the byte code was archived in its woven state ("poor man's AJC"). See
#277 (comment)
for details.

Fixes #277.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
kriegaex added a commit that referenced this issue Feb 7, 2024
This change makes sense independently of #277, but also enables using

  - cp "my.jar;aspectjweaver.jar"
  -XX:+AllowArchivingWithJavaAgent
  -javaagent:aspectjweaver.jar

while creating a CDS archive. Afterward, the application can be run in
its woven state from the CDS archive even without '-javaagent', because
the byte code was archived in its woven state ("poor man's AJC"). See
#277 (comment)
for details.

Fixes #277.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
kriegaex added a commit that referenced this issue Feb 7, 2024
Relates to #277.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
@kriegaex
Copy link
Contributor

kriegaex commented Feb 7, 2024

@KimmingLau, the problem should be fixed. Please try the latest 1.9.21.1-SNAPSHOT.

<repositories>
    <repository>
        <id>ossrh-snapshots</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
        </snapshots>
    </repository>
</repositories>

BTW, the snapshot also contains an improvement which lets you use LTW without --add-opens java.base/java.lang=ALL-UNNAMED on JDk 16+. Until 1.9.21, without that option LTW does not work, unless you are lucky not to need around advice or in general anything that needs to define new classes during weaving.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 7, 2024

Another note for the record: While creating CDS archives with -XX:ArchiveClassesAtExit=demo.jsa and then running them works on JDKs 13-19 even without my fix, it fails on JDKs 20-21. I.e., the fact that the JDK crashes on JDKs 20-21 just because an agent returns identical class bytes for a bootstrap class is to be considered a JDK bug, not one in the AspectJ Weaver or any other java agent. The AspectJ change is simply a case of defensive programming.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 7, 2024

I created a minimal reproducer as a Maven project with a very simple agent, not AspectJ:
https://github.com/kriegaex/cds-javaagent-bug

Just run mvn clean verify to reproduce the error on JDKs 20 or 21. On older JDKs 13-19, there is no error.

As a workaround, edit the line containing

<argument>-javaagent:${project.basedir}/../cds-javaagent-agent/target/cds-javaagent-agent-1.0-SNAPSHOT.jar=XXXreturnNull</argument>

in file cds-javaagent-test/pom.xml, removing the "XXX". Then, the agent will return null instead of the original class bytes.

BTW, the problem does not occur with any program that runs with the agent, but specifically if the program contains ManagementFactory.getGarbageCollectorMXBeans().

The reproducer serves as the input for a new OpenJDK bug I just created. It has the internal review ID 9076545. As soon as the bug is accepted and visible in the public JDK bug database, I will add a link here.

@KimmingLau
Copy link
Contributor Author

KimmingLau commented Feb 7, 2024

Yes, because ManagementFactory.getGarbageCollectorMXBeans() contains a combined lamba expression, which cause BoundMethodHandle$Species_LLJ loading by bootstrap classloader. I've also reported a similar one to JDK bug database, but maybe my reproducer was not completed like yours,. they required me to provide more information. Anyway, It would be nice if they knew it was a bug.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 9, 2024

The reproducer serves as the input for a new OpenJDK bug I just created. It has the internal review ID 9076545. As soon as the bug is accepted and visible in the public JDK bug database, I will add a link here.

https://bugs.openjdk.org/browse/JDK-8325536

kriegaex added a commit that referenced this issue Feb 12, 2024
See https://bugs.openjdk.org/browse/JDK-8325536.

Relates to #277.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants