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

mockito-inline on OpenJDK 17 fails to start due to ByteBuddy agent failure #2436

Open
ascopes opened this issue Sep 26, 2021 · 32 comments
Open

Comments

@ascopes
Copy link
Contributor

ascopes commented Sep 26, 2021

It appears that GraalVM JDK 17 does not allow inline mocks of final classes to be made using mockito-inline or by adding the mockito inline extension file.

org.mockito.exceptions.base.MockitoInitializationException: Could not initialize inline Byte Buddy mock maker.

It appears as if your JDK does not supply a working agent attachment mechanism.
Java               : 17
JVM vendor name    : GraalVM Community
JVM vendor version : 17+35-jvmci-21.3-b02
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17+35-jvmci-21.3-b02
JVM info           : mixed mode
OS name            : Linux
OS version         : 5.15.0-1-MANJARO

Not sure if this is the same issue as #2315 or #2122, so I have opened this to try and get some further clarification.


I have included the full code to reproduce, as well as the full exception trace itself below:

src/test/java/mockito/bug/FinalClassMockTest.java

package mockito.bug;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class FinalClassMockTest {
  @Test
  void try_to_mock() {
    Mockito.mock(FinalClass.class);
  }
}

mockito/bug/FinalClass.java

package mockito.bug;

public final class FinalClass {
  public void methodIWantToMock() {
    System.out.println("I don't want to invoke this");
  }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mockito-bug</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.7.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>3.12.4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Full error:

java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)

	at org.mockito.internal.configuration.plugins.PluginLoader$1.invoke(PluginLoader.java:88)
	at jdk.proxy2/jdk.proxy2.$Proxy11.isTypeMockable(Unknown Source)
	at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:33)
	at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22)
	at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:250)
	at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:232)
	at org.mockito.internal.MockitoCore.mock(MockitoCore.java:83)
	at org.mockito.Mockito.mock(Mockito.java:1964)
	at org.mockito.Mockito.mock(Mockito.java:1879)
	at mockito.bug.FinalClassMockTest.try_to_mock(FinalClassMockTest.java:9)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.IllegalStateException: Failed to load interface org.mockito.plugins.MockMaker implementation declared in java.lang.CompoundEnumeration@20d3d15a
	at org.mockito.internal.configuration.plugins.PluginInitializer.loadImpl(PluginInitializer.java:58)
	at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:69)
	at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:54)
	at org.mockito.internal.configuration.plugins.PluginRegistry.<init>(PluginRegistry.java:29)
	at org.mockito.internal.configuration.plugins.Plugins.<clinit>(Plugins.java:20)
	at org.mockito.internal.util.MockUtil.<clinit>(MockUtil.java:28)
	... 72 more
Caused by: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at org.mockito.internal.configuration.plugins.PluginInitializer.loadImpl(PluginInitializer.java:53)
	... 77 more
Caused by: org.mockito.exceptions.base.MockitoInitializationException: 
Could not initialize inline Byte Buddy mock maker.

It appears as if your JDK does not supply a working agent attachment mechanism.
Java               : 17
JVM vendor name    : GraalVM Community
JVM vendor version : 17+35-jvmci-21.3-b02
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17+35-jvmci-21.3-b02
JVM info           : mixed mode, sharing
OS name            : Linux
OS version         : 5.15.0-1-MANJARO

	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.<init>(InlineDelegateByteBuddyMockMaker.java:246)
	at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.<init>(InlineByteBuddyMockMaker.java:25)
	... 83 more
Caused by: java.lang.IllegalStateException: Could not self-attach to current VM using external process
	at net.bytebuddy.agent.ByteBuddyAgent.installExternal(ByteBuddyAgent.java:698)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:629)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:609)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:561)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:538)
	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.<clinit>(InlineDelegateByteBuddyMockMaker.java:117)
	... 84 more


Process finished with exit code 255

@ascopes
Copy link
Contributor Author

ascopes commented Sep 26, 2021

Looks like this also fails on non-final classes when we use the inline mockmaker.

Disabling inline mockmaker entirely resolves the issue.

@ascopes
Copy link
Contributor Author

ascopes commented Sep 26, 2021

On further investigation, this appears to affect the version of OpenJDK 17 distributed on Dockerhub also. Will remove GraalVM out of the title since this is not GraalVM-specific.

