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

Could not self-attach to current VM using external process (ByteBuddyAgent.installExtension) #2741

Closed
swaechter opened this issue Aug 28, 2022 · 5 comments

Comments

@swaechter
Copy link

swaechter commented Aug 28, 2022

I am switching from Mockito 3 to 4.7.0 and Java 8 to 17. Some unit tests started to fail in a really strange way, so I asked a question on Stackoverflow (https://stackoverflow.com/questions/73492733/how-to-properly-spy-on-an-input-stream). Therefore I switched to mockito-inline and now I get a Could not self-attach to current VM using external process in net.bytebuddy.agent.ByteBuddyAgent.installExternal(ByteBuddyAgent.java:700). Source: https://github.com/raphw/byte-buddy/blob/master/byte-buddy-agent/src/main/java/net/bytebuddy/agent/ByteBuddyAgent.java#L689

My Gradle build.gradle file:

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.mockito:mockito-inline:4.7.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
}

test {
    useJUnitPlatform()
}

My JUnit test:

package ch.swaechter;

import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.spy;

public class InputStreamTest {

    @Test
    public void testInputStream() throws Exception {
        // Create a byte array input stream
        byte[] testData = "Hello".getBytes(StandardCharsets.UTF_8);
        InputStream inputStream = new ByteArrayInputStream(testData);

        // Spy on the object
        InputStream spiedInputStream = spy(inputStream);

        // Check the available length
        assertEquals(testData.length, spiedInputStream.available());
        System.out.println("=============================> " + spiedInputStream.available());
    }
}

The command I run:

./gradlew clean test -info > Output.log

This results in the following JVM crash (I guess), followed by several JVM restarts/re-attempts (Second guess): Output.log

InputStreamTest > testInputStream() FAILED
    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.proxy3/jdk.proxy3.$Proxy13.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:261)
        at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:234)
        at org.mockito.internal.MockitoCore.mock(MockitoCore.java:94)
        at org.mockito.Mockito.spy(Mockito.java:2064)
        at ch.swaechter.InputStreamTest.testInputStream(InputStreamTest.java:21)

        Caused by:
        java.lang.IllegalStateException: Failed to load interface org.mockito.plugins.MockMaker implementation declared in java.lang.CompoundEnumeration@431cd9b2
            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:28)
            at org.mockito.internal.configuration.plugins.Plugins.<clinit>(Plugins.java:22)
            at org.mockito.internal.MockitoCore.<clinit>(MockitoCore.java:77)
            at org.mockito.Mockito.<clinit>(Mockito.java:1630)
            ... 1 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)
                ... 7 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    : Debian
                JVM vendor version : 17.0.4+8-Debian-1deb11u1
                JVM name           : OpenJDK 64-Bit Server VM
                JVM version        : 17.0.4+8-Debian-1deb11u1
                JVM info           : mixed mode, sharing
                OS name            : Linux
                OS version         : 5.10.0-17-amd64
                    at app//org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.<init>(InlineDelegateByteBuddyMockMaker.java:244)
                    at app//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:700)
                        at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:631)
                        at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:611)
                        at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:563)
                        at net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:540)
                        at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.<clinit>(InlineDelegateByteBuddyMockMaker.java:115)
                        ... 14 more
Finished generating test XML results (0.012 secs) into: /home/acme/Workspace/mockitoinline/build/test-results/test
Generating HTML test report...
Finished generating test html results (0.01 secs) into: /home/acme/Workspace/mockitoinline/build/reports/tests/test
Watching 17 directories to track changes
Watching 22 directories to track changes
Watching 23 directories to track changes
:test (Thread[Execution worker Thread 2,5,main]) completed. Took 22.017 secs.
3 actionable tasks: 3 executed

What I checked:

  • The Java path does not contain a space (An issue already mentioned)
  • The class ByteArrayInputStream is not final

Version of the used components:

  • OS: Linux Debian 11, Kernel 5.10.0-17-amd64 SHARP 1 SMP Debian 5.10.136-1 (2022-08-13) x86_64 GNU/Linux
  • Java: OpenJDK 64-Bit Server VM (build 17.0.4+8-Debian-1deb11u1, mixed mode, sharing)
  • Gradle Wrapper: 7.5.1
  • Mockito: 4..7.0

Note: I can provide access to this system if whished :)

@TimvdLippe
Copy link
Contributor

I am switching from Mockito 3 to 4.7.0 and Java 8 to 17

Can you update them in separate steps, so that it is clearer which particular step is breaking it? E.g. you first upgrade your Mockito version to 4.7.0 and then later you upgrade to Java 17.

@swaechter
Copy link
Author

Hey Tim for sure:

1.) The initial situation with mockito-core and Java 8

  • Mockito: mockito-core 3.3.3
  • Java: OpenJDK 64-Bit Server VM (Temurin)(build 25.345-b01, mixed mode)
  • Result: Unit test works

2.) Now we switch from mockito-core to mockito-core to mockito-inline because we are aware of some potential issues in newer Java version.

  • Mockito: mockito-inline 4.7.0
  • Java: OpenJDK 64-Bit Server VM (Temurin)(build 25.345-b01, mixed mode)
  • Result: Unit test works

3.) Now we switch from Java 8 to Java 17. The mockito-inline version stays the same

  • Mockito: mockito-inline 4.7.0
  • Java: OpenJDK 64-Bit Server VM (build 17.0.4+8-Debian-1deb11u1, mixed mode, sharing)
  • Result: The JVM crash mentioned in the first post

So the breaking step is switching from Java 8 to 17

@TimvdLippe
Copy link
Contributor

Unfortunately, InputStream is one of the classes that Mockito relies on internally for its behavior. Stubbing InputStream will therefore lead to undefined behavior. Additionally, it is advised not to mock classes you don't own: https://github.com/mockito/mockito/wiki/How-to-write-good-tests#dont-mock-a-type-you-dont-own We are working on improving the user experience by working on a DoNotMock feature to avoid mocking classes/methods that are known to crash Mockito internals (#1833). Therefore, I am closing this as "Infeasible". Apologies for the uninformative exception that is thrown.

@swaechter
Copy link
Author

swaechter commented Feb 2, 2023

No problem at all and thanks for the explanation :) I never thought about the side effects mocking static methods can have in Mockito

@IUSR
Copy link

IUSR commented Feb 13, 2024

Seems like JDK on Linux is prone to having this problem when the executables' file are owned by a different user but the process is started by another, so I guess it's probably about capabilities. Anyway, seeing it requires self-attaching and only "CAP_SYS_PTRACE" seems relevant, I just tried adding "CAP_SYS_PTRACE" capability to the java executable of the JDK and it solved my problem. I'm not yet sure if this could be dangerous :D if that's a concern, try using an SDK where you own all the files, such as those installed via sdkman.

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

3 participants