reactor / BlockHound

Java agent to detect blocking calls from non-blocking threads.
Apache License 2.0
1.34k stars 98 forks source link

OpenJDK 13 requires -XX:+AllowRedefinitionToAddDeleteMethods flag #33

Open juergenzimmermann opened 5 years ago

juergenzimmermann commented 5 years ago

OpenJDK 12.0.1 with even Kotlin 1.3.40-eap-67 works fine. However, using OpenJDK 13 build 23 I get the following exception:

Exception in thread "main" java.lang.RuntimeException: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
        at reactor.blockhound.BlockHound$Builder.install(BlockHound.java:264)
        at reactor.blockhound.BlockHound.install(BlockHound.java:64)
        at de.hska.kunde.ApplicationKt.main(Application.kt:51)
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
        at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
        at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
        at reactor.blockhound.BlockHound$Builder.instrument(BlockHound.java:273)
        at reactor.blockhound.BlockHound$Builder.install(BlockHound.java:261)
        ... 2 more
bsideup commented 5 years ago

Hi @juergenzimmermann,

I'm afraid that's due to https://bugs.openjdk.java.net/browse/JDK-8192936 and similar to #21 In JDK 13+, they adjusted the spec & impl and no longer allow redefining native methods :(

We will need to find an alternative way for 13+ and J9, meanwhile, they seem to plan adding a flag to keep the behaviour for a few versions: https://bugs.openjdk.java.net/browse/JDK-8192936?focusedCommentId=14254266&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-14254266

juergenzimmermann commented 5 years ago

@bsideup When I use -XX:+AllowRedefinitionToAddDeleteMethods the stacktrace goes away

bsideup commented 5 years ago

@juergenzimmermann thanks a lot for trying it! I will keep this issue open as a reminder to document this flag 👍

yburkouski commented 5 years ago

Heaving quite different behavior on Oracle JDK 13 in Windows: it does not throw an error about blocking call until I add this flag

sergey-morenets commented 4 years ago

Still reproduced with JDK 14.0.1 and BlockHound 1.0.3

jaschenk commented 4 years ago

Using Blockhound 1.0.4 and JDK 14.0.2 and specify the following in surefire plugin worked:

<plugins>
 ...
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>2.22.2</version>
             <configuration>
                 <argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
             </configuration>
         </plugin>
 ...
 </plugins>
abdullahumer commented 3 years ago

any way this can be added to gradle?

bsideup commented 3 years ago

@abdullahumer sure:

    tasks.withType(Test).all {
        if (JavaVersion.current().isCompatibleWith​(JavaVersion.VERSION_13)) {
            jvmArgs += [
                    "-XX:+AllowRedefinitionToAddDeleteMethods"
            ]
        }
    }
abdullahumer commented 3 years ago

Thanks! @bsideup

roimenashe commented 3 years ago

Still reproduced with JDK 15.0.2 and BlockHound 1.0.6.RELEASE. "-XX:+AllowRedefinitionToAddDeleteMethods" option still shows deprecation warning. Any updates?

CodeGTDLearn commented 2 years ago

Using Blockhound 1.0.4 and JDK 14.0.2 and specify the following in surefire plugin worked:

<plugins>
 ...
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>2.22.2</version>
             <configuration>
                 <argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
             </configuration>
         </plugin>
 ...
 </plugins>

In my case Using Blockhound 1.0.6 and JDK 15.0.6, worked

KrzysztofKwiatkowskiK commented 2 years ago

Using blockhound 1.0.6 and JDK 16.0.2 I am still getting this error and has to use the flag...

ajax-semenov-y commented 2 years ago

Hi. As far as I understand, according to https://bugs.openjdk.org/browse/JDK-8221528.

"This option (AllowRedefinitionToAddOrDeleteMethods) is deprecated right away. The plan is to keep it for a couple of releases to allow customers (tool vendors) to remove dependency on old behavior from their tools."

Do you plan BlockHound work according to the new JVM TI behavior?

juliojgd commented 1 year ago