FROM openjdk:17-alpine
COPY . .
RUN java -version
RUN javac -version
RUN ./mvnw -B -q clean package
$ docker build --force-rm -f docker/openjdk17.dockerfile .
Sending build context to Docker daemon  115.2kB
Step 1/5 : FROM openjdk:17-alpine
 ---> 264c9bdce361
Step 2/5 : COPY . .
 ---> 161956775b3f
Step 3/5 : RUN java -version
 ---> Running in fb8f152067dc
openjdk version "17-ea" 2021-09-14
OpenJDK Runtime Environment (build 17-ea+14)
OpenJDK 64-Bit Server VM (build 17-ea+14, mixed mode, sharing)
Removing intermediate container fb8f152067dc
 ---> 847c8ce3fa88
Step 4/5 : RUN javac -version
 ---> Running in a90ba9e03b07
javac 17-ea
Removing intermediate container a90ba9e03b07
 ---> 76dc275e0be9
Step 5/5 : RUN ./mvnw -B -q clean package
 ---> Running in 511d996ac0d6
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.59 s <<< FAILURE! - in mockito.bug.FinalClassMockTest
[ERROR] mockito.bug.FinalClassMockTest.try_to_mock  Time elapsed: 0.574 s  <<< ERROR!
java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
        at mockito.bug.FinalClassMockTest.try_to_mock(FinalClassMockTest.java:9)
Caused by: java.lang.IllegalStateException: Failed to load interface org.mockito.plugins.MockMaker implementation declared in java.lang.CompoundEnumeration@1b1473ab
        at mockito.bug.FinalClassMockTest.try_to_mock(FinalClassMockTest.java:9)
Caused by: java.lang.reflect.InvocationTargetException
        at mockito.bug.FinalClassMockTest.try_to_mock(FinalClassMockTest.java:9)
Caused by: org.mockito.exceptions.base.MockitoInitializationException: 

Could not initialize inline Byte Buddy mock maker.

It appears as if your JDK does not supply a working agent attachment mechanism.
Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17-ea+14
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17-ea+14
JVM info           : mixed mode, sharing
OS name            : Linux
OS version         : 5.15.0-1-MANJARO

        at mockito.bug.FinalClassMockTest.try_to_mock(FinalClassMockTest.java:9)
Caused by: java.lang.IllegalStateException: Could not self-attach to current VM using external process
        at mockito.bug.FinalClassMockTest.try_to_mock(FinalClassMockTest.java:9)

