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"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Config/llvm-config.h"
18 #include "llvm/Support/ErrorOr.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/FileUtilities.h"
21 #include "llvm/Support/Format.h"
22 #include "llvm/Support/FormatVariadic.h"
23 #include "llvm/Support/FormatAdapters.h"
24 #include "llvm/Support/ManagedStatic.h"
25 #include "llvm/Support/MemoryBuffer.h"
26 #include "llvm/Support/Mutex.h"
27 #include "llvm/Support/Program.h"
28 #include "llvm/Support/StringSaver.h"
29 #include "llvm/Support/raw_ostream.h"
30 #include "llvm/Support/Options.h"
33 //===----------------------------------------------------------------------===//
34 //=== WARNING: Implementation here must contain only TRULY operating system
35 //=== independent code.
36 //===----------------------------------------------------------------------===//
40 // Use explicit storage to avoid accessing cl::opt in a signal handler.
41 static bool DisableSymbolicationFlag
= false;
42 static cl::opt
<bool, true>
43 DisableSymbolication("disable-symbolication",
44 cl::desc("Disable symbolizing crash backtraces."),
45 cl::location(DisableSymbolicationFlag
), cl::Hidden
);
47 // Callbacks to run in signal handler must be lock-free because a signal handler
48 // could be running as we add new callbacks. We don't add unbounded numbers of
49 // callbacks, an array is therefore sufficient.
50 struct CallbackAndCookie
{
51 sys::SignalHandlerCallback Callback
;
53 enum class Status
{ Empty
, Initializing
, Initialized
, Executing
};
54 std::atomic
<Status
> Flag
;
56 static constexpr size_t MaxSignalHandlerCallbacks
= 8;
57 static CallbackAndCookie CallBacksToRun
[MaxSignalHandlerCallbacks
];
60 void sys::RunSignalHandlers() {
61 for (size_t I
= 0; I
< MaxSignalHandlerCallbacks
; ++I
) {
62 auto &RunMe
= CallBacksToRun
[I
];
63 auto Expected
= CallbackAndCookie::Status::Initialized
;
64 auto Desired
= CallbackAndCookie::Status::Executing
;
65 if (!RunMe
.Flag
.compare_exchange_strong(Expected
, Desired
))
67 (*RunMe
.Callback
)(RunMe
.Cookie
);
68 RunMe
.Callback
= nullptr;
69 RunMe
.Cookie
= nullptr;
70 RunMe
.Flag
.store(CallbackAndCookie::Status::Empty
);
75 static void insertSignalHandler(sys::SignalHandlerCallback FnPtr
,
77 for (size_t I
= 0; I
< MaxSignalHandlerCallbacks
; ++I
) {
78 auto &SetMe
= CallBacksToRun
[I
];
79 auto Expected
= CallbackAndCookie::Status::Empty
;
80 auto Desired
= CallbackAndCookie::Status::Initializing
;
81 if (!SetMe
.Flag
.compare_exchange_strong(Expected
, Desired
))
83 SetMe
.Callback
= FnPtr
;
84 SetMe
.Cookie
= Cookie
;
85 SetMe
.Flag
.store(CallbackAndCookie::Status::Initialized
);
88 report_fatal_error("too many signal callbacks already registered");
91 static bool findModulesAndOffsets(void **StackTrace
, int Depth
,
92 const char **Modules
, intptr_t *Offsets
,
93 const char *MainExecutableName
,
94 StringSaver
&StrPool
);
96 /// Format a pointer value as hexadecimal. Zero pad it out so its always the
98 static FormattedNumber
format_ptr(void *PC
) {
99 // Each byte is two hex digits plus 2 for the 0x prefix.
100 unsigned PtrWidth
= 2 + 2 * sizeof(void *);
101 return format_hex((uint64_t)PC
, PtrWidth
);
104 /// Helper that launches llvm-symbolizer and symbolizes a backtrace.
106 static bool printSymbolizedStackTrace(StringRef Argv0
, void **StackTrace
,
107 int Depth
, llvm::raw_ostream
&OS
) {
108 if (DisableSymbolicationFlag
)
111 // Don't recursively invoke the llvm-symbolizer binary.
112 if (Argv0
.find("llvm-symbolizer") != std::string::npos
)
115 // FIXME: Subtract necessary number from StackTrace entries to turn return addresses
116 // into actual instruction addresses.
117 // Use llvm-symbolizer tool to symbolize the stack traces. First look for it
118 // alongside our binary, then in $PATH.
119 ErrorOr
<std::string
> LLVMSymbolizerPathOrErr
= std::error_code();
120 if (!Argv0
.empty()) {
121 StringRef Parent
= llvm::sys::path::parent_path(Argv0
);
123 LLVMSymbolizerPathOrErr
= sys::findProgramByName("llvm-symbolizer", Parent
);
125 if (!LLVMSymbolizerPathOrErr
)
126 LLVMSymbolizerPathOrErr
= sys::findProgramByName("llvm-symbolizer");
127 if (!LLVMSymbolizerPathOrErr
)
129 const std::string
&LLVMSymbolizerPath
= *LLVMSymbolizerPathOrErr
;
131 // If we don't know argv0 or the address of main() at this point, try
132 // to guess it anyway (it's possible on some platforms).
133 std::string MainExecutableName
=
134 sys::fs::exists(Argv0
) ? (std::string
)Argv0
135 : sys::fs::getMainExecutable(nullptr, nullptr);
136 BumpPtrAllocator Allocator
;
137 StringSaver
StrPool(Allocator
);
138 std::vector
<const char *> Modules(Depth
, nullptr);
139 std::vector
<intptr_t> Offsets(Depth
, 0);
140 if (!findModulesAndOffsets(StackTrace
, Depth
, Modules
.data(), Offsets
.data(),
141 MainExecutableName
.c_str(), StrPool
))
144 SmallString
<32> InputFile
, OutputFile
;
145 sys::fs::createTemporaryFile("symbolizer-input", "", InputFD
, InputFile
);
146 sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile
);
147 FileRemover
InputRemover(InputFile
.c_str());
148 FileRemover
OutputRemover(OutputFile
.c_str());
151 raw_fd_ostream
Input(InputFD
, true);
152 for (int i
= 0; i
< Depth
; i
++) {
154 Input
<< Modules
[i
] << " " << (void*)Offsets
[i
] << "\n";
158 Optional
<StringRef
> Redirects
[] = {StringRef(InputFile
),
159 StringRef(OutputFile
), StringRef("")};
160 StringRef Args
[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",
162 // Pass --relative-address on Windows so that we don't
163 // have to add ImageBase from PE file.
164 // FIXME: Make this the default for llvm-symbolizer.
165 "--relative-address",
169 sys::ExecuteAndWait(LLVMSymbolizerPath
, Args
, None
, Redirects
);
173 // This report format is based on the sanitizer stack trace printer. See
174 // sanitizer_stacktrace_printer.cc in compiler-rt.
175 auto OutputBuf
= MemoryBuffer::getFile(OutputFile
.c_str());
178 StringRef Output
= OutputBuf
.get()->getBuffer();
179 SmallVector
<StringRef
, 32> Lines
;
180 Output
.split(Lines
, "\n");
181 auto CurLine
= Lines
.begin();
183 for (int i
= 0; i
< Depth
; i
++) {
184 auto PrintLineHeader
= [&]() {
185 OS
<< right_justify(formatv("#{0}", frame_no
++).str(),
186 std::log10(Depth
) + 2)
187 << ' ' << format_ptr(StackTrace
[i
]) << ' ';
194 // Read pairs of lines (function name and file/line info) until we
195 // encounter empty line.
197 if (CurLine
== Lines
.end())
199 StringRef FunctionName
= *CurLine
++;
200 if (FunctionName
.empty())
203 if (!FunctionName
.startswith("??"))
204 OS
<< FunctionName
<< ' ';
205 if (CurLine
== Lines
.end())
207 StringRef FileLineInfo
= *CurLine
++;
208 if (!FileLineInfo
.startswith("??"))
211 OS
<< "(" << Modules
[i
] << '+' << format_hex(Offsets
[i
], 0) << ")";
218 // Include the platform-specific parts of this class.
220 #include "Unix/Signals.inc"
223 #include "Windows/Signals.inc"