Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[xbt] Install a custom exception handler
[simgrid.git] / src / xbt / exception.cpp
1 /* Copyright (c) 2005-2016. The SimGrid Team.
2  * All rights reserved. */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <exception>
8 #include <typeinfo>
9 #include <memory>
10 #include <mutex>
11
12 #include <xbt/backtrace.hpp>
13 #include <xbt/exception.hpp>
14 #include <xbt/log.h>
15 #include <xbt/log.hpp>
16
17 extern "C" {
18 XBT_LOG_EXTERNAL_CATEGORY(xbt);
19 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_exception, xbt, "Exceptions");
20 }
21
22 namespace simgrid {
23 namespace xbt {
24
25 WithContextException::~WithContextException() {}
26
27 void logException(
28   e_xbt_log_priority_t prio,
29   const char* context, std::exception const& exception)
30 {
31   try {
32     auto name = simgrid::xbt::demangle(typeid(exception).name());
33
34     auto with_context =
35       dynamic_cast<const simgrid::xbt::WithContextException*>(&exception);
36     if (with_context != nullptr)
37       XBT_LOG(prio, "%s %s by %s/%d: %s",
38         context, name.get(),
39         with_context->processName().c_str(), with_context->pid(),
40         exception.what());
41     else
42       XBT_LOG(prio, "%s %s: %s", context, name.get(), exception.what());
43
44     // Do we have a backtrace?
45     if (with_context != nullptr) {
46       auto backtrace = simgrid::xbt::resolveBacktrace(
47         with_context->backtrace().data(), with_context->backtrace().size());
48       for (std::string const& s : backtrace)
49         XBT_LOG(prio, "  -> %s", s.c_str());
50     }
51
52     // Do we have a nested exception?
53     auto with_nested = dynamic_cast<const std::nested_exception*>(&exception);
54     if (with_nested == nullptr ||  with_nested->nested_ptr() == nullptr)
55       return;
56     try {
57       with_nested->rethrow_nested();
58     }
59     catch (std::exception& nested_exception) {
60       logException(prio, "Caused by", nested_exception);
61     }
62     // We could catch nested_exception or WithContextException but we don't bother:
63     catch (...) {
64       XBT_LOG(prio, "Caused by an unknown exception");
65     }
66   }
67   catch (...) {
68     // Don't log exceptions we got when trying to log exception
69   }
70 }
71
72 static void showBacktrace(std::vector<xbt_backtrace_location_t>& bt)
73 {
74   std::vector<std::string> res = resolveBacktrace(&bt[0], bt.size());
75   XBT_LOG(xbt_log_priority_critical, "Current backtrace:");
76   for (std::string const& s : res)
77     XBT_LOG(xbt_log_priority_critical, "  -> %s", s.c_str());
78 }
79
80 static std::terminate_handler previous_terminate_handler = nullptr;
81
82 static void handler()
83 {
84   // Avoid doing crazy things if we get an uncaught exception inside
85   // an uncaught exception
86   static std::atomic_flag lock = ATOMIC_FLAG_INIT;
87   if (lock.test_and_set()) {
88     XBT_ERROR("Multiple uncaught exceptions");
89     std::abort();
90   }
91
92   // Get the current backtrace and exception
93   auto e = std::current_exception();
94   auto bt = backtrace();
95   try {
96     std::rethrow_exception(e);
97   }
98
99   // We manage C++ exception ourselves
100   catch (std::exception& e) {
101     logException(xbt_log_priority_critical, "Uncaught exception", e);
102     showBacktrace(bt);
103     std::abort();
104   }
105
106   // We don't know how to manage other exceptions
107   catch (...) {
108     // If there was another handler let's delegate to it
109     if (previous_terminate_handler)
110       previous_terminate_handler();
111     else {
112       XBT_ERROR("Unknown uncaught exception");
113       showBacktrace(bt);
114       std::abort();
115     }
116   }
117
118 }
119
120 void installExceptionHandler()
121 {
122   static std::once_flag handler_flag;
123   std::call_once(handler_flag, [] {
124     previous_terminate_handler = std::set_terminate(handler);
125   });
126 }
127
128 }
129 }
130
131 void xbt_set_terminate()
132 {
133   simgrid::xbt::installExceptionHandler();
134 }