使用 catch(...) (省略号)进行事后分析

发布于 2024-08-19 19:15:16 字数 457 浏览 4 评论 0原文

另一个问题中的某人建议使用 catch(...) 来捕获所有其他未处理的 - 意外/不可预见的异常,方法是用 try 包围整个 main() {}catch(...){} 块。

这听起来是一个有趣的想法,可以节省大量调试程序的时间,并至少留下发生了什么的提示。

问题的本质是可以通过这种方式恢复哪些信息(除了我留下的任何调试全局变量),以及如何恢复它(如何访问和识别任何信息) catch 被调用)

此外,还有哪些警告与之相关。特别是:

  • 它会与稍后萌芽的线程一起很好地发挥作用吗?
  • 它不会破坏处理段错误(在其他地方捕获为信号)吗?
  • 它不会影响不可避免地嵌套在其中的其他 try...catch 块,这些块是用来处理预期异常的吗?

Someone in a different question suggested using catch(...) to capture all otherwise unhandled - unexpected/unforseen exceptions by surrounding the whole main() with the try{}catch(...){} block.

It sounds like an interesting idea that could save a lot of time debugging the program and leave at least a hint of what happened.

The essence of the question is what information can be recovered that way (other than whatever debug globals I leave behind), and how to recover it (how to access and recognize whatever catch was called with)

Also, what caveats are connected with it. In particular:

  • will it play nice with threads that sprout later?
  • will it not break handling segfaults (captured elsewhere as signal)
  • will it not affect other try...catch blocks inevitably nested inside, that are there to handle expected exceptions?

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

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

发布评论

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

评论(6

著墨染雨君画夕 2024-08-26 19:15:16

是的,这是个好主意。

如果您让异常逃逸 main ,则这是实现定义的天气,堆栈在应用程序关闭之前展开。因此,在我看来,捕获 main 中的所有异常至关重要。

那么问题就变成了如何处理它们。
一些操作系统(参见 MS 和 SE)提供了一些额外的调试工具,因此在捕获异常后重新抛出异常很有用(因为堆栈现在已经被展开)。

int main()
{
    try
    {
        /// All real code
    }
    // I see little point in catching other exceptions at this point 
    // (apart from better logging maybe). If the exception could have been caught
    // and fixed you should have done it before here.

    catch(std::exception const& e)
    {
         // Log e.what() Slightly better error message than ...
         throw;
    }
    catch(...)   // Catch all exceptions. Force the stack to unwind correctly.
    {
        // You may want to log something it seems polite.
        throw;  // Re-throw the exception so OS gives you a debug opportunity.
    }
}
  • 它会与稍后萌芽的线程一起使用吗?

它应该对线程没有影响。通常您必须手动加入任何子线程以确保它们已退出。当主退出时子线程发生的情况的确切细节没有明确定义(因此请阅读您的文档),但通常所有子线程都会立即死亡(一种不涉及展开堆栈的令人讨厌和可怕的死亡)。

如果您正在谈论子线程中的异常。同样,这没有明确定义(因此请阅读您的文档),但是如果线程通过异常退出(即用于启动线程的函数由于异常而不是返回而退出),那么这通常会导致应用程序终止(同样的影响)如上所述)。因此,最好阻止所有异常退出线程。

  • 它不会破坏处理段错误(在其他地方捕获为信号)

信号不受异常处理机制的影响。
但是因为信号处理程序可能会在堆栈上放置一个奇怪的结构(为了它们自己的返回处理返回到正常代码),所以从信号处理程序中抛出异常并不是一个好主意,因为这可能会导致意外的结果(并且绝对不可移植) )。

  • 它不会影响不可避免地嵌套在其中的其他用于处理预期异常的 try...catch 块吗?

应该对其他处理程序没有影响。

Yes it is a good idea.

If you let an exception escape main it is implementation defined weather the stack is unwound before the application is shut down. So in my opinion it is essential you catch all exceptions in main.

The question then becomes what to do with them.
Some OS (See MS and SE) provide some extra debugging facilities so it is useful to just re-throw the exception after you catch it (because the stack has been unwound now anyway).

int main()
{
    try
    {
        /// All real code
    }
    // I see little point in catching other exceptions at this point 
    // (apart from better logging maybe). If the exception could have been caught
    // and fixed you should have done it before here.

    catch(std::exception const& e)
    {
         // Log e.what() Slightly better error message than ...
         throw;
    }
    catch(...)   // Catch all exceptions. Force the stack to unwind correctly.
    {
        // You may want to log something it seems polite.
        throw;  // Re-throw the exception so OS gives you a debug opportunity.
    }
}
  • will it play nice with threads that sprout later?

It should have no affect on threads. Usually you have to manually join any child threads to make sure that they have exited. The exact details of what happens to child threads when main exits is not well defined (so read your documentation) but usually all child threads will die instantly (a nasty and horrible death that does not involve unwinding their stacks).

If you are talking about exceptions in child threads. Again this is not well defined (so read your documentation) but if a thread exits via an exception (ie the function used to start the thread exits because of an exception and not a return) then this usually causes the application to terminate (same affect as above). So it is always best to stop ALL exceptions from exiting a thread.

  • will it not break handling segfaults (captured elsewhere as signal)

Signals are not affected by the exception handling mechanism.
But because signal handlers may place an odd structure on the stack (for their own return handling back to normal code) it is not a good idea to throw an exception from within a signal handler as this may cause unexpected results (and is definitely not portable).

  • will it not affect other try...catch blocks inevitably nested inside, that are there to handle expected exceptions?

Should have no effect on other handlers.