I guess that if this issue is not solved, when the option AllowRedefinitionToAddOrDeleteMethods is removed from JVM, then Blockhound is completely unusable, right?

patpatpat123 commented 1 year ago

Now in 2023, almost four years after the original issue, would it be possible to have a fix without the -XX workaround please?

pderop commented 1 year ago

I will take a look, but I don't promise anything for the moment, because looking at the history of this issue, without the work around flag, it seems to be a very very big deal to redefine / retransform classes (add/delete private static and private final instance methods in the new class versions). so, I'm afraid that for the moment, we need to continue using -XX:+AllowRedefinitionToAddDeleteMethods for JDK13+ Is there any issues if you continue using this flag for the moment ?

thanks.

divyanshsaini1805 commented 1 year ago

i am using Java 17 and Spring 3.1.3, even after including the -XX:+AllowRedefinitionToAddDeleteMethods flag, i am still facing the error. Is the flag removed from JVM or has the fix stopped working?

pderop commented 1 year ago

Hi @divyanshsaini1805 ,

please open a separate issue with a reproducer project; From my side, I did the following which worked for me using Spring 3.1.3, and java 17:

build.gradle:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.3'
    id 'io.spring.dependency-management' version '1.1.3'
}

apply plugin: 'application'

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'io.projectreactor.netty:reactor-netty-core:1.0.37'
    implementation 'io.projectreactor.netty:reactor-netty-http:1.0.37'
    implementation('io.projectreactor.tools:blockhound:1.0.8.RELEASE')
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

task(echoServer, dependsOn: 'classes', type: JavaExec) {
    main = 'com.example.EchoServer'
    classpath = sourceSets.main.runtimeClasspath
    args ''
    systemProperty "io.netty.leakDetectionLevel", "DISABLED"
    debugOptions {
        enabled = false
        port = 5005
        server = true
        suspend = true
    }
}

DemoApplication.java:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Demo.java:

package com.example.demo;

import org.springframework.stereotype.Component;
import reactor.blockhound.BlockHound;
import reactor.netty.tcp.TcpServer;

import javax.annotation.PostConstruct;

@Component
public class Demo {
    static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));

    @PostConstruct
    public void init() {
        System.out.println("Jdk version used: " + System.getProperty("java.version"));
        BlockHound.install();

        TcpServer server =
                TcpServer.create()
                        .port(PORT)
                        .handle((in, out) -> {
                            try {
                                Thread.sleep(100);
                            } catch (Throwable err) {
                                err.printStackTrace();
                            }
                            return out.send(in.receive().retain());
                        });

        server.bindNow()
                .onDispose()
                .block();
    }
}

then, from one console using java17:

./gradlew build
java -XX:+AllowRedefinitionToAddDeleteMethods -jar build/libs/demo-0.0.1-SNAPSHOT.jar

from another console:

curl -v http://localhost:8080/foo

this will display the following from the first console:

reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep
    at java.base/java.lang.Thread.sleep(Thread.java)
    at com.example.demo.Demo.lambda$init$0(Demo.java:34)
    at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:277)
    at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:264)
    at reactor.netty.transport.ServerTransportConfig$ServerTransportDoOnConnection.onStateChange(ServerTransportConfig.java:253)
    at reactor.netty.transport.ServerTransport$ChildObserver.onStateChange(ServerTransport.java:481)
    at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:262)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:231)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1398)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:258)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
    at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:895)
    at io.netty.channel.AbstractChannel$AbstractUnsafe.register0(AbstractChannel.java:522)
    at io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(AbstractChannel.java:429)
    at io.netty.channel.AbstractChannel$AbstractUnsafe$1.run(AbstractChannel.java:486)
    at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:833)

Thanks.

apferrarone commented 10 months ago

