静态音频分析函数中间歇性的 EXC_BAD_ACCESS

发布于 2025-01-01 01:12:38 字数 5077 浏览 4 评论 0原文

我正在编写一个使用实时音频分析的 iOS 应用程序。它有间歇性崩溃(大约 5 分钟后,仅在模拟器上)。虽然这意味着它与运输应用程序无关,但它确实对开发来说是一个痛苦,此外,如果我跟踪它,我晚上会睡得更好崩溃总是发生在同一个地方,在我的音频分析代码的静态函数中:崩溃发生在这里:

struct Tone {
// (other stuff)

// THIS is the problem function:
static bool dbCompare(Tone const& l, Tone const& r) { return l.db < r.db; }
  }
};

也在这里:

Tone const* findTone(double minfreq = 70.0, double maxfreq = 700.0) const {
    if (m_tones.empty()) { m_oldfreq = 0.0; return NULL; }
    double db = std::max_element(m_tones.begin(), m_tones.end(), Tone::dbCompare)->db;
    Tone const* best = NULL;
    double bestscore = 0;
    for (tones_t::const_iterator it = m_tones.begin(); it != m_tones.end(); ++it) {

    // ON THIS LINE. Is my iterator somehow becoming invalid?:
        if (it->db < db - 20.0 || it->freq < minfreq || it->age < Tone::MINAGE) continue;
        if (it->freq > maxfreq) break;
        double score = it->db - std::max(180.0, std::abs(it->freq - 300.0)) / 10.0;
        if (m_oldfreq != 0.0 && std::fabs(it->freq/m_oldfreq - 1.0) < 0.05) score += 10.0;
        if (best && bestscore > score) break;
        best = &*it;
        bestscore = score;
    }
    m_oldfreq = (best ? best->freq : 0.0);
    return best;
}

(好吧,这个)下面的修复是错误的,我要离开。为了完整性起见,至少现在如此)。 为了避免可能传入的任何空指针,我添加了一些检查和错误处理尝试:

struct Tone {
    static bool dbCompare(Tone const& l, Tone const& r) {
    try {
      if (&l == NULL || &r == NULL) throw 1;
      return l.db < r.db;
      throw 1;
    }
    catch(int e) {
      NSLog(@"AUDIO ERROR: Pointer invalid. %d", e);
    }
    return 0;
  }
};

没有骰子,它仍然在同一行失败。我是否误解了 try-catch- throw ?错误是否只是发生在管道的更后面?

这段代码来自开源Rock-Band风格的游戏Performous,而他们的游戏没有这个问题。 (它也不适用于 iPhone。)所以这给了我希望,我可以消除模拟器崩溃...

编辑 2 Per @Justin 我正在使用 GuardMalloc 运行。模拟器 5.0 立即崩溃 - 似乎有 一些关于此的问题,但现在我正在运行 4.3 模拟器,它会以正常方式崩溃,但除此之外我不知道如何使用:

#0  0x94f38c97 in malloc_error_break ()
#1  0x94efa4ce in szone_error ()
#2  0x94efc35f in allocate_pages ()
#3  0x94f013cc in allocate_pages_securely ()
#4  0x94f019b8 in szone_malloc_should_clear ()
#5  0x94f0266b in szone_malloc ()
#6  0x00331cc3 in GMmalloc_zone_malloc ()
#7  0x94f38edb in malloc_set_zone_name ()
#8  0x94f394a8 in _malloc_initialize ()
#9  0x94f3986b in malloc ()
#10 0x00002ef0 in start ()
#11 0x00013f1d in std::_List_const_iterator<Tone> std::max_element<std::_List_const_iterator<Tone>, bool (*)(Tone const&, Tone const&)>(std::_List_const_iterator<Tone>, std::_List_const_iterator<Tone>, bool (*)(Tone const&, Tone const&)) at /Developer/of_007_iphone/apps/cwi007/SingXO/SingXO/Pitch/pitch.hh:25
#12 0x000139b2 in Analyzer::findTone(double, double) const ()
#13 0x00011950 in -[AppDelegate timerCallback:] ()
#14 0x00115308 in -[CCTimer update:] ()
#15 0x0011e124 in -[CCScheduler tick:] ()
#16 0x0014993a in -[CCDirectorIOS drawScene] ()
#17 0x0014bda4 in -[CCDirectorDisplayLink mainLoop:] ()
#18 0x007baa88 in CA::Display::DisplayLink::dispatch(unsigned long long, unsigned long long) ()
#19 0x007babcd in CA::Display::EmulatorDisplayLink::callback(__CFRunLoopTimer*, void*) ()
#20 0x019c88c3 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
#21 0x019c9e74 in __CFRunLoopDoTimer ()
#22 0x019262c9 in __CFRunLoopRun ()
#23 0x01925840 in CFRunLoopRunSpecific ()
#24 0x01925761 in CFRunLoopRunInMode ()
#25 0x020bb1c4 in GSEventRunModal ()
#26 0x020bb289 in GSEventRun ()
#27 0x00beac93 in UIApplicationMain ()
#28 0x00002fb6 in main ()

