C++ 如何理解“一个”;“不可能”;崩溃后的堆栈跟踪?

C++ 如何理解“一个”;“不可能”;崩溃后的堆栈跟踪?,c++,macos,stack-trace,crash-reports,wakeup,C++,Macos,Stack Trace,Crash Reports,Wakeup,我的程序似乎遇到了一个很难重现的错误:每当用户将Mac电脑置于睡眠状态,然后再次将其唤醒时,我的程序的一个子进程将在Mac电脑唤醒后立即崩溃 发生这种情况时,苹果的crash reporter机制会可靠地报告如下堆栈跟踪: Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libsystem_kernel.dylib 0x967f9a6a __pthread_kill + 10 1 libsystem_

我的程序似乎遇到了一个很难重现的错误:每当用户将Mac电脑置于睡眠状态,然后再次将其唤醒时,我的程序的一个子进程将在Mac电脑唤醒后立即崩溃

发生这种情况时,苹果的crash reporter机制会可靠地报告如下堆栈跟踪:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib          0x967f9a6a __pthread_kill + 10
1   libsystem_c.dylib               0x9003dacf pthread_kill + 101
2   libsystem_c.dylib               0x900744f8 abort + 168
3   com.meyersound.VirtualD-Mitri   0x0014438e muscle::CrashSignalHandler(int) + 190
4   libsystem_c.dylib               0x9002886b _sigtramp + 43
5   ???                             0xffffffff 0 + 4294967295
6   com.meyersound.VirtualD-Mitri   0x001442d0 muscle::ParsePortArg(muscle::Message const&, muscle::String const&, unsigned short&, unsigned long) + 80
7   com.meyersound.VirtualD-Mitri   0x005b3393 qnet::RepDBPeer::Pulse(muscle::PulseNode::PulseArgs const&) + 1187
8   com.meyersound.VirtualD-Mitri   0x0015717b muscle::PulseNode::PulseAux(unsigned long long) + 203
9   com.meyersound.VirtualD-Mitri   0x000cfb90 muscle::ReflectServer::ServerProcessLoop() + 3232
10  com.meyersound.VirtualD-Mitri   0x00607c7e dcasldmain(int, char**) + 2222
11  com.meyersound.VirtualD-Mitri   0x0072c14d dmitridmain(int, char**) + 4749
12  com.meyersound.VirtualD-Mitri   0x0000bc3a main + 4938
13  com.meyersound.VirtualD-Mitri   0x000061ab _start + 209
14  com.meyersound.VirtualD-Mitri   0x000060d9 start + 41
这一切都很好,除了(暗示怪异的音乐)——这在逻辑上是不可能的。特别是,不仅我的
RepDBPeer::Pulse()
方法从未调用过
ParsePortArg
,崩溃进程也从未在任何地方调用过
ParsePortArg
!(我把所有的源代码都灰显了两次以确保)

所以我的问题是,这个堆栈跟踪想告诉我什么?这是否最有可能是线程0的堆栈被严重损坏,以至于堆栈跟踪机制偏离轨道,将无辜的旁观者作为罪魁祸首?或者苹果的唤醒机制是否可能以某种方式将程序计数器“跳转”到ParsePortArg()(由此产生的混乱导致了崩溃)?还是有其他我无法想象的更深层次的魔法


所讨论的崩溃进程是一个普通的非GUI后台进程,它是一个由Qt GUI进程派生的子进程,这是值得的。

我假设您已经打开了一些优化。没有魔法可以叠加痕迹。一旦代码被内联或省略,它们就变得越来越模糊(读为“不精确”),这正是C++优化器所做的。 在
ParsePortArg
的情况下,该行末尾有一个+80,意味着代码段中该函数的入口点前面有80个字节。这表示指令指针在
0x001442d0
处的真实地址,
ParsePortArg
是堆栈转储猜测的最接近的符号。你认为这是在转移视线,这是对的


除非有任何其他数据,否则我会非常保守地猜测,您的程序期望某些指针保持有效,而这些指针在从睡眠中醒来时无效。查看反汇编以获取该地址的指令。我打赌正在尝试读取内存地址

我试着去想一种情况,在这种情况下,可以调用一个命名函数,而不需要看起来像它。宏和函数指针立即跃入脑海。这个函数显然已经定义好了,我们能看到
ParsePortArg
所在的其他地方吗?可能是大量的优化。如果grep以某种方式在变量中传递此函数,则grep不会有帮助。异常会在堆栈上推送非标准帧,而展开代码并不总是识别它们。@Mooing Duck ParsePortArg()仅在dccdmain()的顶部调用一次。dccdmain()是一个函数,如果argv包含一个关键字告诉它这样做,dmitridmain()将调用该函数,但在本例中它永远不会调用(因为它调用dcasldmain()。(dmitridmain()是busybox风格的委托层)此程序中使用了多少线程?一个线程堆栈上的对象是否有可能被另一个线程丢弃?谢谢,这非常有用。在检查反汇编时(通过“otool-tv MyApp.app/Contents/MacOS/MyApp”,我可以看到ParsePortArg()在可执行文件中与CrashSignalHandler()相邻,因此偏移量一定是欺骗了堆栈跟踪生成器打印ParsePortArg(),而它实际上是堆栈上的CrashSignalHandler()函数。