堆栈展开失败的原因

Posted

技术标签:

【中文标题】堆栈展开失败的原因【英文标题】:Reasons for stack unwinding fail 【发布时间】:2009-03-05 19:28:29 【问题描述】:

我在调试一个应用程序时遇到了以下代码:

int Func()


 try 
 

   CSingleLock aLock(&m_CriticalSection, TRUE);
   
     //user code
   
 
 catch(...)
 
     //exception handling
 
 return -1;


m_CriticalSection 是 CCrialSection。

我发现用户代码抛出了一个异常,使得 m_CriticalSection 根本没有被释放。这意味着由于某些原因堆栈已损坏,因此展开失败。

我的问题是: 1) 在哪些不同情况下堆栈展开可能会失败?

2) 有哪些不同的异常可能引发堆栈展开失败。

3) 我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

谢谢,

【问题讨论】:

如果你提到抛出了什么样的异常,如果它实际上在 catch(...) 处被捕获,将会有所帮助。 为什么不在用户代码周围放置 try ... catch 块呢? 【参考方案1】:

您是否遇到程序异常终止?

我相信你的CCriticalSection 对象将被释放CSingleLock 的析构函数。析构函数总是会被调用,因为这是堆栈上的一个对象。当用户代码抛出时,throw 和函数中的 catch 之间的所有堆栈都将被解除。

但是,您的用户代码中的某些其他对象甚至CSingleLock 析构函数可能在此期间引发了另一个异常。在这种情况下,m_CriticalSection 对象将无法正确释放,std::terminate 会被调用并且您的程序会终止。

这里有一些示例来演示。注意:我正在使用std::terminate 处理函数来通知我状态。您还可以使用std::uncaught_exception 查看是否有任何未捕获的异常。在 here 上有一个很好的讨论和示例代码。

struct S 
    S()  std::cout << __FUNCTION__ << std::endl; 
    ~S()  throw __FUNCTION__; std::cout << __FUNCTION__ << std::endl;  
;

void func() 
    try 
        S s;
        
            throw 42;
        
     catch(int e)             
         std::cout << "Exception: " << e << std::endl; 
    


void rip() 
    std::cout << " help me, O mighty Lord!\n"; // pray


int main() 
    std::set_terminate(rip);
    try 
        func();
    
    catch(char *se) 
        std::cout << "Exception: " << se << std::endl;
    

请阅读 this 常见问题以了解清楚。

我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

如果不查看堆栈和错误/崩溃就很难说。你为什么不试一试。它还可能通过隐藏真正的问题来引入一个微妙的错误。

【讨论】:

【参考方案2】:

首先让我说我不知道​​ CSingleLock 和 CCriticalSection 是做什么的。

我所知道的是,在您的“用户代码”部分中引发的异常应该展开堆栈并销毁在 try 块中创建的所有变量。

在我看来,我希望您的 aLock 变量会被异常破坏,而不是 m_CriticalSection。您正在将指向 m_CriticalSection 的指针传递给 aLock 变量,但 m_CriticalSection 对象已经存在,并且是在其他地方创建的。

【讨论】:

【参考方案3】: 您确定 m_CriticalSection 的生命周期比 CSingleLock 长吗? 可能有人损坏了您的堆栈?

3) 我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

在这种情况下 - 是的。但是请记住,将大块放入互斥锁中对性能不利。

顺便说一句,catch(...) 通常不是好习惯。在 Win32 中,它 (catch(...)) 也捕获 SEH 异常,而不仅仅是 c++ 异常。也许你在这个函数中有核心并用 catch(...) 捕获它。

【讨论】:

VC++ catch (...) 自 VC++2005 以来没有捕获 SEH 异常。 好消息和不错的补充:)【参考方案4】:

我的问题是: 1) 在哪些不同情况下堆栈展开可能会失败?

如果 exit() terminate() abort() 或 unexpected() 被调用。

除了直接调用之外,哪些情况可能发生: 引发未处理的异常。 (此处不适用) throw an exception from a destructor while another exception is popogating

2) 有哪些不同的异常可能引发堆栈展开失败。

throw 表达式的构造函数抛出异常 异常传播时析构函数抛出异常。 抛出的异常从未被捕获(如果这实际上展开堆栈,则实现定义)。 抛出的异常未在异常规范中指定。 跨 C ABI 引发异常。 在未捕获的线程内抛出异常(实现定义了发生的情况)

3) 我可以通过将 CSingleLock 放在 try 块之外来解决这个问题吗?

没有。以上所有情况都会导致应用程序终止而无需进一步展开堆栈。

【讨论】:

以上是关于堆栈展开失败的原因的主要内容,如果未能解决你的问题,请参考以下文章

gcc 如何在 linux 上实现 C++ 异常的堆栈展开?

了解Java在抛出异常时的堆栈展开

堆栈展开真的需要锁吗?

如何使堆栈面板内的按钮展开以占用整个空间?

在展开操作期间遇到无效或未对齐的堆栈

如何展开到导航堆栈上的第一个视图控制器