致命信号11(SIGSEGV)

5
我遇到了一个奇怪的错误,但我找不到它的来源。在logcat中唯一出现的是:
01-10 17:07:10.665: A/libc(20449): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1)
当我运行我的应用程序时,并不会立即出现此错误,而是在随机时间之后(大约在1分钟到5分钟之间)。我无法重现这个错误,因为它似乎是随机的,并且由于没有其他信息,所以很难进行调试。我尝试向我的库添加一些日志信息,但仍然无法确定崩溃发生的位置。
我运行了相同的应用程序多次,每次得到的SIGSEGV地址都不同(有时与之前的地址相同):
01-10 17:29:04.650: A/libc(21588): Fatal signal 11 (SIGSEGV) at 0x6c707063 (code=1)
01-10 17:25:55.165: A/libc(21473): Fatal signal 11 (SIGSEGV) at 0x0069004c (code=1)
01-10 17:11:58.780: A/libc(20742): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1)
01-10 17:00:02.010: A/libc(20160): Fatal signal 11 (SIGSEGV) at 0x00000018 (code=1)
我的应用程序使用一个C++库,该库有一个NetworkThread从服务器接收更新。在Java端,有一个WorkerThread检查是否有来自NetworkThread的新更新,如果有新的更新,则通知所有侦听器。我还有一个LocationSpotter(在Java端),当位置更新时会进行一些JNI调用。
有没有办法调试这个问题或者使用我从SIGSEGV中得到的地址来调试应用程序?此外,我正在使用共享的JavaVM对象来获取一些方法以检索当前JNIEnv(并调用AttachCurrentThread)。这是线程安全的吗?
我注意到,在我得到SIGSEGV错误之后,我仍然会从NetworkThread接收更新(在应用程序实际崩溃之前)。这意味着NetworkThread可能正在工作。
我还注意到了一行可能是问题来源的代码(在notifyAll方法中),因为在SIGSEGV之前打印的最后一条消息是“notifyAll1”。
for (unsigned i = 0; i < listeners.size(); i++) {
    try {
        __android_log_print(ANDROID_LOG_INFO, "FROM C++", "notifyAll1");
        if (listeners.at(i) == NULL)
            __android_log_print(ANDROID_LOG_INFO, "FROM C++", "LISTENER NULL");


        listeners.at(i)->update(u); // <- This line is a potential suspect
        __android_log_print(ANDROID_LOG_INFO, "FROM C++", "notifyAll2");

日志记录工具:

01-10 17:07:10.665: I/FROM C++(20449): notifyAll1
01-10 17:07:10.665: A/libc(20449): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1)

我尝试在每个监听器的update方法的第一行打印日志,但是没有一个被打印出来(我认为这非常奇怪)。

如有帮助将不胜感激。


如果在崩溃后创建了相关的墓碑文件,您能否将其添加到/data/tombstones目录中? - auselen
我找不到墓碑文件。 我尝试进入DDMS透视图-> 文件资源管理器-> 数据-> 数据->(您的包)->文件->(您的文件),但那里什么都没有... 我在手机上也尝试了,但没有更多结果。 - Fr4nz
顺便问一下,你知道为什么我的手机上的数据目录下没有任何内容吗?我需要root我的手机才能读取墓碑文件吗?(https://dev59.com/Imsy5IYBdhLWcg3wxAuO#8921133) - Fr4nz
基本上,您正在取消引用无效指针,可能是通过将其传递给系统库代码在您的代码中,或者更少可能触发平台错误。您想要找到的主要内容是崩溃的本地堆栈转储,有没有可能已经在您的logcat中了?/data目录不可在受保护的设备上访问,但您可以尝试从adb shell切换到其下的特定目录。或者您可以在模拟器上复制问题,其中adb shell已经是root。 - Chris Stratton
你是如何在本机代码中启用异常的?你使用哪个STL? - Alex Cohn
3个回答

5

大多数时间致命信号错误是在您尝试访问未在此时创建的任何对象时发生的。因此,请仔细检查它们。


1
我认为你应该像这样重写 notifyAll 循环:
for (unsigned i = 0; i < listeners.size(); i++) {
  try {
    __android_log_print(ANDROID_LOG_INFO, "FROM C++", "notifyAll1 i=%u", i);
    auto listener = *listeners.at(i);

    if (&listener == NULL) {
      __android_log_print(ANDROID_LOG_INFO, "FROM C++", "LISTENER NULL");
    }
    else {
      __android_log_print(ANDROID_LOG_INFO, "FROM C++", "notifyAll2 : listener[%u] at %p", i, &listener);
      listener.update(u);
    }
  • 在你的原始代码中,检查只是打印到日志中,但是 update() 仍然会失败(@Robin 发现了这个问题)
  • 在你的原始代码中,listener 可能会在检查和 update() 之间无效为 NULL
  • 即使进行了上述更改,如果由 listeners.at(i) 指向的监听器对象变为无效,则 update() 仍可能崩溃。

但是崩溃可能发生在异常处理过程中。您没有披露 catch(...) 代码,所以我无法谈论此问题。


0

这似乎是显而易见的

if (listeners.at(i) == NULL)
            __android_log_print(ANDROID_LOG_INFO, "FROM C++", "LISTENER NULL");


        listeners.at(i)->update(u); // <- This line is a potential suspect
        __android_log_print(ANDROID_LOG_INFO, "FROM C++", "notifyAll2");

应该是

if (listeners.at(i) == NULL) {
    __android_log_print(ANDROID_LOG_INFO, "FROM C++", "LISTENER NULL");
} else {
    listeners.at(i)->update(u); // <- This line is a potential suspect
    __android_log_print(ANDROID_LOG_INFO, "FROM C++", "notifyAll2");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接