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/STLExtras.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Config/llvm-config.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/ErrorOr.h"
23 #include "llvm/Support/FileSystem.h"
24 #include "llvm/Support/FileUtilities.h"
25 #include "llvm/Support/Format.h"
26 #include "llvm/Support/FormatAdapters.h"
27 #include "llvm/Support/FormatVariadic.h"
28 #include "llvm/Support/ManagedStatic.h"
29 #include "llvm/Support/MemoryBuffer.h"
30 #include "llvm/Support/Mutex.h"
31 #include "llvm/Support/Program.h"
32 #include "llvm/Support/StringSaver.h"
33 #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";
76 // Callbacks to run in signal handler must be lock-free because a signal handler
77 // could be running as we add new callbacks. We don't add unbounded numbers of
78 // callbacks, an array is therefore sufficient.
79 struct CallbackAndCookie
{
80 sys::SignalHandlerCallback Callback
;
82 enum class Status
{ Empty
, Initializing
, Initialized
, Executing
};
83 std::atomic
<Status
> Flag
;
85 static constexpr size_t MaxSignalHandlerCallbacks
= 8;
86 static CallbackAndCookie CallBacksToRun
[MaxSignalHandlerCallbacks
];
89 void sys::RunSignalHandlers() {
90 for (size_t I
= 0; I
< MaxSignalHandlerCallbacks
; ++I
) {
91 auto &RunMe
= CallBacksToRun
[I
];
92 auto Expected
= CallbackAndCookie::Status::Initialized
;
93 auto Desired
= CallbackAndCookie::Status::Executing
;
94 if (!RunMe
.Flag
.compare_exchange_strong(Expected
, Desired
))
96 (*RunMe
.Callback
)(RunMe
.Cookie
);
97 RunMe
.Callback
= nullptr;
98 RunMe
.Cookie
= nullptr;
99 RunMe
.Flag
.store(CallbackAndCookie::Status::Empty
);
104 static void insertSignalHandler(sys::SignalHandlerCallback FnPtr
,
106 for (size_t I
= 0; I
< MaxSignalHandlerCallbacks
; ++I
) {
107 auto &SetMe
= CallBacksToRun
[I
];
108 auto Expected
= CallbackAndCookie::Status::Empty
;
109 auto Desired
= CallbackAndCookie::Status::Initializing
;
110 if (!SetMe
.Flag
.compare_exchange_strong(Expected
, Desired
))
112 SetMe
.Callback
= FnPtr
;
113 SetMe
.Cookie
= Cookie
;
114 SetMe
.Flag
.store(CallbackAndCookie::Status::Initialized
);
117 report_fatal_error("too many signal callbacks already registered");
120 static bool findModulesAndOffsets(void **StackTrace
, int Depth
,
121 const char **Modules
, intptr_t *Offsets
,
122 const char *MainExecutableName
,
123 StringSaver
&StrPool
);
125 /// Format a pointer value as hexadecimal. Zero pad it out so its always the
127 static FormattedNumber
format_ptr(void *PC
) {
128 // Each byte is two hex digits plus 2 for the 0x prefix.
129 unsigned PtrWidth
= 2 + 2 * sizeof(void *);
130 return format_hex((uint64_t)PC
, PtrWidth
);
133 /// Helper that launches llvm-symbolizer and symbolizes a backtrace.
135 static bool printSymbolizedStackTrace(StringRef Argv0
, void **StackTrace
,
136 int Depth
, llvm::raw_ostream
&OS
) {
137 if (DisableSymbolicationFlag
|| getenv(DisableSymbolizationEnv
))
140 // Don't recursively invoke the llvm-symbolizer binary.
141 if (Argv0
.find("llvm-symbolizer") != std::string::npos
)
144 // FIXME: Subtract necessary number from StackTrace entries to turn return addresses
145 // into actual instruction addresses.
146 // Use llvm-symbolizer tool to symbolize the stack traces. First look for it
147 // alongside our binary, then in $PATH.
148 ErrorOr
<std::string
> LLVMSymbolizerPathOrErr
= std::error_code();
149 if (const char *Path
= getenv(LLVMSymbolizerPathEnv
)) {
150 LLVMSymbolizerPathOrErr
= sys::findProgramByName(Path
);
151 } else if (!Argv0
.empty()) {
152 StringRef Parent
= llvm::sys::path::parent_path(Argv0
);
154 LLVMSymbolizerPathOrErr
= sys::findProgramByName("llvm-symbolizer", Parent
);
156 if (!LLVMSymbolizerPathOrErr
)
157 LLVMSymbolizerPathOrErr
= sys::findProgramByName("llvm-symbolizer");
158 if (!LLVMSymbolizerPathOrErr
)
160 const std::string
&LLVMSymbolizerPath
= *LLVMSymbolizerPathOrErr
;
162 // If we don't know argv0 or the address of main() at this point, try
163 // to guess it anyway (it's possible on some platforms).
164 std::string MainExecutableName
=
165 sys::fs::exists(Argv0
) ? (std::string
)std::string(Argv0
)
166 : sys::fs::getMainExecutable(nullptr, nullptr);
167 BumpPtrAllocator Allocator
;
168 StringSaver
StrPool(Allocator
);
169 std::vector
<const char *> Modules(Depth
, nullptr);
170 std::vector
<intptr_t> Offsets(Depth
, 0);
171 if (!findModulesAndOffsets(StackTrace
, Depth
, Modules
.data(), Offsets
.data(),
172 MainExecutableName
.c_str(), StrPool
))
175 SmallString
<32> InputFile
, OutputFile
;
176 sys::fs::createTemporaryFile("symbolizer-input", "", InputFD
, InputFile
);
177 sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile
);
178 FileRemover
InputRemover(InputFile
.c_str());
179 FileRemover
OutputRemover(OutputFile
.c_str());
182 raw_fd_ostream
Input(InputFD
, true);
183 for (int i
= 0; i
< Depth
; i
++) {
185 Input
<< Modules
[i
] << " " << (void*)Offsets
[i
] << "\n";
189 Optional
<StringRef
> Redirects
[] = {InputFile
.str(), OutputFile
.str(),
191 StringRef Args
[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",
193 // Pass --relative-address on Windows so that we don't
194 // have to add ImageBase from PE file.
195 // FIXME: Make this the default for llvm-symbolizer.
196 "--relative-address",
200 sys::ExecuteAndWait(LLVMSymbolizerPath
, Args
, None
, Redirects
);
204 // This report format is based on the sanitizer stack trace printer. See
205 // sanitizer_stacktrace_printer.cc in compiler-rt.
206 auto OutputBuf
= MemoryBuffer::getFile(OutputFile
.c_str());
209 StringRef Output
= OutputBuf
.get()->getBuffer();
210 SmallVector
<StringRef
, 32> Lines
;
211 Output
.split(Lines
, "\n");
212 auto CurLine
= Lines
.begin();
214 for (int i
= 0; i
< Depth
; i
++) {
215 auto PrintLineHeader
= [&]() {
216 OS
<< right_justify(formatv("#{0}", frame_no
++).str(),
217 std::log10(Depth
) + 2)
218 << ' ' << format_ptr(StackTrace
[i
]) << ' ';
225 // Read pairs of lines (function name and file/line info) until we
226 // encounter empty line.
228 if (CurLine
== Lines
.end())
230 StringRef FunctionName
= *CurLine
++;
231 if (FunctionName
.empty())
234 if (!FunctionName
.startswith("??"))
235 OS
<< FunctionName
<< ' ';
236 if (CurLine
== Lines
.end())
238 StringRef FileLineInfo
= *CurLine
++;
239 if (!FileLineInfo
.startswith("??"))
242 OS
<< "(" << Modules
[i
] << '+' << format_hex(Offsets
[i
], 0) << ")";
249 // Include the platform-specific parts of this class.
251 #include "Unix/Signals.inc"
254 #include "Windows/Signals.inc"