1 //===- Signals.cpp - Signal Handling support --------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines some helpful functions for dealing with the possibility of
10 // Unix signals occurring while your program is running.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/Support/Signals.h"
16 #include "DebugOptions.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Config/llvm-config.h"
20 #include "llvm/Support/CommandLine.h"
21 #include "llvm/Support/ErrorOr.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/FileUtilities.h"
24 #include "llvm/Support/Format.h"
25 #include "llvm/Support/FormatVariadic.h"
26 #include "llvm/Support/ManagedStatic.h"
27 #include "llvm/Support/MemoryBuffer.h"
28 #include "llvm/Support/Path.h"
29 #include "llvm/Support/Program.h"
30 #include "llvm/Support/StringSaver.h"
31 #include "llvm/Support/raw_ostream.h"
36 //===----------------------------------------------------------------------===//
37 //=== WARNING: Implementation here must contain only TRULY operating system
38 //=== independent code.
39 //===----------------------------------------------------------------------===//
43 // Use explicit storage to avoid accessing cl::opt in a signal handler.
44 static bool DisableSymbolicationFlag
= false;
45 static ManagedStatic
<std::string
> CrashDiagnosticsDirectory
;
47 struct CreateDisableSymbolication
{
49 return new cl::opt
<bool, true>(
50 "disable-symbolication",
51 cl::desc("Disable symbolizing crash backtraces."),
52 cl::location(DisableSymbolicationFlag
), cl::Hidden
);
55 struct CreateCrashDiagnosticsDir
{
57 return new cl::opt
<std::string
, true>(
58 "crash-diagnostics-dir", cl::value_desc("directory"),
59 cl::desc("Directory for crash diagnostic files."),
60 cl::location(*CrashDiagnosticsDirectory
), cl::Hidden
);
64 void llvm::initSignalsOptions() {
65 static ManagedStatic
<cl::opt
<bool, true>, CreateDisableSymbolication
>
67 static ManagedStatic
<cl::opt
<std::string
, true>, CreateCrashDiagnosticsDir
>
69 *DisableSymbolication
;
73 constexpr char DisableSymbolizationEnv
[] = "LLVM_DISABLE_SYMBOLIZATION";
74 constexpr char LLVMSymbolizerPathEnv
[] = "LLVM_SYMBOLIZER_PATH";
75 constexpr char EnableSymbolizerMarkupEnv
[] = "LLVM_ENABLE_SYMBOLIZER_MARKUP";
77 // Callbacks to run in signal handler must be lock-free because a signal handler
78 // could be running as we add new callbacks. We don't add unbounded numbers of
79 // callbacks, an array is therefore sufficient.
80 struct CallbackAndCookie
{
81 sys::SignalHandlerCallback Callback
;
83 enum class Status
{ Empty
, Initializing
, Initialized
, Executing
};
84 std::atomic
<Status
> Flag
;
87 static constexpr size_t MaxSignalHandlerCallbacks
= 8;
89 // A global array of CallbackAndCookie may not compile with
90 // -Werror=global-constructors in c++20 and above
91 static std::array
<CallbackAndCookie
, MaxSignalHandlerCallbacks
> &
93 static std::array
<CallbackAndCookie
, MaxSignalHandlerCallbacks
> callbacks
;
98 void sys::RunSignalHandlers() {
99 for (CallbackAndCookie
&RunMe
: CallBacksToRun()) {
100 auto Expected
= CallbackAndCookie::Status::Initialized
;
101 auto Desired
= CallbackAndCookie::Status::Executing
;
102 if (!RunMe
.Flag
.compare_exchange_strong(Expected
, Desired
))
104 (*RunMe
.Callback
)(RunMe
.Cookie
);
105 RunMe
.Callback
= nullptr;
106 RunMe
.Cookie
= nullptr;
107 RunMe
.Flag
.store(CallbackAndCookie::Status::Empty
);
112 static void insertSignalHandler(sys::SignalHandlerCallback FnPtr
,
114 for (CallbackAndCookie
&SetMe
: CallBacksToRun()) {
115 auto Expected
= CallbackAndCookie::Status::Empty
;
116 auto Desired
= CallbackAndCookie::Status::Initializing
;
117 if (!SetMe
.Flag
.compare_exchange_strong(Expected
, Desired
))
119 SetMe
.Callback
= FnPtr
;
120 SetMe
.Cookie
= Cookie
;
121 SetMe
.Flag
.store(CallbackAndCookie::Status::Initialized
);
124 report_fatal_error("too many signal callbacks already registered");
127 static bool findModulesAndOffsets(void **StackTrace
, int Depth
,
128 const char **Modules
, intptr_t *Offsets
,
129 const char *MainExecutableName
,
130 StringSaver
&StrPool
);
132 /// Format a pointer value as hexadecimal. Zero pad it out so its always the
134 static FormattedNumber
format_ptr(void *PC
) {
135 // Each byte is two hex digits plus 2 for the 0x prefix.
136 unsigned PtrWidth
= 2 + 2 * sizeof(void *);
137 return format_hex((uint64_t)PC
, PtrWidth
);
140 /// Helper that launches llvm-symbolizer and symbolizes a backtrace.
142 static bool printSymbolizedStackTrace(StringRef Argv0
, void **StackTrace
,
143 int Depth
, llvm::raw_ostream
&OS
) {
144 if (DisableSymbolicationFlag
|| getenv(DisableSymbolizationEnv
))
147 // Don't recursively invoke the llvm-symbolizer binary.
148 if (Argv0
.contains("llvm-symbolizer"))
151 // FIXME: Subtract necessary number from StackTrace entries to turn return addresses
152 // into actual instruction addresses.
153 // Use llvm-symbolizer tool to symbolize the stack traces. First look for it
154 // alongside our binary, then in $PATH.
155 ErrorOr
<std::string
> LLVMSymbolizerPathOrErr
= std::error_code();
156 if (const char *Path
= getenv(LLVMSymbolizerPathEnv
)) {
157 LLVMSymbolizerPathOrErr
= sys::findProgramByName(Path
);
158 } else if (!Argv0
.empty()) {
159 StringRef Parent
= llvm::sys::path::parent_path(Argv0
);
161 LLVMSymbolizerPathOrErr
= sys::findProgramByName("llvm-symbolizer", Parent
);
163 if (!LLVMSymbolizerPathOrErr
)
164 LLVMSymbolizerPathOrErr
= sys::findProgramByName("llvm-symbolizer");
165 if (!LLVMSymbolizerPathOrErr
)
167 const std::string
&LLVMSymbolizerPath
= *LLVMSymbolizerPathOrErr
;
169 // If we don't know argv0 or the address of main() at this point, try
170 // to guess it anyway (it's possible on some platforms).
171 std::string MainExecutableName
=
172 sys::fs::exists(Argv0
) ? (std::string
)std::string(Argv0
)
173 : sys::fs::getMainExecutable(nullptr, nullptr);
174 BumpPtrAllocator Allocator
;
175 StringSaver
StrPool(Allocator
);
176 std::vector
<const char *> Modules(Depth
, nullptr);
177 std::vector
<intptr_t> Offsets(Depth
, 0);
178 if (!findModulesAndOffsets(StackTrace
, Depth
, Modules
.data(), Offsets
.data(),
179 MainExecutableName
.c_str(), StrPool
))
182 SmallString
<32> InputFile
, OutputFile
;
183 sys::fs::createTemporaryFile("symbolizer-input", "", InputFD
, InputFile
);
184 sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile
);
185 FileRemover
InputRemover(InputFile
.c_str());
186 FileRemover
OutputRemover(OutputFile
.c_str());
189 raw_fd_ostream
Input(InputFD
, true);
190 for (int i
= 0; i
< Depth
; i
++) {
192 Input
<< Modules
[i
] << " " << (void*)Offsets
[i
] << "\n";
196 std::optional
<StringRef
> Redirects
[] = {InputFile
.str(), OutputFile
.str(),
198 StringRef Args
[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",
200 // Pass --relative-address on Windows so that we don't
201 // have to add ImageBase from PE file.
202 // FIXME: Make this the default for llvm-symbolizer.
203 "--relative-address",
207 sys::ExecuteAndWait(LLVMSymbolizerPath
, Args
, std::nullopt
, Redirects
);
211 // This report format is based on the sanitizer stack trace printer. See
212 // sanitizer_stacktrace_printer.cc in compiler-rt.
213 auto OutputBuf
= MemoryBuffer::getFile(OutputFile
.c_str());
216 StringRef Output
= OutputBuf
.get()->getBuffer();
217 SmallVector
<StringRef
, 32> Lines
;
218 Output
.split(Lines
, "\n");
219 auto CurLine
= Lines
.begin();
221 for (int i
= 0; i
< Depth
; i
++) {
222 auto PrintLineHeader
= [&]() {
223 OS
<< right_justify(formatv("#{0}", frame_no
++).str(),
224 std::log10(Depth
) + 2)
225 << ' ' << format_ptr(StackTrace
[i
]) << ' ';
232 // Read pairs of lines (function name and file/line info) until we
233 // encounter empty line.
235 if (CurLine
== Lines
.end())
237 StringRef FunctionName
= *CurLine
++;
238 if (FunctionName
.empty())
241 if (!FunctionName
.starts_with("??"))
242 OS
<< FunctionName
<< ' ';
243 if (CurLine
== Lines
.end())
245 StringRef FileLineInfo
= *CurLine
++;
246 if (!FileLineInfo
.starts_with("??"))
249 OS
<< "(" << Modules
[i
] << '+' << format_hex(Offsets
[i
], 0) << ")";
256 static bool printMarkupContext(raw_ostream
&OS
, const char *MainExecutableName
);
259 static bool printMarkupStackTrace(StringRef Argv0
, void **StackTrace
, int Depth
,
261 const char *Env
= getenv(EnableSymbolizerMarkupEnv
);
265 std::string MainExecutableName
=
266 sys::fs::exists(Argv0
) ? std::string(Argv0
)
267 : sys::fs::getMainExecutable(nullptr, nullptr);
268 if (!printMarkupContext(OS
, MainExecutableName
.c_str()))
270 for (int I
= 0; I
< Depth
; I
++)
271 OS
<< format("{{{bt:%d:%#016x}}}\n", I
, StackTrace
[I
]);
275 // Include the platform-specific parts of this class.
277 #include "Unix/Signals.inc"
280 #include "Windows/Signals.inc"