[ERROR] Errors: 
[ERROR]   FinalClassMockTest.try_to_mock:9 » IllegalState Could not initialize plugin: i...
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.0.0-M5:test (default-test) on project mockito-bug: There are test failures.
[ERROR] 
[ERROR] Please refer to /target/surefire-reports for the individual test results.
[ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Removing intermediate container 511d996ac0d6
The command '/bin/sh -c ./mvnw -B -q clean package' returned a non-zero code: 1

Updated POM to use build GAV mvn:org.apache.maven.plugins/maven-surefire-plugin/3.0.0-M5 and generated ./mvn with io.takari:maven:0.7.7:wrapper

Attached tarball of code to reproduce for reference.

If you need any other details, or if this is mirrored by another issue, please let me know!

@ascopes ascopes changed the title mockito-inline on GraalVM JDK 17 fails mockito-inline on OpenJDK 17 fails to start due to ByteBuddy agent failure Sep 26, 2021
@ascopes
Copy link
Contributor Author

ascopes commented Oct 22, 2021

Any updates on this? Currently this is blocking us from using Mockito on JDK 17, as we need the inline mockmaker for other internals.

@TimvdLippe
Copy link
Contributor

The exception stacktrace mentions the following, which I think is the root cause: Caused by: java.lang.IllegalStateException: Could not self-attach to current VM using external process

Can you try a different VM and see if that works?

FWIW we are running our test suite on JDK 17 and it is passing:

java: [8, 11, 17]

@ascopes
Copy link
Contributor Author

ascopes commented Oct 26, 2021

When I ran this, I ran it on the standard OpenJDK provided on the Arch User Repository as well as with Graal. I will recheck this when I have some time, and get back to you!

@TimvdLippe
Copy link
Contributor

Closing this as "not reproducible" for now, as we are successfully building on JDK 17. Let us know if you can reproduce this issue specifically with JDK 17.

@ascopes
Copy link
Contributor Author

ascopes commented Nov 20, 2021

Closing this as "not reproducible" for now, as we are successfully building on JDK 17. Let us know if you can reproduce this issue specifically with JDK 17.

This appears to be a persistent issue with GraalVM 17+35-jvmci-21.3-b02; I am just trying to check if there is a more up-to-date version on the AUR for this machine, as I used the version on SDKMAN on another Linux machine, and on a Mac OS machine and it was not reproducible which feels a bit strange.

@ascopes
Copy link
Contributor Author

ascopes commented Nov 20, 2021

Can confirm that this seems to be an issue with that build of GraalVM. Updating fixes the issue.

@dotsad
Copy link

dotsad commented Apr 21, 2023

This happens on 17.0.1 (arm64) "Oracle Corporation" - "Java SE 17.0.1" /Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home but not x86_64 version. Confirmed locally.

@vunhatchuong
Copy link

I have the same issue:

Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17.0.7+7
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17.0.7+7
JVM info           : mixed mode
OS name            : Linux
OS version         : 6.1.33-1-lts

@plmakoc
Copy link

plmakoc commented Jun 19, 2023

I have same issue:

It appears as if your JDK does not supply a working agent attachment mechanism.
Java : 17
JVM vendor name : Oracle Corporation
JVM vendor version : 17.0.2+8-LTS-86
JVM name : Java HotSpot(TM) 64-Bit Server VM
JVM version : 17.0.2+8-LTS-86
JVM info : mixed mode, sharing
OS name : Windows 10
OS version : 10.0

Please reopen it because it blocking update to Spring Boot 3.

@ascopes
Copy link
Contributor Author

ascopes commented Jun 19, 2023

@plmakoc probably worth making a new issue and linking this one.

I can replicate it with termux's openjdk build as well on Java 17.

@plmakoc
Copy link

plmakoc commented Jun 20, 2023

Ok, I will make, becuase I got this issue also using Amazon Correto 17

@chris-hatton
Copy link

Why on earth is this closed!? A long thread of people confirming a serious issue with Mockito, and then it just get closed?

@ascopes
Copy link
Contributor Author

ascopes commented Jul 11, 2023

@TimvdLippe is it worth reopening this?

@TimvdLippe
Copy link
Contributor

If anybody can submit us a PR with a regression test setup that allows us to debug what is going on here, we can triage this further. For now, we have seen this error pop up as a result of local setup problems (e.g. missing permissions), unrelated to Mockito. Still, if we can have a regression test to show this with Mockito on our CI, we can work on fixing it.

@TimvdLippe TimvdLippe reopened this Jul 11, 2023
@ascopes
Copy link
Contributor Author

ascopes commented Jul 11, 2023

Right now, running this on Termux within Android on OpenJDK 17 seems to replicate this for me (Android 13, ARM)

Might be a red herring, but is probably worth looking into. Even if it is a problem with certain Java buillds, providing a more descriptive error message in these cases may be useful (given the complexity of how ByteBuddy and Mockito instrument things internally)

@concurrent-recursion
Copy link

I just figured this out on my machine, it was caused by the security software on my computer killing the Java process. It is flagged as an "OopAllocate" violation (I'm guessing its Out Of Process Memory Allocation or something similar) which triggers the software to terminate the process.

You may want to check your Event Viewer Logs to see if there is anything created there when running your project.

I was able to work around the issue by running a docker container with maven:3-openjdk-17 image to confirm that the maven project and code is fine, its something in my operating system

If it helps anyone here is the command I used to troubleshoot
docker run -it -v C:\path\to\code:/code -v C:\Users\myuser\.m2:/root/.m2 maven:3-openjdk-17 /bin/sh
Then in the terminal, cd /code and run your maven commands

@eolivelli
Copy link

This option saved my life
-Djdk.lang.Process.launchMechanism=vfork

@LitschiW
Copy link

LitschiW commented Dec 8, 2023

We have the same issue running tests on docker with maven:3-temurin-17 and maven:3-openjdk-17. Running with
MAVEN_OPTS="-Dmaven.deploy.skip=true -Djdk.attach.allowAttachSelf=true -Djdk.lang.Process.launchMechanism=vfork" and Mockito 5.8.0

Caused by: org.mockito.exceptions.base.MockitoInitializationException: 
Could not initialize inline Byte Buddy mock maker.

It appears as if your JDK does not supply a working agent attachment mechanism.
Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17.0.2+8-86
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17.0.2+8-86
JVM info           : mixed mode, sharing
OS name            : Linux
OS version         : 6.2.0-36-generic

Edit
After further testing: We also unsuccessfuly tested with an Azulu based image. The issue could also be recreated running with podman on a macOS 14 host and kernel 6.6.x (fedora something).

We had a successful run on using podman on an Ubuntu 22.04 host with kernel 6.2.0 and a temurin-based image. However the same image does not work in our CI pipeline using the same kernel.
We had success using maven:3-ibm-semeru-17 tho, but did not find any cause yet.

@mstephenson6
Copy link

mstephenson6 commented Dec 8, 2023

With Rancher Desktop on macOS, I discovered I needed to use a VZ virtual machine and virtiofs mounts when trying to run Mockito in a JDK container sharing my host fs. I saw ByteBuddy's JVM attachment quickly create and remove some temp files (with pid in the name), and I believe the default mount type just couldn't support that.

@stleray
Copy link

stleray commented Jan 3, 2024

I got the same issue but only as root.
It seems that mockito fails to attach to a java process launched by root.
As a standard user, all my tests passed.

@sbailliez
Copy link

Can reproduce the issue using Mockito 5.10.0 and ByteBuddy 1.14.12 (or 1.14.11), I had to stick with 4.11.0 which works fine.

This does not work running on a vmware fusion instance of ubuntu:

Ubuntu 20.04.4 LTS
Linux dev-m1 5.4.0-100-generic #113-Ubuntu SMP Thu Feb 3 18:44:51 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux
openjdk version "21.0.1" 2023-10-17 LTS
OpenJDK Runtime Environment Corretto-21.0.1.12.1 (build 21.0.1+12-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.1.12.1 (build 21.0.1+12-LTS, mixed mode, sharing)

This does not work either on openjdk 17.

This does work however on host OS, macOS:

Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:44 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6000 arm64
openjdk version "21.0.1" 2023-10-17 LTS
OpenJDK Runtime Environment Corretto-21.0.1.12.1 (build 21.0.1+12-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.1.12.1 (build 21.0.1+12-LTS, mixed mode, sharing)

On Ubuntu, I get:

java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
	at org.mockito.internal.configuration.plugins.PluginLoader$1.invoke(PluginLoader.java:85)
	at jdk.proxy2/jdk.proxy2.$Proxy22.isTypeMockable(Unknown Source)
	at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:78)
	at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22)
	at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:275)
	at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:236)
	at org.mockito.internal.MockitoCore.mock(MockitoCore.java:82)
	at org.mockito.Mockito.mock(Mockito.java:2104)
	at org.mockito.Mockito.mock(Mockito.java:2019)
	at com.zola.commons.util.log.MaskingPatternLayoutTest.apply(MaskingPatternLayoutTest.java:23)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.IllegalStateException: Internal problem occurred, please report it. Mockito is unable to load the default implementation of class that is a part of Mockito distribution. Failed to load interface org.mockito.plugins.MockMaker
	at org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.create(DefaultMockitoPlugins.java:105)
	at org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.getDefaultPlugin(DefaultMockitoPlugins.java:79)
	at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:75)
	at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:49)
	at org.mockito.internal.configuration.plugins.PluginRegistry.<init>(PluginRegistry.java:29)
	at org.mockito.internal.configuration.plugins.Plugins.<clinit>(Plugins.java:26)
	at org.mockito.internal.MockitoCore.<clinit>(MockitoCore.java:71)
	at org.mockito.Mockito.<clinit>(Mockito.java:1683)
	at org.mockito.junit.jupiter.MockitoExtension.beforeEach(MockitoExtension.java:156)
	... 2 more