I have tried on windows with corretto-17 and temurin-17. When I add the vm option to the Surefire plugin config in pom.xml, my unit tests work with BlockHound. But when I add the vm option to my IntelliJ run configuration or in the vm options file I still see this error when I start up my springboot app:

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.IllegalStateException: The instrumentation have failed.
It looks like you're running on JDK 13+.
You need to add '-XX:+AllowRedefinitionToAddDeleteMethods' JVM flag.
public static void main(String[] args) {
    System.out.println(args[0] + " ");
    SpringApplication.run(BillGenerationServiceApplication.class, args);
}

I see this: -XX:+AllowRedefinitionToAddDeleteMethods at the top of the console.

Any ideas? Thanks.

divyanshsaini1805 commented 10 months ago

Hi @divyanshsaini1805 ,

please open a separate issue with a reproducer project; From my side, I did the following which worked for me using Spring 3.1.3, and java 17:

build.gradle:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.3'
    id 'io.spring.dependency-management' version '1.1.3'
}

apply plugin: 'application'

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'io.projectreactor.netty:reactor-netty-core:1.0.37'
    implementation 'io.projectreactor.netty:reactor-netty-http:1.0.37'
    implementation('io.projectreactor.tools:blockhound:1.0.8.RELEASE')
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

task(echoServer, dependsOn: 'classes', type: JavaExec) {
    main = 'com.example.EchoServer'
    classpath = sourceSets.main.runtimeClasspath
    args ''
    systemProperty "io.netty.leakDetectionLevel", "DISABLED"
    debugOptions {
        enabled = false
        port = 5005
        server = true
        suspend = true
    }
}

DemoApplication.java:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
  public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
  }
}

Demo.java:

package com.example.demo;

import org.springframework.stereotype.Component;
import reactor.blockhound.BlockHound;
import reactor.netty.tcp.TcpServer;

import javax.annotation.PostConstruct;

@Component
public class Demo {
    static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));

    @PostConstruct
    public void init() {
        System.out.println("Jdk version used: " + System.getProperty("java.version"));
        BlockHound.install();

        TcpServer server =
                TcpServer.create()
                        .port(PORT)
                        .handle((in, out) -> {
                            try {
                                Thread.sleep(100);
                            } catch (Throwable err) {
                                err.printStackTrace();
                            }
                            return out.send(in.receive().retain());
                        });

        server.bindNow()
                .onDispose()
                .block();
    }
}

then, from one console using java17:

./gradlew build
java -XX:+AllowRedefinitionToAddDeleteMethods -jar build/libs/demo-0.0.1-SNAPSHOT.jar

from another console:

curl -v http://localhost:8080/foo

this will display the following from the first console:

reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep
  at java.base/java.lang.Thread.sleep(Thread.java)
  at com.example.demo.Demo.lambda$init$0(Demo.java:34)
  at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:277)
  at reactor.netty.tcp.TcpServer$OnConnectionHandle.accept(TcpServer.java:264)
  at reactor.netty.transport.ServerTransportConfig$ServerTransportDoOnConnection.onStateChange(ServerTransportConfig.java:253)
  at reactor.netty.transport.ServerTransport$ChildObserver.onStateChange(ServerTransport.java:481)
  at reactor.netty.channel.ChannelOperationsHandler.channelActive(ChannelOperationsHandler.java:62)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:262)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
  at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:231)
  at io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(DefaultChannelPipeline.java:1398)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:258)
  at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:238)
  at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:895)
  at io.netty.channel.AbstractChannel$AbstractUnsafe.register0(AbstractChannel.java:522)
  at io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(AbstractChannel.java:429)
  at io.netty.channel.AbstractChannel$AbstractUnsafe$1.run(AbstractChannel.java:486)
  at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
  at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
  at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
  at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
  at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
  at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
  at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
  at java.base/java.lang.Thread.run(Thread.java:833)

Thanks.

hi, i know it's been a super long time, but since this thread is a lot active, i would like to add my part, for Spring 3.1.3, and java 17, the -XX:+AllowRedefinitionToAddDeleteMethods flag works good indeed if used with the following command: java -XX:+AllowRedefinitionToAddDeleteMethods -jar target/Required_jar_file.jar,