I have some library code that would like to be able to gather Ruby backtraces for all Ruby threads in a process. It does the moral equivalent of a 'Thread.list.map(&:backtrace)'.
In some applications, this works fine under JRuby, however in others, I get a NullPointerException upon calling Thread#backtrace.
I've narrowed one such case down to a small, reproducible test case (attached). The test case backticks 'uname -p' 100 times, then tries to gather backtraces of all Ruby threads 100 times, and consistently fails for me will the following error:
ThreadContext.java:756:in `gatherCallerBacktrace': java.lang.NullPointerException
from ThreadContext.java:727:in `createCallerBacktrace'
from RubyThread.java:973:in `backtrace'
from RubyThread$INVOKER$i$0$0$backtrace.gen:-1:in `call'
from JavaMethod.java:861:in `call'
from CachingCallSite.java:70:in `call'
from RubySymbol.java:428:in `yieldInner'
from RubySymbol.java:433:in `yield'
from Block.java:130:in `yield'
from RubyArray.java:2347:in `collect'
from RubyArray.java:2360:in `map19'
from RubyArray$INVOKER$i$0$0$map19.gen:-1:in `call'
from CachingCallSite.java:316:in `cacheAndCall'
from CachingCallSite.java:145:in `callBlock'
from CachingCallSite.java:149:in `call'
from backtrace_npe.rb:2:in `block_1$RUBY$_file_'
from backtrace_npe$block_1$RUBY$_file_:-1:in `call'
from CompiledBlock19.java:139:in `yield'
from Block.java:130:in `yield'
from RubyFixnum.java:273:in `times'
from RubyFixnum$INVOKER$i$0$0$times.gen:-1:in `call'
from CachingCallSite.java:316:in `cacheAndCall'
from CachingCallSite.java:145:in `callBlock'
from CachingCallSite.java:154:in `callIter'
from backtrace_npe.rb:2:in `_file_'
from backtrace_npe.rb:-1:in `load'
from Ruby.java:779:in `runScript'
from Ruby.java:772:in `runScript'
from Ruby.java:649:in `runNormally'
from Ruby.java:498:in `runFromMain'
from Main.java:375:in `doRunFromMain'
from Main.java:264:in `internalRun'
from Main.java:230:in `run'
from Main.java:214:in `run'
from Main.java:194:in `main'
Not doing the backticking seems to make the issue go away.
I have minimal Java experience or knowledge, but digging around a bit with heap dumps in Visual VM, it looks like maybe the thread that we're failing to backtrace is an 'adopted' Ruby thread (presumably created by Java, adopted by Ruby as a side-effect of the backticking?).
It makes sense that an adopted Java thread like this might not have a valid Ruby backtrace, but I'd expect Thread#backtrace to return nil or [] in this case, rather than producing a NullPointerException.
|