BlockHound icon indicating copy to clipboard operation
BlockHound copied to clipboard

BlockHound.install() fails with NPE on jdk17 if application is started as java module

Open dvoloshyn opened this issue 4 years ago • 3 comments

BlockHound.install(); makes jvm exit because of unhandled exception: dynamicThreadPredicate is null.

Steps to Reproduce

  • Use Oracle JDK 17,
  • Create spring-boot application
  • Add module-info.java with required dependencies
  • Use spring-boot-starter-log4j2:2.5.5 and disruptor:3.4.4
  • Use the following VM options:
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector -XX:+AllowRedefinitionToAddDeleteMethods
  • execute BlockHound.install(); inside the static {} section of the main class
  • Put breakpoint into BlockHoundRuntime:62 and run the application from Intellij IDEA
  • Breakpoint will be hit with the following stacktrace:
"Log4j2-TF-1-AsyncLogger[AsyncContext@340f438e]-1@2177" daemon prio=5 tid=0x13 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at reactor.blockhound.BlockHoundRuntime.lambda$static$0(BlockHoundRuntime.java:62)
	  at reactor.blockhound.BlockHoundRuntime$$Lambda$247/0x0000000800c64260.get(Unknown Source:-1)
	  at java.lang.ThreadLocal$SuppliedThreadLocal.initialValue(ThreadLocal.java:305)
	  at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:195)
	  at java.lang.ThreadLocal.get(ThreadLocal.java:172)
	  at reactor.blockhound.BlockHoundRuntime.checkBlocking(BlockHoundRuntime.java:78)
	  at jdk.internal.misc.Unsafe.park(Unsafe.java:-1)
	  at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252)
	  at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1672)
	  at com.lmax.disruptor.TimeoutBlockingWaitStrategy.waitFor(TimeoutBlockingWaitStrategy.java:38)
	  at com.lmax.disruptor.ProcessingSequenceBarrier.waitFor(ProcessingSequenceBarrier.java:56)
	  at com.lmax.disruptor.BatchEventProcessor.processEvents(BatchEventProcessor.java:159)
	  at com.lmax.disruptor.BatchEventProcessor.run(BatchEventProcessor.java:125)
	  at java.lang.Thread.run(Thread.java:833)
  • Observe that field dynamicThreadPredicate is null. Resuming results in NPE

Your Environment

reactor 3.4.10 blockhound 1.0.6.RELEASE Windows 10

java 17 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing)

dvoloshyn avatar Oct 05 '21 14:10 dvoloshyn

Side note found while reproducing: for the agent to attach, here is the module-info.java that I ended up with:

module blockhoundNpe.main {
	requires jdk.attach; //required for bytebuddy to attach
	requires jdk.unsupported; //required for Unsafe access by log4j
	requires reactor.blockhound;
	requires spring.boot;
	requires spring.boot.autoconfigure;
}

Otherwise, an IllegalStateException: No compatible attachment provider is available is thrown by bytebuddy.

simonbasle avatar Oct 06 '21 10:10 simonbasle

I'll need @bsideup's expertise here. It looks like with modules there is either an issue with BlockHoundRuntime being loaded outside the bootstrap classloader, or some initialization change that leads to out-of-order usage vs loading of blockhound...

In any case, BlockhoundRuntime.STATE.withInitial block sees null values for the predicates and the blockingMethodConsumer.

I tried to give these default values, but that renders Blockhound#testInstrumentation non-functional 😞

simonbasle avatar Oct 06 '21 11:10 simonbasle

I met the same issue in open JDK 11. Anyone can support it or fix it? It looks the BlockHound community is inactive now.

0 = {StackTraceElement@3075} "reactor.blockhound.BlockHoundRuntime.lambda$static$0(BlockHoundRuntime.java:62)" 1 = {StackTraceElement@3076} "java.base/java.lang.ThreadLocal$SuppliedThreadLocal.initialValue(ThreadLocal.java:305)" 2 = {StackTraceElement@3077} "java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:195)" 3 = {StackTraceElement@3078} "java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)" 4 = {StackTraceElement@3079} "java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)" 5 = {StackTraceElement@3080} "reactor.blockhound.TestThread.(TestThread.java:29)" 6 = {StackTraceElement@3081} "reactor.blockhound.BlockHound$Builder.testInstrumentation(BlockHound.java:462)" 7 = {StackTraceElement@3082} "reactor.blockhound.BlockHound$Builder.install(BlockHound.java:445)" 8 = {StackTraceElement@3083} "reactor.blockhound.BlockHound.install(BlockHound.java:95)"

KasperDeng avatar Oct 14 '22 17:10 KasperDeng

it seems that there is a work around: since #297, we can now start the blockhound agent using the jvm option -javaagent, which seems to work well in jpms. You don't need to call BlockHound.install() from the code, just start the jvm with the -javaagent:; and blockhound will even be able to locate BlockHoundIntegration plugins declared inside modules (named, un-named, or automatic modules), because ServiceLoader also locates SPIs from modules.

I'm attaching a sample project (see README.MD file), which is using reactor-netty in a jpms environment:

blockhound-jpms.tgz

pderop avatar Jan 20 '23 16:01 pderop

I'm closing this issue because there is a work around (see previous posted sample demo project). Feel free to reopen if needed.

pderop avatar Jan 20 '23 16:01 pderop