我也在控制台中得到了这个:

GuardMalloc[MyApp-723]: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

哦,当然,这是现在看来可能是罪魁祸首的函数:

- (void)timerCallback:(NSTimer *)timer {
  Tone const * tone = audio->analyzer->findTone();
  if (tone && tone->db > kToneMinDB) {
    self.currentPitch = tone->freq;
    self.currentVolume = tone->stabledb;
    self.currentNoteName = [NSString stringWithCString:scale->getNoteStr(tone->freq).c_str() encoding:NSUTF8StringEncoding];
//    NSLog(@"Current Note Name in timerCallback: %@", currentNoteName);
    self.currentNoteFunction = [pitchFilter getPitchFunctionFromPitchName:currentNoteName];
  } else {
    self.currentNoteName = @"--";
    self.currentNoteFunction = -1;
  }

}

我不明白程序如何输入 if (tone) 块没有有效的音调,或者相反,如果音调有效,它如何崩溃。帮助?

(这太长了。我应该删除其中一些以解决更集中的问题吗?)

我在想的事情: 1. 这是一个线程问题 ::shudder:: 其中一个线程正在更改音频数据,而另一个线程正在读取它。 2. 我不知道,timerCallback 函数中的某些内容正在损坏堆栈,因为它分配得太频繁了。这个函数被调用很多,但不是一个极端的数量;每 150 毫秒安排一次。

但不确定为什么这两个问题在模拟器中会成为问题,但在设备上却不会。

I am writing an iOS app that uses live audio analysis. It has an intermittent crash (after ~5 min, only on the Simulator. While this means it's not of concern for a shipping app, it sure is a pain for development, and besides, I'll sleep better at night if I track it down. The crash always happens in the same place, in a static function in my audio-analysis code: The crash has happened here:

struct Tone {
// (other stuff)

// THIS is the problem function:
static bool dbCompare(Tone const& l, Tone const& r) { return l.db < r.db; }
  }
};

And also here:

Tone const* findTone(double minfreq = 70.0, double maxfreq = 700.0) const {
    if (m_tones.empty()) { m_oldfreq = 0.0; return NULL; }
    double db = std::max_element(m_tones.begin(), m_tones.end(), Tone::dbCompare)->db;
    Tone const* best = NULL;
    double bestscore = 0;
    for (tones_t::const_iterator it = m_tones.begin(); it != m_tones.end(); ++it) {

    // ON THIS LINE. Is my iterator somehow becoming invalid?:
        if (it->db < db - 20.0 || it->freq < minfreq || it->age < Tone::MINAGE) continue;
        if (it->freq > maxfreq) break;
        double score = it->db - std::max(180.0, std::abs(it->freq - 300.0)) / 10.0;
        if (m_oldfreq != 0.0 && std::fabs(it->freq/m_oldfreq - 1.0) < 0.05) score += 10.0;
        if (best && bestscore > score) break;
        best = &*it;
        bestscore = score;
    }
    m_oldfreq = (best ? best->freq : 0.0);
    return best;
}

(Ok, this fix, below, is wrong per @Justin. I'm leaving it here for completeness, for now at least).
In an attempt to survive any null pointers that might get passed in, I added some checks and attempts at error-handling:

struct Tone {
    static bool dbCompare(Tone const& l, Tone const& r) {
    try {
      if (&l == NULL || &r == NULL) throw 1;
      return l.db < r.db;
      throw 1;
    }
    catch(int e) {
      NSLog(@"AUDIO ERROR: Pointer invalid. %d", e);
    }
    return 0;
  }
};

No dice, it still fails on the same line. Am I misunderstanding try-catch-throw? Is the error just happening further back up the pipeline?

This bit of code is from the open-source Rock-Band-style game Performous, and their game doesn't have this problem. (It's also not for the iPhone.) So this gives me hope that I can eliminate the Simulator crash...

EDIT 2 Per @Justin I am running with GuardMalloc. Simulator 5.0 gives immediate crash— there seem to be some SO questions about this but for now I'm running the 4.3 simulator, which gives the crash in the normal fashion, but with nothing else I know how to use:

