Проект
Я пишу интерфейс командной строки Java в библиотеку C для внутренних сетевых и сетевых средств тестирования с использованием Java Native Interface. Код C (который я не писал) является сложным и низким уровнем, часто манипулирует памятью на уровне бит и использует исключительно сырые сокеты. Приложение многопоточное со стороны C (pthreads работает в фоновом режиме), а также на стороне Java (ScheduledThreadPoolExecutors работает с потоками, которые вызывают собственный код). Тем не менее, библиотека C должна быть в основном стабильной. Код интерфейса Java и JNI, как выясняется, вызывает проблемы.
Проблема (ы)
При сбое приложения с ошибкой сегментации при входе в нативную функцию C. Это происходит только тогда, когда программа находится в определенном состоянии (то есть успешно работает определенная нативная функция, вызывает следующий вызов другой конкретной нативной функции для segfault). Кроме того, приложение выходит из строя с аналогичным видом segfault, когда выдается команда quit
, но опять же, только после успешного запуска той же самой конкретной встроенной функции.
Я - неопытный разработчик C и опытный разработчик Java. Я привык к сбоям, дающим мне определенную причину и конкретный номер строки. Все, от чего мне нужно работать, в этом случае - вывод hs_err_pid*.log
и дамп ядра. Я включил все, что мог, в конце этого вопроса.
Моя работа до сих пор
- Естественно, я хотел найти конкретную строку кода, где произошел сбой. Я поместил a
System.out.println()
прямо перед нативным вызовами на стороне Java иprintf()
в качестве первой строки встроенной функции, в которой программа аварийно отключилась, используяfflush(stdout)
непосредственно после. Вызвал вызовSystem.out
, а вызовprintf
- нет. Это говорит мне, что segfault произошел при входе в функцию - то, что я никогда раньше не видел. - Я triple проверил параметры функции, чтобы они не действовали. Однако, я передаю только один параметр (типа
jint
). Остальные два (JNIEnv *env, jobject j_object
) являются конструкциями JNI и вне моего контроля. - Я прокомментировал каждую строчку в функции, оставив только
return 0;
в конце. Сегфафт все же произошел. Это заставляет меня думать, что проблема не в этой функции. - Я запускал команду в разных порядках (эффективно выполнял собственные функции разных заказов). Сегменты происходят только тогда, когда перед вызовом функции сбоя запускается одна конкретная нативная функция. Эта конкретная функция, как представляется, ведет себя правильно, когда она запускается.
- Я напечатал значение указателя
env
и значение&j_object
ближе к концу этой другой функции, чтобы убедиться, что я их каким-то образом не повредил. Я не знаю, испортил ли я их, но оба они имеют ненулевые значения при выходе из функции. - Изменить 1: Как правило, одна и та же функция запускается во многих потоках (обычно не параллельно, но должна быть потокобезопасной). Я запускал функцию из основного потока без каких-либо других потоков, чтобы гарантировать, что многопоточность на стороне Java не вызовет проблемы. Это было не так, и у меня был тот же самый segfault.
Все это меня озадачивает. Почему это все еще segfault, если я прокомментирую всю функцию, за исключением оператора return? Если проблема в этой другой функции, почему она не работает? Если это проблема, когда первая функция помешает памяти, а вторая функция незаконно обращается к поврежденной памяти, почему бы не сделать ошибку в строке с незаконным доступом, а не при входе в функцию?
Если вы видите интернет-статью, где кто-то объясняет проблему, похожую на мою, прокомментируйте ее. Существует так много статей segfault, и ни одна из них не содержит этой конкретной проблемы. То же самое для SO вопросов. Проблема также может заключаться в том, что я недостаточно опытен, чтобы применить абстрактное решение этой проблемы.
Мой вопрос
Что может вызвать родную функцию Java (в C) для segfault при входе вроде этого? Какие конкретные вещи я могу найти, это поможет мне скворовать эту ошибку? Как я могу написать код в будущем, который поможет мне избежать этой проблемы?
Полезная информация
Для записи я не могу опубликовать код. Если вы считаете, что описание кода было бы полезно, прокомментируйте, и я его отредактирую.
Сообщение об ошибке
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00002aaaaaf6d9c3, pid=2185, tid=1086892352
#
# JRE version: 6.0_21-b06
# Java VM: Java HotSpot(TM) 64-Bit Server VM (17.0-b16 mixed mode linux-amd64 )
# Problematic frame:
# j path.to.my.Object.native_function_name(I)I+0
#
# An error report file with more information is saved as:
# /path/to/hs_err_pid2185.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
Важные биты файла hs_err_pid*.log
--------------- T H R E A D ---------------
Current thread (0x000000004fd13800): JavaThread "pool-1-thread-1" [_thread_in_native, id=2198, stack(0x0000000040b8a000,0x0000000040c8b000)]
siginfo:si_signo=SIGSEGV: si_errno=0, si_code=128 (), si_addr=0x0000000000000000
Registers:
RAX=0x34372e302e3095e1, RBX=0x00002aaaae39dcd0, RCX=0x0000000000000000, RDX=0x0000000000000000
RSP=0x0000000040c89870, RBP=0x0000000040c898c0, RSI=0x0000000040c898e8, RDI=0x000000004fd139c8
R8 =0x000000004fb631f0, R9 =0x000000004faf5d30, R10=0x00002aaaaaf6d999, R11=0x00002b1243b39580
R12=0x00002aaaae3706d0, R13=0x00002aaaae39dcd0, R14=0x0000000040c898e8, R15=0x000000004fd13800
RIP=0x00002aaaaaf6d9c3, EFL=0x0000000000010202, CSGSFS=0x0000000000000033, ERR=0x0000000000000000
TRAPNO=0x000000000000000d
Stack: [0x0000000040b8a000,0x0000000040c8b000], sp=0x0000000040c89870, free space=3fe0000000000000018k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
j path.to.my.Object.native_function_name(I)I+0
j path.to.my.Object$CustomThread.fire()V+18
j path.to.my.CustomThreadSuperClass.run()V+1
j java.util.concurrent.Executors$RunnableAdapter.call()Ljava/lang/Object;+4
j java.util.concurrent.FutureTask$Sync.innerRun()V+30
j java.util.concurrent.FutureTask.run()V+4
j java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;)V+1
j java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run()V+15
j java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Ljava/lang/Runnable;)V+59
j java.util.concurrent.ThreadPoolExecutor$Worker.run()V+28
j java.lang.Thread.run()V+11
v ~StubRoutines::call_stub
V [libjvm.so+0x3e756d]
V [libjvm.so+0x5f6f59]
V [libjvm.so+0x3e6e39]
V [libjvm.so+0x3e6eeb]
V [libjvm.so+0x476387]
V [libjvm.so+0x6ee452]
V [libjvm.so+0x5f80df]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j path.to.my.Object.native_function_name(I)I+0
j path.to.my.Object$CustomThread.fire()V+18
j path.to.my.CustomThreadSuperClass.run()V+1
j java.util.concurrent.Executors$RunnableAdapter.call()Ljava/lang/Object;+4
j java.util.concurrent.FutureTask$Sync.innerRun()V+30
j java.util.concurrent.FutureTask.run()V+4
j java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;)V+1
j java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run()V+15
j java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Ljava/lang/Runnable;)V+59
j java.util.concurrent.ThreadPoolExecutor$Worker.run()V+28
j java.lang.Thread.run()V+11
v ~StubRoutines::call_stub
--------------- P R O C E S S ---------------
Java Threads: ( => current thread )
0x000000004fabc800 JavaThread "pool-1-thread-6" [_thread_new, id=2203, stack(0x0000000000000000,0x0000000000000000)]
0x000000004fbcb000 JavaThread "pool-1-thread-5" [_thread_blocked, id=2202, stack(0x0000000042c13000,0x0000000042d14000)]
0x000000004fbc9800 JavaThread "pool-1-thread-4" [_thread_blocked, id=2201, stack(0x0000000042b12000,0x0000000042c13000)]
0x000000004fbc7800 JavaThread "pool-1-thread-3" [_thread_blocked, id=2200, stack(0x0000000042a11000,0x0000000042b12000)]
0x000000004fc54800 JavaThread "pool-1-thread-2" [_thread_blocked, id=2199, stack(0x0000000042910000,0x0000000042a11000)]
=>0x000000004fd13800 JavaThread "pool-1-thread-1" [_thread_in_native, id=2198, stack(0x0000000040b8a000,0x0000000040c8b000)]
0x000000004fb04800 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=2194, stack(0x0000000041d0d000,0x0000000041e0e000)]
0x000000004fb02000 JavaThread "CompilerThread1" daemon [_thread_blocked, id=2193, stack(0x0000000041c0c000,0x0000000041d0d000)]
0x000000004fafc800 JavaThread "CompilerThread0" daemon [_thread_blocked, id=2192, stack(0x0000000040572000,0x0000000040673000)]
0x000000004fafa800 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=2191, stack(0x0000000040471000,0x0000000040572000)]
0x000000004fad6000 JavaThread "Finalizer" daemon [_thread_blocked, id=2190, stack(0x0000000041119000,0x000000004121a000)]
0x000000004fad4000 JavaThread "Reference Handler" daemon [_thread_blocked, id=2189, stack(0x0000000041018000,0x0000000041119000)]
0x000000004fa51000 JavaThread "main" [_thread_in_vm, id=2186, stack(0x00000000418cc000,0x00000000419cd000)]
Other Threads:
0x000000004facf800 VMThread [stack: 0x0000000040f17000,0x0000000041018000] [id=2188]
0x000000004fb0f000 WatcherThread [stack: 0x0000000041e0e000,0x0000000041f0f000] [id=2195]
VM state:not at safepoint (normal execution)
VM Mutex/Monitor currently owned by a thread: None
Heap
PSYoungGen total 305856K, used 31465K [0x00002aaadded0000, 0x00002aaaf3420000, 0x00002aaaf3420000)
eden space 262208K, 12% used [0x00002aaadded0000,0x00002aaadfd8a6a8,0x00002aaaedee0000)
from space 43648K, 0% used [0x00002aaaf0980000,0x00002aaaf0980000,0x00002aaaf3420000)
to space 43648K, 0% used [0x00002aaaedee0000,0x00002aaaedee0000,0x00002aaaf0980000)
PSOldGen total 699072K, used 0K [0x00002aaab3420000, 0x00002aaadded0000, 0x00002aaadded0000)
object space 699072K, 0% used [0x00002aaab3420000,0x00002aaab3420000,0x00002aaadded0000)
PSPermGen total 21248K, used 3741K [0x00002aaaae020000, 0x00002aaaaf4e0000, 0x00002aaab3420000)
object space 21248K, 17% used [0x00002aaaae020000,0x00002aaaae3c77c0,0x00002aaaaf4e0000)
VM Arguments:
jvm_args: -Xms1024m -Xmx1024m -XX:+UseParallelGC
--------------- S Y S T E M ---------------
OS:Red Hat Enterprise Linux Client release 5.5 (Tikanga)
uname:Linux 2.6.18-194.8.1.el5 #1 SMP Wed Jun 23 10:52:51 EDT 2010 x86_64
libc:glibc 2.5 NPTL 2.5
rlimit: STACK 10240k, CORE 102400k, NPROC 10000, NOFILE 1024, AS infinity
load average:0.21 0.08 0.05
CPU:total 1 (1 cores per cpu, 1 threads per core) family 6 model 26 stepping 4, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt
Memory: 4k page, physical 3913532k(1537020k free), swap 1494004k(1494004k free)
vm_info: Java HotSpot(TM) 64-Bit Server VM (17.0-b16) for linux-amd64 JRE (1.6.0_21-b06), built on Jun 22 2010 01:10:00 by "java_re" with gcc 3.2.2 (SuSE Linux)
time: Tue Oct 15 15:08:13 2013
elapsed time: 13 seconds
Выход Valgrind
Я действительно не знаю, как правильно использовать Valgrind. Это то, что появилось при запуске valgrind app arg1
==2184==
==2184== HEAP SUMMARY:
==2184== in use at exit: 16,914 bytes in 444 blocks
==2184== total heap usage: 673 allocs, 229 frees, 32,931 bytes allocated
==2184==
==2184== LEAK SUMMARY:
==2184== definitely lost: 0 bytes in 0 blocks
==2184== indirectly lost: 0 bytes in 0 blocks
==2184== possibly lost: 0 bytes in 0 blocks
==2184== still reachable: 16,914 bytes in 444 blocks
==2184== suppressed: 0 bytes in 0 blocks
==2184== Rerun with --leak-check=full to see details of leaked memory
==2184==
==2184== For counts of detected and suppressed errors, rerun with: -v
==2184== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 7 from 7)
Изменить 2:
Выход GDB и обратная трассировка
Я провела его с помощью GDB. Я убедился, что библиотека C была скомпилирована с флагом -g
.
$ gdb `which java`
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-23.el5)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/bin/java...(no debugging symbols found)...done.
(gdb) run -jar /opt/scts/scts.jar test.config
Starting program: /usr/bin/java -jar /opt/scts/scts.jar test.config
[Thread debugging using libthread_db enabled]
Executing new program: /usr/lib/jvm/java-1.6.0-sun-1.6.0.21.x86_64/jre/bin/java
[Thread debugging using libthread_db enabled]
[New Thread 0x4022c940 (LWP 3241)]
[New Thread 0x4032d940 (LWP 3242)]
[New Thread 0x4042e940 (LWP 3243)]
[New Thread 0x4052f940 (LWP 3244)]
[New Thread 0x40630940 (LWP 3245)]
[New Thread 0x40731940 (LWP 3246)]
[New Thread 0x40832940 (LWP 3247)]
[New Thread 0x40933940 (LWP 3248)]
[New Thread 0x40a34940 (LWP 3249)]
... моя программа выполняет некоторую работу и запускает фоновый поток...
[New Thread 0x41435940 (LWP 3250)]
... Я набираю команду, которая, похоже, вызывает segfault для следующей команды; новые потоки ожидаются...
[New Thread 0x41536940 (LWP 3252)]
[New Thread 0x41637940 (LWP 3253)]
[New Thread 0x41738940 (LWP 3254)]
[New Thread 0x41839940 (LWP 3255)]
[New Thread 0x4193a940 (LWP 3256)]
... Я набираю команду, которая фактически запускает segfault. Ожидается новый поток, поскольку функция запускается в своем потоке. Если это не segfault, оно создало бы такое же количество потоков, как предыдущая команда...
[New Thread 0x41a3b940 (LWP 3257)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x41839940 (LWP 3255)]
0x00002aaaabcaec45 in ?? ()
... Я яростно прочитал справку gdb, а затем запустил backtrace...
(gdb) bt
#0 0x00002aaaabcaec45 in ?? ()
#1 0x00002aaaf3ad7800 in ?? ()
#2 0x00002aaaf3ad81e8 in ?? ()
#3 0x0000000041838600 in ?? ()
#4 0x00002aaaeacddcd0 in ?? ()
#5 0x0000000041838668 in ?? ()
#6 0x00002aaaeace23f0 in ?? ()
#7 0x0000000000000000 in ?? ()
... Не должно быть символов, если я скомпилирован с -g
? Я сделал, согласно строкам с выхода make
:
gcc -g -Wall -fPIC -c -I ...
gcc -g -shared -W1,soname, ...