EXC_BREAKPOINT崩溃的原因

时间:2018-02-18 07:38:19

标签: c++ macos debugging

我在Logic X内运行的C ++ audiounit组件中的某些用户计算机上发生了崩溃。我不能在本地重复它,并且在尝试弄清楚它是如何发生的过程中我有一些的问题。

以下是崩溃转储中的相关信息:

Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY

Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [0]

问题是:

  • 在我正在考虑的情况下,什么可能导致EXC_BREAKPOINT。 Apple的这些信息是否完整和准确:“与异常退出类似,此异常旨在为附加调试器提供在执行中的特定点中断进程的机会。您可以从自己的触发器触发此异常代码使用__builtin_trap()函数。如果没有连接调试器,则终止进程并生成崩溃报告。“
  • 为什么会在SharedObject + 200上发生(请参阅反汇编)
  • 崩溃发生时RBX是'this'指针。

崩溃发生在这里:

juce::ValueTree::SharedObject::SharedObject(juce::ValueTree::SharedObject const&) + 200

C ++如下:

SharedObject (const SharedObject& other)
    : ReferenceCountedObject(),
      type (other.type), properties (other.properties), parent (nullptr)
{
    for (int i = 0; i < other.children.size(); ++i)
    {
        SharedObject* const child = new SharedObject (*other.children.getObjectPointerUnchecked(i));
        child->parent = this;
        children.add (child);
    }
}

反汇编:

->  0x127167950 <+0>:   pushq  %rbp
    0x127167951 <+1>:   movq   %rsp, %rbp
    0x127167954 <+4>:   pushq  %r15
    0x127167956 <+6>:   pushq  %r14
    0x127167958 <+8>:   pushq  %r13
    0x12716795a <+10>:  pushq  %r12
    0x12716795c <+12>:  pushq  %rbx
    0x12716795d <+13>:  subq   $0x18, %rsp
    0x127167961 <+17>:  movq   %rsi, %r12
    0x127167964 <+20>:  movq   %rdi, %rbx
    0x127167967 <+23>:  leaq   0x589692(%rip), %rax      ; vtable for juce::ReferenceCountedObject + 16
    0x12716796e <+30>:  movq   %rax, (%rbx)
    0x127167971 <+33>:  movl   $0x0, 0x8(%rbx)
    0x127167978 <+40>:  leaq   0x599fe9(%rip), %rax      ; vtable for juce::ValueTree::SharedObject + 16
    0x12716797f <+47>:  movq   %rax, (%rbx)
    0x127167982 <+50>:  leaq   0x10(%rbx), %rdi
    0x127167986 <+54>:  movq   %rdi, -0x30(%rbp)
    0x12716798a <+58>:  leaq   0x10(%r12), %rsi
    0x12716798f <+63>:  callq  0x12711cf70               ; juce::Identifier::Identifier(juce::Identifier const&)
    0x127167994 <+68>:  leaq   0x18(%rbx), %rdi
    0x127167998 <+72>:  movq   %rdi, -0x38(%rbp)
    0x12716799c <+76>:  leaq   0x18(%r12), %rsi
    0x1271679a1 <+81>:  callq  0x12711c7b0               ; juce::NamedValueSet::NamedValueSet(juce::NamedValueSet const&)
    0x1271679a6 <+86>:  movq   $0x0, 0x30(%rbx)
    0x1271679ae <+94>:  movl   $0x0, 0x38(%rbx)
    0x1271679b5 <+101>: movl   $0x0, 0x40(%rbx)
    0x1271679bc <+108>: movq   $0x0, 0x48(%rbx)
    0x1271679c4 <+116>: movl   $0x0, 0x50(%rbx)
    0x1271679cb <+123>: movl   $0x0, 0x58(%rbx)
    0x1271679d2 <+130>: movq   $0x0, 0x60(%rbx)
    0x1271679da <+138>: cmpl   $0x0, 0x40(%r12)
    0x1271679e0 <+144>: jle    0x127167aa2               ; <+338>
    0x1271679e6 <+150>: xorl   %r14d, %r14d
    0x1271679e9 <+153>: nopl   (%rax)
    0x1271679f0 <+160>: movl   $0x68, %edi
    0x1271679f5 <+165>: callq  0x12728c232               ; symbol stub for: operator new(unsigned long)
    0x1271679fa <+170>: movq   %rax, %r13
    0x1271679fd <+173>: movq   0x30(%r12), %rax
    0x127167a02 <+178>: movq   (%rax,%r14,8), %rsi
    0x127167a06 <+182>: movq   %r13, %rdi
    0x127167a09 <+185>: callq  0x127167950               ; <+0>
    0x127167a0e <+190>: movq   %rbx, 0x60(%r13)        // MY NOTES: child->parent = this
    0x127167a12 <+194>: movl   0x38(%rbx), %ecx
    0x127167a15 <+197>: movl   0x40(%rbx), %eax
    0x127167a18 <+200>: cmpl   %eax, %ecx

更新1: 看起来RIP似乎建议我们处于'add'调用的中间,这就是这个函数,内联:

/** Appends a new object to the end of the array.

    This will increase the new object's reference count.

    @param newObject       the new object to add to the array
    @see set, insert, addIfNotAlreadyThere, addSorted, addArray
*/
ObjectClass* add (ObjectClass* const newObject) noexcept
{
    data.ensureAllocatedSize (numUsed + 1);
    jassert (data.elements != nullptr);
    data.elements [numUsed++] = newObject;

    if (newObject != nullptr)
        newObject->incReferenceCount();

    return newObject;
}

更新2: 在相关寄存器的崩溃寄存器值处:

this == rbx: 0x00007fe5bc37c950
&other == r12: 0x00007fe5bc348cc0
rax = 0
rcx = 0

2 个答案:

答案 0 :(得分:1)

此代码中可能存在一些问题:

  • 就像SM提到的那样,other.children.getObjectPointerUnchecked(i)可以返回nullptr
  • ObjectClass* add (ObjectClass* const newObject) noexcept中,你在调用incReferenceCount之前检查newObject是否为空(这意味着在此方法调用中可能会出现null),但是你不进行空检查在data.elements [numUsed++] = newObject;期间添加此对象之前,所以你可能在这里有一个nullptr,如果你在没有检查的情况下在其他地方调用这个数组,你可能会崩溃。
  • 我们真的不知道data.elements的类型(我假设一个数组),但是由于指针赋值可能会发生复制操作(如果指针转换为object,如果是data.elements不是一个指针数组或者有一些操作符超载),这里可能会崩溃(但不太可能)
  • 在子级和父级之间存在循环引用,因此在对象销毁期间可能存在问题

答案 1 :(得分:1)

我怀疑问题是应该放在堆上的共享的,重新计数的对象是无意中在堆栈上分配的。如果堆栈展开并且之后被覆盖,并且仍然存在对引用计数的对象的引用,则在访问该引用时将在不可预测的时间发生随机事件。 (这个和&amp; other的地址空间的接近程度也让我怀疑它们都在堆栈中。)