#0  0x94f38c97 in malloc_error_break ()
#1  0x94efa4ce in szone_error ()
#2  0x94efc35f in allocate_pages ()
#3  0x94f013cc in allocate_pages_securely ()
#4  0x94f019b8 in szone_malloc_should_clear ()
#5  0x94f0266b in szone_malloc ()
#6  0x00331cc3 in GMmalloc_zone_malloc ()
#7  0x94f38edb in malloc_set_zone_name ()
#8  0x94f394a8 in _malloc_initialize ()
#9  0x94f3986b in malloc ()
#10 0x00002ef0 in start ()
#11 0x00013f1d in std::_List_const_iterator<Tone> std::max_element<std::_List_const_iterator<Tone>, bool (*)(Tone const&, Tone const&)>(std::_List_const_iterator<Tone>, std::_List_const_iterator<Tone>, bool (*)(Tone const&, Tone const&)) at /Developer/of_007_iphone/apps/cwi007/SingXO/SingXO/Pitch/pitch.hh:25
#12 0x000139b2 in Analyzer::findTone(double, double) const ()
#13 0x00011950 in -[AppDelegate timerCallback:] ()
#14 0x00115308 in -[CCTimer update:] ()
#15 0x0011e124 in -[CCScheduler tick:] ()
#16 0x0014993a in -[CCDirectorIOS drawScene] ()
#17 0x0014bda4 in -[CCDirectorDisplayLink mainLoop:] ()
#18 0x007baa88 in CA::Display::DisplayLink::dispatch(unsigned long long, unsigned long long) ()
#19 0x007babcd in CA::Display::EmulatorDisplayLink::callback(__CFRunLoopTimer*, void*) ()
#20 0x019c88c3 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ ()
#21 0x019c9e74 in __CFRunLoopDoTimer ()
#22 0x019262c9 in __CFRunLoopRun ()
#23 0x01925840 in CFRunLoopRunSpecific ()
#24 0x01925761 in CFRunLoopRunInMode ()
#25 0x020bb1c4 in GSEventRunModal ()
#26 0x020bb289 in GSEventRun ()
#27 0x00beac93 in UIApplicationMain ()
#28 0x00002fb6 in main ()

I also get this in the console:

GuardMalloc[MyApp-723]: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

Oh, and of course, here's the function that NOW seems to be the likely culprit:

- (void)timerCallback:(NSTimer *)timer {
  Tone const * tone = audio->analyzer->findTone();
  if (tone && tone->db > kToneMinDB) {
    self.currentPitch = tone->freq;
    self.currentVolume = tone->stabledb;
    self.currentNoteName = [NSString stringWithCString:scale->getNoteStr(tone->freq).c_str() encoding:NSUTF8StringEncoding];
//    NSLog(@"Current Note Name in timerCallback: %@", currentNoteName);
    self.currentNoteFunction = [pitchFilter getPitchFunctionFromPitchName:currentNoteName];
  } else {
    self.currentNoteName = @"--";
    self.currentNoteFunction = -1;
  }

}

I don't understand how the program is entering the if (tone) block without a valid tone, or conversely how it can crash if the tone is valid. Help?

(This is getting long. Should I delete some of it in the service of a more-focused question?)

Things I'm thinking:
1. It's a threading issue ::shudder:: where one thread is changing the audio data while another one is reading it.
2. Something in the timerCallback function is corrupting, I dunno, the stack because it's alloc'ing too often. This function gets called a lot, but not an extreme amount; it's scheduled for every 150 ms.

Not sure why either of these would be an issue in the simulator but not on the device, though.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

俯瞰星空 2025-01-08 01:12:38

C++ 引用永远不能为 0/NULL。如果是的话,那么你已经进入了未定义的领域。如果参数可能为 0,则必须将测试在执行链中向上移动。

也就是说,dbCompare 的唯一格式正确的版本如下所示:

struct Tone {
    static bool dbCompare(Tone const& l, Tone const& r) {
        return l.db < r.db;
    }
};

Update

如果在迭代时调整迭代器的大小,则迭代器将无效——我在这个实现中没有看到这种情况发生。您是否正在从另一个线程调整容器的大小?

另外,尝试使用 GuardMalloc 运行它 - 它的目的是使这些问题对您来说显而易见。通过更快地失败,它对于实施来说将更加本地化(在许多情况下)。

A C++ reference can never be 0/NULL. If it were, you're already well into Undefined Territory. You will have to move your tests up in the execution chain if the parameters may be 0.

That is to say, the only well formed version of dbCompare looks like this:

struct Tone {
    static bool dbCompare(Tone const& l, Tone const& r) {
        return l.db < r.db;
    }
};

Update

Your iterator would be invalid if it were resized while you iterate -- I don't see that happening in this implementation. Are you resizing the container from another thread?

Also, try running it with GuardMalloc - it aims to make these issues obvious to you. By failing sooner, it will be more local to the implementation (in many cases).

忆梦 2025-01-08 01:12:38

' best = &*it' 行对我来说看起来很奇怪。容器是按引用存储还是按值存储?看来您可能正在返回临时存储的地址。这可能会导致问题。

The ' best = &*it' line looks odd to me. Is the container storing by reference or value? It seems like you might be returning the address of temporary storage. That could cause problems down the line.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文