Caused by: java.lang.reflect.InvocationTargetException
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.create(DefaultMockitoPlugins.java:103)
	... 10 more
Caused by: org.mockito.exceptions.base.MockitoInitializationException:
Could not initialize inline Byte Buddy mock maker.

It appears as if your JDK does not supply a working agent attachment mechanism.
Java               : 21
JVM vendor name    : Amazon.com Inc.
JVM vendor version : 21.0.1+12-LTS
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 21.0.1+12-LTS
JVM info           : mixed mode, sharing
OS name            : Linux
OS version         : 5.4.0-100-generic

	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.<init>(InlineDelegateByteBuddyMockMaker.java:260)
	at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.<init>(InlineByteBuddyMockMaker.java:23)
	... 13 more
Caused by: java.lang.IllegalStateException: Could not self-attach to current VM using external process
	at net.bytebuddy.agent.ByteBuddyAgent.installExternal(ByteBuddyAgent.java:706)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:636)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:616)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:568)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:545)
	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.<clinit>(InlineDelegateByteBuddyMockMaker.java:133)
	... 14 more```

@ascopes
Copy link
Contributor Author

ascopes commented Feb 26, 2024

@sbailliez are you able to possibly provide something like a Vagrantfile that can be used to consistently reproduce this issue? I was only able to reproduce it originally on an older JVM and on Fedora but updating fixed it. The other place I have been able to reproduce it was via Termux but I have since contributed a change that detects the latter edge case.

This looks like it is related to attaching from a separate process... could it be SELinux related or something perhaps? i.e. if you ran # setenforce Permissive then is it still an issue?

@sbailliez
Copy link

@ascopes I will try to find time to investigate this week and either debug or provide a way to reproduce easily

@sbailliez
Copy link

Update :when debugging, I found the bytebuddy system propertynet.bytebuddy.agent.attacher.dump that can be used to have a dump of what the process fails with.

So you can run

mvn test -Dtest=MyFailingTest -X -Dnet.bytebuddy.agent.attacher.dump=bytebuddy-mockito.log

which gave me

java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at net.bytebuddy.agent.Attacher.install(Attacher.java:102)
        at net.bytebuddy.agent.Attacher.main(Attacher.java:64)
Caused by: com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file /proc/2318086/root/tmp/.java_pid2318086: target process 2318086 doesn't respond within 10500ms or HotSpot VM not loaded
        at jdk.attach/sun.tools.attach.VirtualMachineImpl.<init>(VirtualMachineImpl.java:99)
        at jdk.attach/sun.tools.attach.AttachProviderImpl.attachVirtualMachine(AttachProviderImpl.java:58)
        at jdk.attach/com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:207)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        ... 3 more

behind the scene the bytebuddy agent tries to run this process:

/usr/lib/jvm/java-21-corretto/amazon-corretto-21.0.1.12.1-linux-aarch64/bin/java -Dnet.bytebuddy.agent.attacher.dump=bytebuddy-mockito.log -cp /web/.m2/repository/net/bytebuddy/byte-buddy-agent/1.14.11/byte-buddy-agent-1.14.11.jar net.bytebuddy.agent.Attacher com.sun.tools.attach.VirtualMachine 2318086 /home/vagrant/.m2/repository/net/bytebuddy/byte-buddy-agent/1.14.11/byte-buddy-agent-1.14.11.jar false

The failure seems to match raphw/byte-buddy#612

I was able to make it work by configuring the surefire plugin to pass -Djdk.attach.allowAttachSelf=true -XX:+StartAttachListener:

in my case:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>@{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED -Djdk.attach.allowAttachSelf=true -XX:+StartAttachListener</argLine>
    </configuration>
</plugin>

This might be helpful for people looking for a workaround, but trying to dig deeper.

@sbailliez
Copy link

Also adding for context that SELinux is NOT enabled

getenforce

Disabled

sestatus

SELinux status:                 disabled

@sbailliez
Copy link

I have managed to reproduce the issue and narrowed it down to a file attribute issue. In my current vmware/ubuntu/vagrant setup, I have nfs to share code between the host and guest.

Any file that gets created on nfs ends up with a uid/gid that does not match the uid/gid of the current user.

I originally used this piece of code to run the bytebuddy agent install:

import net.bytebuddy.agent.ByteBuddyAgent;
import com.sun.security.auth.module.UnixSystem;
import java.lang.instrument.Instrumentation;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.File;

public class Bug {


    public void check() throws Exception {
        System.out.println("Current pid: " + ProcessHandle.current().pid());
        System.out.println("Current command line: " + ProcessHandle.current().info().commandLine().orElse(null));

        UnixSystem system = new com.sun.security.auth.module.UnixSystem();
        System.out.format("User uid:gid -> %d:%d%n", system.getUid(), system.getGid());

        // get the class file path, won't work as is in a jar, that's ugly but good enough for this
        File location = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile());
        Path classFile = new File(location, getClass().getName().replace('.', '/') + ".class").toPath();
        System.out.format("Current class file -> %s%n", classFile);

        // now get the uid and guid of the current file
        int uidFile = (Integer)Files.getAttribute(classFile, "unix:uid");
        int gidFile = (Integer)Files.getAttribute(classFile, "unix:gid");
        System.out.format("File uid/gid -> %d:%d%n", uidFile, gidFile);

        if (system.getUid() != uidFile || system.getGid() != gidFile) {
            System.out.println("uid/gid of current user and class do not match. This should FAIL");
        } else {
            System.out.println("uid/gid of current user and class do match. This should SUCCEED");
        }

        // let's install the agent...moment of truth
        try {
            Instrumentation instrumentation = ByteBuddyAgent.install();
            System.out.println("SUCCESS");
        } catch (Exception e) {
            System.out.println("FAILED");
            throw e;
        }
    }

    public static void main(String[] args) throws Exception {
        Bug test = new Bug();
        test.check();
    }
}

Let's compile it via javac -cp byte-buddy-agent-1.14.11.jar Bug.java

On the NFS shared folder, I have:

-rw-rw-r-- 1 501 dialout   3305 Feb 27 02:48 Bug.class
-rw-rw-r-- 1 501 dialout   2007 Feb 27 02:48 Bug.java
-rw-r--r-- 1 501 dialout 256776 Feb 27 02:48 byte-buddy-agent-1.14.11.jar

If I execute via java -cp byte-buddy-agent-1.14.11.jar:. Bug I get:

Current pid: 2331442
Current command line: /usr/lib/jvm/java-21-corretto/amazon-corretto-21.0.1.12.1-linux-aarch64/bin/java -cp byte-buddy-agent-1.14.11.jar:. Bug
User uid:gid -> 1000:1000
Current class file -> /web/tmp/mockito/data/Bug.class
File uid/gid -> 501:20
uid/gid of current user and class do not match. This should FAIL
2024-02-27 03:01:19
Full thread dump OpenJDK 64-Bit Server VM (21.0.1+12-LTS mixed mode, sharing):
[...multiple thread dump ...]

whereas on a normal directory I have the vagrant/vagrant user/group

-rw-rw-r-- 1 vagrant vagrant   3305 Feb 27 02:47 Bug.class
-rw-rw-r-- 1 vagrant vagrant   2007 Feb 27 02:47 Bug.java
-rw-r--r-- 1 vagrant vagrant 256776 Feb 27 02:04 byte-buddy-agent-1.14.11.jar

When I execute I get:

Current pid: 2331599
Current command line: /usr/lib/jvm/java-21-corretto/amazon-corretto-21.0.1.12.1-linux-aarch64/bin/java -cp byte-buddy-agent-1.14.11.jar:. Bug
User uid:gid -> 1000:1000
Current class file -> /home/vagrant/mockito/bug/Bug.class
File uid/gid -> 1000:1000
uid/gid of current user and class do match. This should SUCCEED
WARNING: A Java agent has been loaded dynamically (/home/vagrant/mockito/bug/byte-buddy-agent-1.14.11.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
SUCCESS

ff I'm looking at the openjdk code it looks like it is more the temporary file that gets created and then there is a mismatch uid happening.

I don't see any easy way to identify this, would be nice if bytebuddy was doing but, otherwise mockito could try creating a file in the working directory and check for resulting uid/gid compare to the user one.

@ascopes
Copy link
Contributor Author

ascopes commented Feb 27, 2024

@sbailliez that's interesting... does setting the default permissions of that directory or setting -Djava.tmpdir="$(pwd)/tmp" make any difference? This might also explain why this misbehaves on Termux which would be cool to be able to fix.

Likely something @TimvdLippe would be able to advise on anyway!

@sbailliez
Copy link

sbailliez commented Feb 27, 2024

The JVM checks in the working directory for the file it creates named .attach_pid<pid> file, the issue is actually visible in the JVM debug log (just needed to know where to look) when running the jvm in trace logs.

My tmp directory is always in a "good" directory that will behave properly as it is not on nfs.

Running with the current working directory on NFS:

java -Xlog:attach=trace -cp byte-buddy-agent-1.14.11.jar:. Bug

Eventually in the log, there is a line:

[5.887s][debug][attach                  ] File .attach_pid2340476 has wrong user id 501 (vs 1000). Attach is not triggered

If I cd in a non-nfs working directory while pointing to the files on nfs volume (for me /web is nfs), it works:

java -Xlog:attach=trace  -cp /web/tmp/mockito/data/byte-buddy-agent-1.14.11.jar:/web/tmp/mockito/data Bug
[0.334s][trace][attach                  ] Attach triggered by .attach_pid2341148

@sbailliez
Copy link

sbailliez commented Feb 27, 2024

I updated the code to check for the real problem, it creates a temporary file in the current working directory and checks for the user id the same way the jvm does:

import net.bytebuddy.agent.ByteBuddyAgent;
import com.sun.security.auth.module.UnixSystem;
import java.lang.instrument.Instrumentation;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.File;

public class Bug {


    public void check() throws Exception {
        System.out.println("Current pid: " + ProcessHandle.current().pid());
        System.out.println("Current command line: " + ProcessHandle.current().info().commandLine().orElse(null));

        UnixSystem system = new com.sun.security.auth.module.UnixSystem();
        System.out.format("User uid:gid -> %d:%d%n", system.getUid(), system.getGid());

        // get the class file path, won't work as is in a jar, that's ugly but good enough for this
        File checkFile = new File(".check_pid%d".formatted(ProcessHandle.current().pid())).getAbsoluteFile();
        if (checkFile.createNewFile()) {
            System.out.format("Created check file -> %s%n", checkFile);
        }

        // now get the uid and guid of the current file
        int uidFile = (Integer)Files.getAttribute(checkFile.toPath(), "unix:uid");
        System.out.format("File uid -> %d%n", uidFile);

        if (system.getUid() != uidFile) {
            System.out.format("Check file %s has different uid %d (vs %d). This should FAIL%n", checkFile, uidFile, system.getUid());
        } else {
            System.out.format("Check file %s has identical uid %s. This should SUCCEED%n", checkFile, uidFile);
        }

        // let's install the agent...moment of truth
        try {
            Instrumentation instrumentation = ByteBuddyAgent.install();
            System.out.println("SUCCESS");
        } catch (Exception e) {
            System.out.println("FAILED");
            throw e;
        }
    }

    public static void main(String[] args) throws Exception {
        Bug test = new Bug();
        test.check();
    }
}

When I execute from a working directory on nfs I get the uid mismatch as described above:

Current pid: 2342178
Current command line: /usr/lib/jvm/java-21-corretto/amazon-corretto-21.0.1.12.1-linux-aarch64/bin/java -cp /web/tmp/mockito/data/byte-buddy-agent-1.14.11.jar:/web/tmp/mockito/data Bug
User uid:gid -> 1000:1000
Created check file -> /web/tmp/mockito/data/.check_pid2342178
File uid -> 501
Check file /web/tmp/mockito/data/.check_pid2342178 has different uid 501 (vs 1000). This should FAIL
[3 thread dumps]
FAILED
Exception in thread "main" java.lang.IllegalStateException: Could not self-attach to current VM using external process
	at net.bytebuddy.agent.ByteBuddyAgent.installExternal(ByteBuddyAgent.java:706)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:636)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:616)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:568)
	at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:545)
	at Bug.check(Bug.java:36)
	at Bug.main(Bug.java:46)

@TimvdLippe
Copy link
Contributor

To me this indeed sounds like a feature improvement for ByteBuddy to improve error reporting here. As Mockito, we consume the ByteBuddy package for instrumentation. If instrumentation fails, we rely on ByteBuddy to provide that error. I see you filed raphw/byte-buddy#1602 which sounds like a good feature improvement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests