引言
如果你像我一样是一个 Java 程序员,并且经常进行 JNI 代码的开发,那么你一定也体会到了调试 JNI 代码的困难,比如有一天突然程序意外崩溃了,我们很难搞清楚它到底是因为什么崩溃的。接下来我要介绍的这几个技术,可以帮助我们快速的解决上述问题。
解读 hs_err_pid 文件
如果 JVM 在执行 native c++ 代码(JNI)的时候崩溃了,一般来说 JVM 会将错误报告写到一个文件中,在 Linux 平台上这个文件形如 /tmp/jvm-8666/hs_error.log
, 而在 Mac 平台上,它的文件名类似于 hs_err_pid76448.log
,一般存放在 Java 进程的工作目录。
hs_err_pid 文件的内容可能如下所示:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007fff87283132, pid=76448, tid=5891
#
# JRE version: Java(TM) SE Runtime Environment (7.0_80-b15) (build 1.7.0_80-b15)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.80-b11 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# C [libsystem_c.dylib+0x1132] strlen+0x12
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
--------------- T H R E A D ---------------
Current thread (0x00007fc3c2007800): JavaThread "main" [_thread_in_native, id=5891, stack(0x000070000011a000,0x000070000021a000)]
siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x00000000fffffff0
Registers:
RAX=0x00000000ffffffff, RBX=0x00000000ffffffff, RCX=0x00000000ffffffff, RDX=0x00000000ffffffff
RSP=0x0000700000216450, RBP=0x0000700000216450, RSI=0x0000000000000007, RDI=0x00000000fffffff0
R8 =0x00000000fffffffc, R9 =0x00007fc3c143f8e8, R10=0x00000000ffffffff, R11=0x0000000102ac7f40
R12=0x00007fc3c288a600, R13=0x00007fc3c1442bf8, R14=0x00007fc3c288a638, R15=0x00007fc3c288a638
RIP=0x00007fff87283132, EFLAGS=0x0000000000010206, ERR=0x0000000000000004
TRAPNO=0x000000000000000e
Top of Stack: (sp=0x0000700000216450)
0x0000700000216450: 0000700000216490 0000000112bb538a
0x0000700000216460: 0000000000000000 0000000000000000
0x0000700000216470: 0000000000000000 00007fc3c289c800
0x0000700000216480: 00007fc3c288a638 00007fc3c143c0e8
0x0000700000216490: 00007000002166f0 0000000112bcd82c
0x00007000002164a0: 303733312e34333a 00007fc3c288a608
0x00007000002164b0: 0000700000216ca8 697265766f636552
0x00007000002164c0: 206d6f726620676e 74736566696e616d
0x00007000002164d0: 4d203a656c696620 2d54534546494e41
0x00007000002164e0: 00007fc3c289c800 00007fc3c143a870
0x00007000002164f0: 0000000000000060 00007fc3c1530e40
0x0000700000216500: 00007fc300000006 0000000100c2d000
0x0000700000216510: 00007fc3c1500000 0000700000216df0
0x0000700000216520: 0000700000216550 0000700000216df8
0x0000700000216530: 0000700000216e00 00007fc3c28d7000
0x0000700000216540: 0000000100c30a00 0000000000000006
0x0000700000216550: 0000700000216590 00007fff91b94154
0x0000700000216560: 00007fc3c28a6808 0000000000000004
0x0000700000216570: 0000000100c47a00 0000000100c2d000
0x0000700000216580: 0000000100c48e00 0000000000001400
0x0000700000216590: 0000700000216680 00007fff91b90ee5
0x00007000002165a0: 0000000000000001 00007fc3c1530e46
0x00007000002165b0: 00007000002166a0 00007fff91b90a26
0x00007000002165c0: 0000000000001400 0000000100c30a00
0x00007000002165d0: 00007000002166c0 00007fff91b90a26
0x00007000002165e0: 00007fc3c28a6808 0000000000000006
0x00007000002165f0: 0000000100c47a00 0000000100c2d000
0x0000700000216600: 0000000000001400 0000000100c30a00
0x0000700000216610: 0000700000216700 00000000000006e8
0x0000700000216620: 0000000000001002 0000000000c31e00
0x0000700000216630: 0000000000001002 0000000100c48e00
0x0000700000216640: ff80000000001002 00000000c153ffff
Instructions: (pc=0x00007fff87283132)
0x00007fff87283112: 0e 01 f3 0f 7f 44 0f 01 5d c3 90 90 90 90 55 48
0x00007fff87283122: 89 e5 48 89 f9 48 89 fa 48 83 e7 f0 66 0f ef c0
0x00007fff87283132: 66 0f 74 07 66 0f d7 f0 48 83 e1 0f 48 83 c8 ff
0x00007fff87283142: 48 d3 e0 21 c6 74 17 0f bc c6 48 29 d7 48 01 f8
Register to memory mapping:
RAX=0x00000000ffffffff is an unknown value
RBX=0x00000000ffffffff is an unknown value
RCX=0x00000000ffffffff is an unknown value
RDX=0x00000000ffffffff is an unknown value
RSP=0x0000700000216450 is pointing into the stack for thread: 0x00007fc3c2007800
RBP=0x0000700000216450 is pointing into the stack for thread: 0x00007fc3c2007800
RSI=0x0000000000000007 is an unknown value
RDI=0x00000000fffffff0 is an unknown value
R8 =0x00000000fffffffc is an unknown value
R9 =0x00007fc3c143f8e8 is an unknown value
R10=0x00000000ffffffff is an unknown value
R11=0x0000000102ac7f40 is at entry_point+0 in (nmethod*)0x0000000102ac7e10
R12=0x00007fc3c288a600 is an unknown value
R13=0x00007fc3c1442bf8 is an unknown value
R14=0x00007fc3c288a638 is an unknown value
R15=0x00007fc3c288a638 is an unknown value
Stack: [0x000070000011a000,0x000070000021a000], sp=0x0000700000216450, free space=1009k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [libsystem_c.dylib+0x1132] strlen+0x12
C [librocksdbjni-osx.jnilib+0x1c38a] rocksdb::InternalKeyComparator::InternalKeyComparator(rocksdb::Comparator const*)+0x4a
C [librocksdbjni-osx.jnilib+0x3482c] rocksdb::ColumnFamilyData::ColumnFamilyData(unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::Version*, rocksdb::Cache*, rocksdb::WriteBufferManager*, rocksdb::ColumnFamilyOptions const&, rocksdb::DBOptions const*, rocksdb::EnvOptions const&, rocksdb::ColumnFamilySet*)+0x7c
C [librocksdbjni-osx.jnilib+0x382dd] rocksdb::ColumnFamilySet::CreateColumnFamily(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int, rocksdb::Version*, rocksdb::ColumnFamilyOptions const&)+0x7d
C [librocksdbjni-osx.jnilib+0x12ca6f] rocksdb::VersionSet::CreateColumnFamily(rocksdb::ColumnFamilyOptions const&, rocksdb::VersionEdit*)+0xaf
C [librocksdbjni-osx.jnilib+0x12d831] rocksdb::VersionSet::Recover(std::__1::vector<rocksdb::ColumnFamilyDescriptor, std::__1::allocator<rocksdb::ColumnFamilyDescriptor> > const&, bool)+0xc51
C [librocksdbjni-osx.jnilib+0x80df4] rocksdb::DBImpl::Recover(std::__1::vector<rocksdb::ColumnFamilyDescriptor, std::__1::allocator<rocksdb::ColumnFamilyDescriptor> > const&, bool, bool, bool)+0x244
C [librocksdbjni-osx.jnilib+0x9c0aa] rocksdb::DB::Open(rocksdb::DBOptions const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<rocksdb::ColumnFamilyDescriptor, std::__1::allocator<rocksdb::ColumnFamilyDescriptor> > const&, std::__1::vector<rocksdb::ColumnFamilyHandle*, std::__1::allocator<rocksdb::ColumnFamilyHandle*> >*, rocksdb::DB**)+0xb0a
C [librocksdbjni-osx.jnilib+0x9b138] rocksdb::DB::Open(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)+0x448
C [librocksdbjni-osx.jnilib+0x1234c] std::__1::__function::__func<rocksdb::Status (*)(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**), std::__1::allocator<rocksdb::Status (*)(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)>, rocksdb::Status (rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)>::operator()(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**&&)+0x1c
C [librocksdbjni-osx.jnilib+0xd84b] rocksdb_open_helper(JNIEnv_*, long, _jstring*, std::__1::function<rocksdb::Status (rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)>)+0x8b
C [librocksdbjni-osx.jnilib+0xd9ab] Java_org_rocksdb_RocksDB_open__JLjava_lang_String_2+0x4b
j org.rocksdb.RocksDB.open(JLjava/lang/String;)J+0
j org.rocksdb.RocksDB.open(Lorg/rocksdb/Options;Ljava/lang/String;)Lorg/rocksdb/RocksDB;+9
j org.rocksdb.util.BytewiseComparatorTest.openDatabase(Ljava/nio/file/Path;Lorg/rocksdb/AbstractComparator;)Lorg/rocksdb/RocksDB;+28
j org.rocksdb.util.BytewiseComparatorTest.java_vs_java_directBytewiseComparator()V+37
v ~StubRoutines::call_stub
V [libjvm.dylib+0x2dc898] JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x22a
V [libjvm.dylib+0x2dc668] JavaCalls::call(JavaValue*, methodHandle, JavaCallArguments*, Thread*)+0x28
V [libjvm.dylib+0x468428] Reflection::invoke(instanceKlassHandle, methodHandle, Handle, bool, objArrayHandle, BasicType, objArrayHandle, bool, Thread*)+0x9fc
V [libjvm.dylib+0x46888e] Reflection::invoke_method(oopDesc*, Handle, objArrayHandle, Thread*)+0x16e
V [libjvm.dylib+0x329247] JVM_InvokeMethod+0x166
j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+57
j org.junit.runners.model.FrameworkMethod$1.runReflectiveCall()Ljava/lang/Object;+15
j org.junit.internal.runners.model.ReflectiveCallable.run()Ljava/lang/Object;+1
j org.junit.runners.model.FrameworkMethod.invokeExplosively(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+10
j org.junit.internal.runners.statements.InvokeMethod.evaluate()V+12
j org.junit.runners.ParentRunner.runLeaf(Lorg/junit/runners/model/Statement;Lorg/junit/runner/Description;Lorg/junit/runner/notification/RunNotifier;)V+17
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/runner/notification/RunNotifier;)V+30
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6
j org.junit.runners.ParentRunner$3.run()V+12
j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1
j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+44
j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.ParentRunner$2.evaluate()V+8
j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20
j org.junit.runners.Suite.runChild(Lorg/junit/runner/Runner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.Suite.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6
j org.junit.runners.ParentRunner$3.run()V+12
j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1
j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+44
j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.ParentRunner$2.evaluate()V+8
j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20
j org.junit.runner.JUnitCore.run(Lorg/junit/runner/Runner;)Lorg/junit/runner/Result;+37
j org.junit.runner.JUnitCore.run(Lorg/junit/runner/Request;)Lorg/junit/runner/Result;+5
j org.junit.runner.JUnitCore.run(Lorg/junit/runner/Computer;[Ljava/lang/Class;)Lorg/junit/runner/Result;+6
j org.junit.runner.JUnitCore.run([Ljava/lang/Class;)Lorg/junit/runner/Result;+5
j org.rocksdb.test.RocksJunitRunner.main([Ljava/lang/String;)V+93
v ~StubRoutines::call_stub
V [libjvm.dylib+0x2dc898] JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x22a
V [libjvm.dylib+0x2dc668] JavaCalls::call(JavaValue*, methodHandle, JavaCallArguments*, Thread*)+0x28
V [libjvm.dylib+0x31004e] jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*)+0xe6
V [libjvm.dylib+0x3092d5] jni_CallStaticVoidMethodV+0x9c
V [libjvm.dylib+0x31c28e] checked_jni_CallStaticVoidMethod+0x16f
C [java+0x30fe] JavaMain+0x91d
C [libsystem_pthread.dylib+0x399d] _pthread_body+0x83
C [libsystem_pthread.dylib+0x391a] _pthread_body+0x0
C [libsystem_pthread.dylib+0x1351] thread_start+0xd
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j org.rocksdb.RocksDB.open(JLjava/lang/String;)J+0
j org.rocksdb.RocksDB.open(Lorg/rocksdb/Options;Ljava/lang/String;)Lorg/rocksdb/RocksDB;+9
j org.rocksdb.util.BytewiseComparatorTest.openDatabase(Ljava/nio/file/Path;Lorg/rocksdb/AbstractComparator;)Lorg/rocksdb/RocksDB;+28
j org.rocksdb.util.BytewiseComparatorTest.java_vs_java_directBytewiseComparator()V+37
v ~StubRoutines::call_stub
j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+57
j org.junit.runners.model.FrameworkMethod$1.runReflectiveCall()Ljava/lang/Object;+15
j org.junit.internal.runners.model.ReflectiveCallable.run()Ljava/lang/Object;+1
j org.junit.runners.model.FrameworkMethod.invokeExplosively(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+10
j org.junit.internal.runners.statements.InvokeMethod.evaluate()V+12
j org.junit.runners.ParentRunner.runLeaf(Lorg/junit/runners/model/Statement;Lorg/junit/runner/Description;Lorg/junit/runner/notification/RunNotifier;)V+17
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/runner/notification/RunNotifier;)V+30
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6
j org.junit.runners.ParentRunner$3.run()V+12
j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1
j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+44
j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.ParentRunner$2.evaluate()V+8
j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20
j org.junit.runners.Suite.runChild(Lorg/junit/runner/Runner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.Suite.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6
j org.junit.runners.ParentRunner$3.run()V+12
j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1
j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+44
j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.ParentRunner$2.evaluate()V+8
j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20
j org.junit.runner.JUnitCore.run(Lorg/junit/runner/Runner;)Lorg/junit/runner/Result;+37
j org.junit.runner.JUnitCore.run(Lorg/junit/runner/Request;)Lorg/junit/runner/Result;+5
j org.junit.runner.JUnitCore.run(Lorg/junit/runner/Computer;[Ljava/lang/Class;)Lorg/junit/runner/Result;+6
j org.junit.runner.JUnitCore.run([Ljava/lang/Class;)Lorg/junit/runner/Result;+5
j org.rocksdb.test.RocksJunitRunner.main([Ljava/lang/String;)V+93
v ~StubRoutines::call_stub
--------------- P R O C E S S ---------------
... truncated for brevity!
在上述文件中,最重要的信息莫过于栈帧的信息,就拿下列的栈帧为例:
C [librocksdbjni-osx.jnilib+0x1c38a] rocksdb::InternalKeyComparator::InternalKeyComparator(rocksdb::Comparator const*)+0x4a
从这个栈帧中,我们可以看出崩溃发生在 InternalKeyComparator
函数中,但是我们怎么通过这个信息反推出崩溃发生在源文件的第几行呢?
很显然,我们得通过栈帧中的偏移地址信息(注意栈帧最前面的 0x1c38a
就是偏移地址)反推其对应的代码行号。
在 Mac 平台上,我们可以这样进行反推:
atos -o java/target/librocksdbjni-osx.jnilib 0x1c38a
#ava_org_rocksdb_Logger_setInfoLogLevel (in librocksdbjni-osx.jnilib) (loggerjnicallback.cc:152)
当然,在 Linux 我们也可以做到这件事:
addr2line -e java/target/librocksjni-linux64.so 0x1c38a
ASAN
ASAN (Google Address Sanitizer), 它是一个内存错误检查器,可以编译到我们的代码中,在运行时,它会报告内存问题。
要想使用 ASAN 你需要修改一下编译参数:
# ASAN doesn't work well with jemalloc. If we're compiling with ASAN, we should use regular malloc.
DISABLE_JEMALLOC=1
EXEC_LDFLAGS += -fsanitize=address
PLATFORM_CCFLAGS += -fsanitize=address
PLATFORM_CXXFLAGS += -fsanitize=address
值得一提的是,如果在 Mac 平台上出现了下述错误信息:
==20705==ERROR: Interceptors are not working. This may be because AddressSanitizer is loaded too late (e.g. via dlopen). Please launch the executable with:
DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
"interceptors not installed" && 0
那么你需要先执行下列命令:
export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
如果 ASAN 发现了问题,你会看到类似如下的错误信息:
Run: org.rocksdb.BackupableDBOptionsTest testing now -> destroyOldData
Run: org.rocksdb.BackupEngineTest testing now -> deleteBackup
=================================================================
==80632==ERROR: AddressSanitizer: unknown-crash on address 0x7fd93940d6e8 at pc 0x00011cebe075 bp 0x70000020ffe0 sp 0x70000020ffd8
WRITE of size 8 at 0x7fd93940d6e8 thread T0
#0 0x11cebe074 in rocksdb::PosixLogger::PosixLogger(__sFILE*, unsigned long long (*)(), rocksdb::Env*, rocksdb::InfoLogLevel) posix_logger.h:47
#1 0x11cebc847 in rocksdb::PosixLogger::PosixLogger(__sFILE*, unsigned long long (*)(), rocksdb::Env*, rocksdb::InfoLogLevel) posix_logger.h:53
#2 0x11ce9888c in rocksdb::(anonymous namespace)::PosixEnv::NewLogger(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<rocksdb::Logger>*) env_posix.cc:574
#3 0x11c09a3e3 in rocksdb::CreateLoggerFromOptions(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DBOptions const&, std::__1::shared_ptr<rocksdb::Logger>*) auto_roll_logger.cc:166
#4 0x11c3a8a55 in rocksdb::SanitizeOptions(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DBOptions const&) db_impl.cc:143
#5 0x11c3ac2f3 in rocksdb::DBImpl::DBImpl(rocksdb::DBOptions const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) db_impl.cc:307
#6 0x11c3b38b4 in rocksdb::DBImpl::DBImpl(rocksdb::DBOptions const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) db_impl.cc:350
#7 0x11c4497bc in rocksdb::DB::Open(rocksdb::DBOptions const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<rocksdb::ColumnFamilyDescriptor, std::__1::allocator<rocksdb::ColumnFamilyDescriptor> > const&, std::__1::vector<rocksdb::ColumnFamilyHandle*, std::__1::allocator<rocksdb::ColumnFamilyHandle*> >*, rocksdb::DB**) db_impl.cc:5665
#8 0x11c447b74 in rocksdb::DB::Open(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**) db_impl.cc:5633
#9 0x11bff8ca4 in rocksdb::Status std::__1::__invoke_void_return_wrapper<rocksdb::Status>::__call<rocksdb::Status (*&)(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**), rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**>(rocksdb::Status (*&&&)(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**), rocksdb::Options const&&&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&&&, rocksdb::DB**&&) __functional_base:437
#10 0x11bff89ff in std::__1::__function::__func<rocksdb::Status (*)(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**), std::__1::allocator<rocksdb::Status (*)(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)>, rocksdb::Status (rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)>::operator()(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**&&) functional:1437
#11 0x11bff269b in std::__1::function<rocksdb::Status (rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)>::operator()(rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**) const functional:1817
#12 0x11bfd6edb in rocksdb_open_helper(JNIEnv_*, long, _jstring*, std::__1::function<rocksdb::Status (rocksdb::Options const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, rocksdb::DB**)>) rocksjni.cc:37
#13 0x11bfd723e in Java_org_rocksdb_RocksDB_open__JLjava_lang_String_2 rocksjni.cc:55
#14 0x10be77757 (<unknown module>)
#15 0x10be6b174 (<unknown module>)
#16 0x10be6b232 (<unknown module>)
#17 0x10be654e6 (<unknown module>)
#18 0x10b6dc897 in JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*) (libjvm.dylib+0x2dc897)
#19 0x10b6dc667 in JavaCalls::call(JavaValue*, methodHandle, JavaCallArguments*, Thread*) (libjvm.dylib+0x2dc667)
#20 0x10b868427 in Reflection::invoke(instanceKlassHandle, methodHandle, Handle, bool, objArrayHandle, BasicType, objArrayHandle, bool, Thread*) (libjvm.dylib+0x468427)
#21 0x10b86888d in Reflection::invoke_method(oopDesc*, Handle, objArrayHandle, Thread*) (libjvm.dylib+0x46888d)
#22 0x10b729246 in JVM_InvokeMethod (libjvm.dylib+0x329246)
#23 0x10be77757 (<unknown module>)
#24 0x10be6b232 (<unknown module>)
#25 0x10be6b232 (<unknown module>)
#26 0x10be6b8e0 (<unknown module>)
#27 0x10be6b232 (<unknown module>)
#28 0x10be6b232 (<unknown module>)
#29 0x10be6b232 (<unknown module>)
#30 0x10be6b232 (<unknown module>)
#31 0x10be6b057 (<unknown module>)
#32 0x10be6b057 (<unknown module>)
#33 0x10be6b057 (<unknown module>)
#34 0x10be6b057 (<unknown module>)
#35 0x10be6b057 (<unknown module>)
#36 0x10be6b057 (<unknown module>)
#37 0x10be6b057 (<unknown module>)
#38 0x10be6b705 (<unknown module>)
#39 0x10be6b705 (<unknown module>)
#40 0x10be6b057 (<unknown module>)
#41 0x10be6b057 (<unknown module>)
#42 0x10be6b057 (<unknown module>)
#43 0x10be6b057 (<unknown module>)
#44 0x10be6b057 (<unknown module>)
#45 0x10be6b057 (<unknown module>)
#46 0x10be6b057 (<unknown module>)
#47 0x10be6b057 (<unknown module>)
#48 0x10be6b705 (<unknown module>)
#49 0x10be6b705 (<unknown module>)
#50 0x10be6b057 (<unknown module>)
#51 0x10be6b057 (<unknown module>)
#52 0x10be6b057 (<unknown module>)
#53 0x10be6b057 (<unknown module>)
#54 0x10be6b232 (<unknown module>)
#55 0x10be6b232 (<unknown module>)
#56 0x10be6b232 (<unknown module>)
#57 0x10be6b232 (<unknown module>)
#58 0x10be654e6 (<unknown module>)
#59 0x10b6dc897 in JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*) (libjvm.dylib+0x2dc897)
#60 0x10b6dc667 in JavaCalls::call(JavaValue*, methodHandle, JavaCallArguments*, Thread*) (libjvm.dylib+0x2dc667)
#61 0x10b71004d in jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) (libjvm.dylib+0x31004d)
#62 0x10b7092d4 in jni_CallStaticVoidMethodV (libjvm.dylib+0x3092d4)
#63 0x10b71c28d in checked_jni_CallStaticVoidMethod (libjvm.dylib+0x31c28d)
#64 0x109fdd0fd in JavaMain (java+0x1000030fd)
#65 0x7fff8df9c99c in _pthread_body (libsystem_pthread.dylib+0x399c)
#66 0x7fff8df9c919 in _pthread_start (libsystem_pthread.dylib+0x3919)
#67 0x7fff8df9a350 in thread_start (libsystem_pthread.dylib+0x1350)
AddressSanitizer can not describe address in more detail (wild memory access suspected).
SUMMARY: AddressSanitizer: unknown-crash posix_logger.h:47 in rocksdb::PosixLogger::PosixLogger(__sFILE*, unsigned long long (*)(), rocksdb::Env*, rocksdb::InfoLogLevel)
Shadow bytes around the buggy address:
0x1ffb27281a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1ffb27281ad0: 00 00 00 00 00 00 00 00 00 04 00 00 00[04]00 00
0x1ffb27281ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1ffb27281b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==80632==ABORTING
make[1]: *** [run_test] Abort trap: 6
make: *** [jtest_run] Error 2
ASAN 会输出栈信息,包括导致内存问题的代码所在的文件名和行号,这或多或少能帮助到我们一些。
你会发现在 ASAN 的输出中,会有一些 <unknown module>
的存在,这些都是执行在 JVM 内部的代码,因为 JVM 并不是编译的时候启用 ASAN,所以 ASAN 没办法检查这部分代码。
C++ 调试器
如果上述两种方式仍然无法解决你的问题,那么你可能就需要通过 C++ 调试器来追踪 C++ JNI 代码的执行过程了。
Mac
在 Mac 平台,我们可以通过 lldb
来调试我们的 java 程序:
lldb -- /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/bin/java -ea -Xcheck:jni -Djava.library.path=target -cp "target/classes:target/test-classes:test-libs/junit-4.12.jar:test-libs/hamcrest-core-1.3.jar:test-libs/mockito-all-1.10.19.jar:test-libs/cglib-2.2.2.jar:test-libs/assertj-core-1.7.1.jar:target/*" org.rocksdb.test.RocksJunitRunner org.rocksdb.ComparatorTest
启动成功后,可以通过下述命令开启代码的执行:
(lldb) run
如果你想让程序在发生错误的时候(发出 SIGSEGV 和 SIGBUS 信号)不要直接退出,那么你可以执行下述命令:
(lldb) pro hand -p true -s false SIGSEGV
(lldb) pro hand -p true -s false SIGBUS
Linux
在 Linux 上,我们需要使用的 C++ 调试工具是 gdb,使用方法如下:
gdb --args java -ea -Xcheck:jni -Djava.library.path=target -cp "target/classes:target/test-classes:test-libs/junit-4.12.jar:test-libs/hamcrest-core-1.3.jar:test-libs/mockito-all-1.10.19.jar:test-libs/cglib-2.2.2.jar:test-libs/assertj-core-1.7.1.jar:target/*" org.rocksdb.test.RocksJunitRunner org.rocksdb.ComparatorTest
如果你想让程序在发生错误的时候(发出 SIGSEGV 和 SIGBUS 信号)不要直接退出,那么你可以执行下述命令:
gdb> handle SIGSEGV pass noprint nostop
配置好执行方式后,可以通过下述命令开始代码执行:
gdb> start
文章说明
更多有价值的文章均收录于贝贝猫的文章目录
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
创作声明: 本文基于下列所有参考内容进行创作,其中可能涉及复制、修改或者转换,图片均来自网络,如有侵权请联系我,我会第一时间进行删除。
参考内容
[1] JNI-Debugging