Моя библиотека JNI безупречно работает в Windows, однако в Linux я всегда получаю странную ошибку сегментации.
siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000000
Стек стека из файла сбоя выглядит следующим образом:
C [libfmodjavaL.so+0xfb8c] JNIEnv_::GetStaticObjectField(_jclass*, _jfieldID*)+0x18
C [libfmodjavaL.so+0xf72b] Logger::sendToSystemOut(bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)+0x75
C [libfmodjavaL.so+0xf7c2] Logger::log(char const*)+0x4c
C [libfmodjavaL.so+0xd70d] fmodDebugCallback(unsigned int, char const*, int, char const*, char const*)+0x127
Итак, похоже, что он разбился при вызове поля GetStaticObject в классе Logger. Это тот метод:
void Logger::sendToSystemOut(bool error, std::string message) {
JNIEnv* jni = FMODWrapper::utils->getJNI();
jobject printStream;
if (error) {
printStream = jni->GetStaticObjectField(this->systemClass, this->errFieldID);
} else {
printStream = jni->GetStaticObjectField(this->systemClass, this->outFieldID);
}
jobject messageString = jni->NewStringUTF(message.c_str());
jni->CallObjectMethod(printStream, this->printlnMethodID, messageString);
}
Итак, я предполагаю, что что-то не так с хранением идентификаторов классов и полей этих полей. Но странно то, что я получаю вывод журнала, когда моя библиотека запускается, даже из FMOD, который вызывает fmodDebugCallback.
Logger::Logger(const char* name) {
this->name = name;
JNIEnv* jni = FMODWrapper::utils->getJNI();
this->systemClass = FMODWrapper::utils->findClass("java/lang/System");
this->outFieldID = jni->GetStaticFieldID(this->systemClass, "out", "Ljava/io/PrintStream;");
this->errFieldID = jni->GetStaticFieldID(this->systemClass, "err", "Ljava/io/PrintStream;");
jclass printStreamClass = FMODWrapper::utils->findClass("java/io/PrintStream");
this->printlnMethodID = jni->GetMethodID(printStreamClass, "println", "(Ljava/lang/String;)V");
}
Итак, логгирование работает безотказно в Windows, но через какое-то время вылетает в Linux. Скомпилировано с помощью g++ на 64-битной версии Fedora 29.
Обновление: мой метод получения JNIEnv*
JNIEnv* Utils::getJNI() {
JNIEnv* jni;
int getEnvResult = FMODWrapper::jvm->GetEnv((void**) &jni, JNI_VERSION_1_6);
if (getEnvResult == JNI_EDETACHED) {
FMODWrapper::jvm->AttachCurrentThread(ANDROID_VOIDPP_CAST &jni, nullptr);
}
return jni;
}
Обновление 2: сам код работает до определенного момента, так как я получаю сообщения журнала. Может что-то с нитками? https://hastebin.com/kuzefuwawu.txt
JNIEnv*
,jobject
илиjclass
для вызовов JNI. В этом коде удручающе полное отсутствие проверки ошибок. Каждый вызов JNI должен проверяться на наличие ошибок, и вы не должны продолжать в случае ошибки. - person user207421   schedule 20.04.2019env->GetJavaVM(&jvm)
при первом вызове JNI сenv->NewGlobalRef(your jobject here)
. При дополнительных вызовах вне области действия JNI вы можете использовать указатель jvm для получения JNIEnv, с помощью которого вы можете получить jclass и/или jmethodID для выполнения ваших вызовов. Этот пост помог мне с моей связанной проблемой adamish.com/blog/archives/327 - person Paul Gregoire   schedule 19.12.2019