天邊彩虹 2024-08-26 19:15:16

据我记得,Win32 上的 catch(...) 也会捕获 SEH 异常,而您不想这样做。如果您收到 SEH 异常,那是因为发生了非常可怕的事情(主要是访问违规),因此您不能再信任您的环境。几乎所有你能做的事情都可能因另一个 SEH 异常而失败,所以它甚至不值得尝试。此外,一些SEH异常是为了让系统捕获的;有关此内容的更多信息,请此处

因此,我的建议是对所有异常使用基本异常类(例如 std::exception),并在“catchall”中捕获该类型;您的代码无法准备好处理其他类型的异常,因为它们的定义是未知的。

As far as I remember, catch(...) on Win32 catches also SEH exceptions, and you do not want to do that. If you get a SEH exception it's because something very scary happened (mainly access violations), so you can't trust your environment anymore. Almost everything you could do may fail with another SEH exception, so it's not even worth trying. Moreover, some SEH exceptions are intended to be caught by the system; more on this here.

So, my advice is to use a base exception class (e.g. std::exception) for all your exceptions, and catch just that type in the "catchall"; your code cannot be prepared to deal with other kind of exceptions, since they are unknown by definition.

靑春怀旧 2024-08-26 19:15:16

全局 try catch 块对于生产系统很有用,以避免向用户显示令人讨厌的消息。在开发过程中,我认为最好避免这种情况。

关于您的问题:

  • 我相信全局 catch 块不会捕获另一个线程中的异常。每个线程都有自己的堆栈空间。
  • 我对此不太确定。
  • 嵌套的 try...catch 块不受影响并且将照常执行。异常在堆栈中向上传播,直到找到 try 块。

A global try catch block is useful for production systems, in order to avoid displaying a nasty message to the user. During development I believe that are best avoided.

Regarding your questions:

  • I believe that a global catch block won't catch exceptions in another thread. Each thread has its own stack space.
  • I am not sure about this.
  • Nested try...catch blocks aren't affected and will execute as usual. An exception propagates up the stack, until it finds a try block.
走野 2024-08-26 19:15:16

如果您正在做,您可以尝试我使用的解决方案 .net 应用程序。这捕获了所有未处理的异常。当我不使用调试器时,我通常只为生产代码启用代码(使用 #ifndef DEBUG)。

值得指出的是,kgiannakakis 提到您无法捕获其他线程中的异常,但您可以在这些线程中使用相同的 try-catch 方案,并将异常发布回主线程,您可以在主线程中重新抛出它们以获得完整堆栈跟踪出了什么问题。

You could try a solution I use if you're making a .net application. That captures all unhandled exceptions. I generally only enable the code (with #ifndef DEBUG) for production code when I'm not using the debugger.

It's worth pointing out as kgiannakakis mentions that you can't capture exceptions in other threads, but you can use the same try-catch scheme in those threads and post the exceptions back to the main thread where you can re-throw them to get a full stack track of what went wrong.

野の 2024-08-26 19:15:16

以及如何恢复它(如何访问
并认识到无论捕获了什么
调用)

如何恢复抛出的异常类型,则可以在回退到 catch (...) 之前链接特定类型的 catch 块(从更具体到更一般) >:

try {
   ...
} catch (const SomeCustomException& e) {
   ...
} catch (const std::bad_alloc& e) {
   ...
} catch (const std::runtime_error& e) {
   // Show some diagnosic for generic runtime errors...
} catch (const std::exception& e) {
   // Show some diagnosic for any other unhandled std::exceptions...
} catch (...) {
   // Fallback for unknown errors.
   // Possibly rethrow or omit this if you think the OS can do something with it.
}

请注意,如果您发现自己在多个地方执行此操作并想要合并代码(可能是单独程序的多个 main 函数),您可以编写一个函数:

void MyExceptionHandler() {
   try {
      throw; // Rethrow the last exception.
   } catch (const SomeCustomException& e) {
      ...
   }
   ...
}

int main(int argc, char** argv) {
   try {
      ...
   } catch (...) {
      MyExceptionHandler();
   }
}

and how to recover it (how to access
and recognize whatever catch was
called with)

If you mean how to recover the type of exception that was thrown, you can chain catch blocks for specific types (proceeding from more specific to more general) before falling back to catch (...):

try {
   ...
} catch (const SomeCustomException& e) {
   ...
} catch (const std::bad_alloc& e) {
   ...
} catch (const std::runtime_error& e) {
   // Show some diagnosic for generic runtime errors...
} catch (const std::exception& e) {
   // Show some diagnosic for any other unhandled std::exceptions...
} catch (...) {
   // Fallback for unknown errors.
   // Possibly rethrow or omit this if you think the OS can do something with it.
}

Note that if you find yourself doing this in multiple places and want to consolidate code (maybe multiple main functions for separate programs), you can write a function:

void MyExceptionHandler() {
   try {
      throw; // Rethrow the last exception.
   } catch (const SomeCustomException& e) {
      ...
   }
   ...
}

int main(int argc, char** argv) {
   try {
      ...
   } catch (...) {
      MyExceptionHandler();
   }
}
冷心人i 2024-08-26 19:15:16

包罗万象不会很有用,因为没有可以查询的类型/对象信息。但是,如果您可以确保应用程序引发的所有异常均派生自单个基本对象,则可以对基本异常使用 catch 块。但这样就不能包罗万象了。

A catch-all will not be terribly useful since there is no type/object information that you can query. However, if you can make sure all exceptions raised by your application are derived from a single base object, you can use a catch block for the base exception. But then that wouldn't be a catch-all.

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