I'm using the openSmile 2.0-rc1 library in Android and I'm facing a very annoying problem. When I run runAnalysis
the first time it passes without any problems. I get valid results. However, when I run the same function twice with the same parameters my app crashes and I get this error:
07-18 11:47:22.609 5128-5128/com.test A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 5128 (com.test)
07-18 11:47:22.711 196-196/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
07-18 11:47:22.711 196-196/? A/DEBUG: Build fingerprint: 'google/hammerhead/hammerhead:6.0.1/user/release-keys'
07-18 11:47:22.711 196-196/? A/DEBUG: Revision: '11'
07-18 11:47:22.711 196-196/? A/DEBUG: ABI: 'arm'
07-18 11:47:22.711 196-196/? A/DEBUG: pid: 5128, tid: 5128, name: com.test >>> com.test <<<
07-18 11:47:22.711 196-196/? A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
07-18 11:47:22.723 196-196/? A/DEBUG: r0 00000000 r1 00001408 r2 00000006 r3 b6f7eb7c
07-18 11:47:22.723 196-196/? A/DEBUG: r4 b6f7eb84 r5 b6f7eb34 r6 0000000b r7 0000010c
07-18 11:47:22.723 196-196/? A/DEBUG: r8 9eb91e40 r9 9c97a9c0 sl aa125d80 fp 00000001
07-18 11:47:22.723 196-196/? A/DEBUG: ip 00000006 sp bedf5848 lr b6cedb61 pc b6ceff50 cpsr 400f0010
07-18 11:47:22.737 196-196/? A/DEBUG: backtrace:
07-18 11:47:22.737 196-196/? A/DEBUG: #00 pc 00041f50 /system/lib/libc.so (tgkill+12)
07-18 11:47:22.737 196-196/? A/DEBUG: #01 pc 0003fb5d /system/lib/libc.so (pthread_kill+32)
07-18 11:47:22.737 196-196/? A/DEBUG: #02 pc 0001c30f /system/lib/libc.so (raise+10)
07-18 11:47:22.737 196-196/? A/DEBUG: #03 pc 000194c1 /system/lib/libc.so (__libc_android_abort+34)
07-18 11:47:22.737 196-196/? A/DEBUG: #04 pc 000174ac /system/lib/libc.so (abort+4)
07-18 11:47:22.737 196-196/? A/DEBUG: #05 pc 000c12e7 /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN9__gnu_cxx27__verbose_terminate_handlerEv+226)
07-18 11:47:22.737 196-196/? A/DEBUG: #06 pc 00091e05 /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN10__cxxabiv111__terminateEPFvvE+4)
07-18 11:47:22.737 196-196/? A/DEBUG: #07 pc 00091e79 /data/app/com.test-1/lib/arm/libopenSmile.so (_ZSt9terminatev+8)
07-18 11:47:22.737 196-196/? A/DEBUG: #08 pc 00091f9d /data/app/com.test-1/lib/arm/libopenSmile.so (__cxa_throw+120)
07-18 11:47:22.737 196-196/? A/DEBUG: #09 pc 00033a73 /data/app/com.test-1/lib/arm/libopenSmile.so (_ZNK10ConfigType10findFieldHEPKcPiPPKS_S2_PPc+346)
07-18 11:47:22.737 196-196/? A/DEBUG: #10 pc 00034d5f /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN17cFileConfigReader11getInstanceEPKcPK10ConfigTypeP14cConfigManager+854)
07-18 11:47:22.737 196-196/? A/DEBUG: #11 pc 00036a73 /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN14cConfigManager10readConfigEv+102)
07-18 11:47:22.737 196-196/? A/DEBUG: #12 pc 00090df3 /data/app/com.test-1/lib/arm/libopenSmile.so (runAnalysis+194)
07-18 11:47:22.738 196-196/? A/DEBUG: #13 pc 00fe573f /data/app/com.test-1/oat/arm/base.odex (offset 0xb75000) (void com.test.probing.probe.voiceAnalysis.OpenSmile.runAnalysis(java.lang.String, java.lang.String, java.lang.String, java.lang.String)+170)
07-18 11:47:22.738 196-196/? A/DEBUG: #14 pc 00fe532d /data/app/com.test-1/oat/arm/base.odex (offset 0xb75000) (java.lang.String com.test.probing.probe.voiceAnalysis.OpenSmile.runAnalysis(android.content.Context, java.lang.String)+872)
07-18 11:47:22.738 196-196/? A/DEBUG: #15 pc 0187d1c9 /data/app/com.test-1/oat/arm/base.odex (offset 0xb75000) (int com.test.probing.probe.voiceAnalysis.VoiceAnalyserService.onStartCommand(android.content.Intent, int, int)+292)
07-18 11:47:22.738 196-196/? A/DEBUG: #16 pc 72a4f10f /data/dalvik-cache/arm/system@[email protected] (offset 0x1ed6000)
--------- beginning of system
07-18 11:47:23.199 778-6481/? W/ActivityManager: Force finishing activity com.test/.app.MainActivity
07-18 11:47:23.203 196-196/? A/DEBUG: Tombstone written to: /data/tombstones/tombstone_00
07-18 11:47:23.203 196-196/? E/DEBUG: AM write failed: Broken pipe
I'm using the open smile as JNI and I have a service running the analysis. I have tried on different Android version, on different architectures, but I face the same problem.
I don't understand how can this happen when the first time it runs flawlessly.
I'd be grateful for any help regarding this. Thank you in advance.
The problem itself was not with the openSmile
library.
Since it successfully runs on the first try and fails on the second run.
After activity restarts calls to native methods may fail.
This can happen because shared library does not get reloaded. There is no symmetric method to System.loadLibrary(String libName)
to manually unload or reload library. The shared library on Android will be unloaded when there are no processes that use it. Even when activity finishes its lifecycle and its onDestroy()
method is called the host process of activity may still run. From the Android docs "the process hosting the activity may killed by the system at any time" after activity is finished. I think it would be killed when OS will need more memory for other tasks.
So, if the old process still sticks around so does the shared library. After activity starts again, its onCreate()
called etc, it attaches to the same running process and deals with the same shared library instance. The shared library's data section where all static and global variables are stored is still in the state it was carrying old values.
Now, to highlight the possible problem, suppose we have a Singleton class on a native side. We call in to a native function to create it from activity's onCreate
method. The native method may look like this.
CSomeSingleton* CSomeSingleton::GetInstance()
{
if (m_Instance == NULL)
m_Instance = new CSomeSingleton();
return m_Instance;
}
In activity's onDestroy we call in to another native method that destroys it. Suppose it looks like this.
void CSomeSingleton::FreeInstance()
{
delete m_Instance;
}
After acivity restarts and attaches to same process the problem arises. m_Instance will not be NULL, it will point to invalid address in memory.
There are 3 variants to unravel this issue.
Do not deinitialize your native-side objects at all (in activity's onDestroy()
). When new activity instance will start it would attach to running process with valid pointers and global variables. If the process would be killed by the time, it would be started again, reloading the shared library, reinitializing the data section.
Find all places where the global/static variables where left in inconsistent state. For the above example that would be
void CSomeSingleton::FreeInstance() { delete m_Instance; m_Instance = NULL; }
Call System.exit(0)
in onDestory()
to stop the process and thus unload the shared library. Considered as a bad practice, because you may have yet another component that is running in that process, and this component may be not in the appropriate state to stop its execution (because it may have some unsaved data).
Credits to: http://choruscode.blogspot.dk/2013/12/on-android-ndk-and-activity-lifecycle.html