|
64 | 64 | #include "llvm/Support/FileSystem.h" |
65 | 65 | #include "llvm/Support/ManagedStatic.h" |
66 | 66 | #include "llvm/Support/Path.h" |
| 67 | +#include "llvm/Support/Process.h" |
| 68 | +#include "llvm/Support/Signals.h" |
67 | 69 | #include "llvm/Support/TargetSelect.h" |
68 | 70 | #include "llvm/Support/raw_ostream.h" |
69 | 71 | #include "llvm/TargetParser/Host.h" |
|
79 | 81 | #include <iterator> |
80 | 82 | #include <map> |
81 | 83 | #include <memory> |
| 84 | +#include <mutex> |
82 | 85 | #include <set> |
83 | 86 | #include <sstream> |
84 | 87 | #include <stack> |
@@ -163,24 +166,67 @@ struct InterpreterInfo { |
163 | 166 | InterpreterInfo& operator=(const InterpreterInfo&) = delete; |
164 | 167 | }; |
165 | 168 |
|
| 169 | +static void DefaultProcessCrashHandler(void*); |
166 | 170 | // Function-static storage for interpreters |
167 | 171 | static std::deque<InterpreterInfo>& GetInterpreters() { |
| 172 | + // static int FakeArgc = 1; |
| 173 | + // static const std::string VersionStr = GetVersion(); |
| 174 | + // static const char* ArgvBuffer[] = {VersionStr.c_str(), nullptr}; |
| 175 | + // static const char** FakeArgv = ArgvBuffer; |
| 176 | + // static llvm::InitLLVM X(FakeArgc, FakeArgv); |
| 177 | + // Cannot be a llvm::ManagedStatic because X will call shutdown which will |
| 178 | + // trigger destruction on llvm::ManagedStatics and the destruction of the |
| 179 | + // InterpreterInfos require to have llvm around. |
| 180 | + // FIXME: Currently we never call llvm::llvm_shutdown and sInterpreters leaks. |
168 | 181 | static llvm::ManagedStatic<std::deque<InterpreterInfo>> sInterpreters; |
169 | | - static bool ProcessInitialized = false; |
| 182 | + static std::once_flag ProcessInitialized; |
| 183 | + std::call_once(ProcessInitialized, []() { |
| 184 | + llvm::sys::PrintStackTraceOnErrorSignal("CppInterOp"); |
170 | 185 |
|
171 | | - if (!ProcessInitialized) { |
172 | 186 | // Initialize all targets (required for device offloading) |
173 | 187 | llvm::InitializeAllTargetInfos(); |
174 | 188 | llvm::InitializeAllTargets(); |
175 | 189 | llvm::InitializeAllTargetMCs(); |
176 | 190 | llvm::InitializeAllAsmParsers(); |
177 | 191 | llvm::InitializeAllAsmPrinters(); |
178 | | - ProcessInitialized = true; |
179 | | - } |
| 192 | + |
| 193 | + llvm::sys::AddSignalHandler(DefaultProcessCrashHandler, /*Cookie=*/nullptr); |
| 194 | + // std::atexit(llvm::llvm_shutdown); |
| 195 | + }); |
180 | 196 |
|
181 | 197 | return *sInterpreters; |
182 | 198 | } |
183 | 199 |
|
| 200 | +// Global crash handler for the entire process |
| 201 | +static void DefaultProcessCrashHandler(void*) { |
| 202 | + // Access the static deque via the getter |
| 203 | + std::deque<InterpreterInfo>& Interps = GetInterpreters(); |
| 204 | + |
| 205 | + llvm::errs() << "\n**************************************************\n"; |
| 206 | + llvm::errs() << " CppInterOp CRASH DETECTED\n"; |
| 207 | + |
| 208 | + if (!Interps.empty()) { |
| 209 | + llvm::errs() << " Active Interpreters:\n"; |
| 210 | + for (const auto& Info : Interps) { |
| 211 | + if (Info.Interpreter) |
| 212 | + llvm::errs() << " - " << Info.Interpreter << "\n"; |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + llvm::errs() << "**************************************************\n"; |
| 217 | + llvm::errs().flush(); |
| 218 | + |
| 219 | + // Print backtrace (includes JIT symbols if registered) |
| 220 | + llvm::sys::PrintStackTrace(llvm::errs()); |
| 221 | + |
| 222 | + llvm::errs() << "**************************************************\n"; |
| 223 | + llvm::errs().flush(); |
| 224 | + |
| 225 | + // The process must actually terminate for EXPECT_DEATH to pass. |
| 226 | + // We use _exit to avoid calling atexit() handlers which might be corrupted. |
| 227 | + llvm::sys::Process::Exit(/*RetCode=*/1, /*NoCleanup=*/false); |
| 228 | +} |
| 229 | + |
184 | 230 | static void RegisterInterpreter(compat::Interpreter* I, bool Owned) { |
185 | 231 | std::deque<InterpreterInfo>& Interps = GetInterpreters(); |
186 | 232 | Interps.emplace_back(I, Owned); |
|
0 commit comments