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
JavaAgent produces unusable output for Spring Boot Uber JARs #1008
Comments
This relates to #1004 |
See FAQ - https://www.jacoco.org/jacoco/trunk/doc/faq.html :
|
Do you have a recommendation for Spring Boot applications? At development time, there are no duplicate classes. However JaCoCo perceives the packaged Uber JAR to have duplicate classes... |
Because in your uber JAR there are indeed multiple class files for the same class name:
Generate report only for classes of your application, not for the classes of whole uber JAR:
|
So I was trying to give an example with the source code repo. The use-case is where the Spring Boot Uber JAR is all I have (i.e. I don't have a target/classes directory to run against). |
Extract classes of your application from this Uber JAR. According to output of
they seem to be in
to confirm consult with Sprint Boot documentation. |
@Godin - thanks for digging in some. I've got other use-cases as well (WARs, EARs, etc.). But if the answer is for each archive format, I've got to explicitly extract the files, and then figure out a way to run (and instrument) the extracted application, then I'm going to have a long road ahead of me. I was hoping that there was a means within JaCoCo to be fully run-time aware, even if that meant it also being class-loader aware (i.e. to be able to differentiate that one class-loader is using instance A of the class, whereas another class-loader is using instance B). |
There are two stages: Agent is perfectly aware of ClassLoaders and in case of many different classes with same name collects and stores information about all of them. However report can't contain two classes with same name, so you need to specify which ones exacly you want in a report. |
So then if the class-loaders can load the different classes having the same names, is there any possibility that the ClassIds could also take this information into account? i.e. could the ClassId be class-loader-specific to prevent issues like this (and the many other scenarios that I've seen on other tickets related to the Can't add different class with same name error such as #215)? |
i.e. is there an issue with report, since it's unable to handle things in such a manner that agent is providing it? Or do both report and agent need to be augmented in terms of their ClassId implementations to facilitate better isolation of classes at a class-loader level? |
Well, in practice it's not that easy:
|
No, as was already said multiple times in my comments above: At runtime there is no way for agent to determine location of class on disk, furthermore location of class files can be different during execution and at a time of generation of report. So instead of location agent records checksums (aka In the vast majority of cases report shows source files (option However feel free to use JaCoCo APIs to write your own report generator and maybe contribute it back. |
@Godin - I meant no offense. I understand what you're saying about it not being a bug and it being by design. I understand that by design, the report function of JaCoCo does not allow for multiple classes with the same name. However, by design, agent and the CLI instrumentation code does allow for multiple classes with the same name... So my question then becomes, should an error be thrown earlier on in the flow by any of the other JaCoCo tools (i.e. the agent, CLI instrumentation, etc.) when multiple classes with the same name exist? In trying to provide the JaCoCo CLI report function the ability to filter out the duplicated classes, I tried using the |
No.
First of all as was already said in #1008 (comment)
Not the other way around: There is no way to determine names of class files based on source files without being a compiler for all the languages running on JVM for which JaCoCo works (Java, Kotlin, Groovy, etc). Please believe me, or try to do this by yourself. Name of a source file is much easier to obtain from a class file and this is what JaCoCo does. The only existing as of today way to select class files for inclusion into HTML report when using command line interface in your case was already described in #1008 (comment)
|
@Godin - once again, thank you for your involvement on this ticket and willingness to discuss. So taking everything that you've said into consideration, the best solution that I've come up with is the following: # Assuming that you have a Spring Boot Uber JAR and can get a sources JAR for that Uber JAR
# Determine the includes list
includes=$(zipinfo -2 ${sourcesJar} -x META-INF/** | grep -E '*.java' | awk -F/ 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' | uniq | sed 's|/|.|g' | awk -v suffix=".*" '{print $0 suffix}' | paste -sd ":" -)
# Run JaCoCo
rm -f jacoco.exec
java -javaagent:${curDir}/jacoco-agent-runtime.jar=destfile=${curDir}/jacoco.exec,includes=${includes},output=file,dumponexit=true -jar ${uberJar}
# The following blows up with "java.lang.IllegalStateException: Can't add different class with same name:..."
# java -jar ${curDir}/jacoco-cli.jar report jacoco.exec --classfiles=${uberJar} --html ${curDir}/coverage-html
# Instead, extract the Uber JAR and explicitly determine which class files to report on based on the includes list (i.e. source packages)
tempClassfilesDir=$(mktemp -d)
unzip ${uberJar} -d ${tempClassfilesDir}
includesFilter=$(echo ${includes} | sed 's/:/|/g')
classfiles=$(zipinfo -2 ${uberJar} | grep -E ${includesFilter} | grep -E '*\.class' | awk -F/ 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' | uniq | awk -v prefix="--classfiles=${tempClassfilesDir}/" -v suffix="/" '{print prefix $0 suffix}' | paste -sd " " -)
# Extract the source JAR so that source files can be linked during reporting
tempSourcefilesDir=$(mktemp -d)
unzip ${sourcesJar} -d ${tempSourcefilesDir}
# Do the actual reporting
htmlCoverageDir=${curDir}/coverage-html
rm -rf ${htmlCoverageDir}
java -jar ${curDir}/jacoco-cli.jar report jacoco.exec ${classfiles} --sourcefiles=${tempSourcefilesDir} --html ${htmlCoverageDir}
# Cleanup
rm -rf ${tempSourcefilesDir}
rm -rf ${tempClassfilesDir} Maybe it'll help someone else out. |
Steps to reproduce
Expected behavior
My expectation is that running a Spring Boot Uber JAR with the javaagent should result in usable output.
Actual behavior
Running the resultant jacoco.exec file through the CLI's report function results in:
The text was updated successfully, but these errors were encountered: