Skip to content

Commit 12f5a10

Browse files
committed
[DLCov] Origin-Tracking: SymbolizeAddresses
1 parent f631e37 commit 12f5a10

File tree

4 files changed

+176
-0
lines changed

4 files changed

+176
-0
lines changed

llvm/include/llvm/Support/Signals.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,32 @@
1414
#ifndef LLVM_SUPPORT_SIGNALS_H
1515
#define LLVM_SUPPORT_SIGNALS_H
1616

17+
#include "llvm/Config/llvm-config.h"
1718
#include "llvm/Support/Compiler.h"
19+
#include <array>
1820
#include <cstdint>
1921
#include <string>
2022

2123
namespace llvm {
2224
class StringRef;
2325
class raw_ostream;
2426

27+
#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING
28+
// Typedefs that are convenient but only used by the stack-trace-collection code
29+
// added if DebugLoc origin-tracking is enabled.
30+
template <typename T, typename Enable> struct DenseMapInfo;
31+
template <typename ValueT, typename ValueInfoT> class DenseSet;
32+
namespace detail {
33+
template <typename KeyT, typename ValueT> struct DenseMapPair;
34+
}
35+
template <typename KeyT, typename ValueT, typename KeyInfoT, typename BucketT>
36+
class DenseMap;
37+
using AddressSet = DenseSet<void *, DenseMapInfo<void *, void>>;
38+
using SymbolizedAddressMap =
39+
DenseMap<void *, std::string, DenseMapInfo<void *, void>,
40+
detail::DenseMapPair<void *, std::string>>;
41+
#endif
42+
2543
namespace sys {
2644

2745
/// This function runs all the registered interrupt handlers, including the
@@ -57,6 +75,28 @@ LLVM_ABI void DisableSystemDialogsOnCrash();
5775
/// specified, the entire frame is printed.
5876
LLVM_ABI void PrintStackTrace(raw_ostream &OS, int Depth = 0);
5977

78+
#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING
79+
#ifdef NDEBUG
80+
#error DebugLoc origin-tracking should not be enabled in Release builds.
81+
#endif
82+
/// Populates the given array with a stack trace of the current program, up to
83+
/// MaxDepth frames. Returns the number of frames returned, which will be
84+
/// inserted into \p StackTrace from index 0. All entries after the returned
85+
/// depth will be unmodified. NB: This is only intended to be used for
86+
/// introspection of LLVM by Debugify, will not be enabled in release builds,
87+
/// and should not be relied on for other purposes.
88+
template <unsigned long MaxDepth>
89+
int getStackTrace(std::array<void *, MaxDepth> &StackTrace);
90+
91+
/// Takes a set of \p Addresses, symbolizes them and stores the result in the
92+
/// provided \p SymbolizedAddresses map.
93+
/// NB: This is only intended to be used for introspection of LLVM by
94+
/// Debugify, will not be enabled in release builds, and should not be relied
95+
/// on for other purposes.
96+
void symbolizeAddresses(AddressSet &Addresses,
97+
SymbolizedAddressMap &SymbolizedAddresses);
98+
#endif
99+
60100
// Run all registered signal handlers.
61101
LLVM_ABI void RunSignalHandlers();
62102

llvm/lib/Support/Signals.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,122 @@ static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace,
253253
return true;
254254
}
255255

256+
#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING
257+
void sys::symbolizeAddresses(AddressSet &Addresses,
258+
SymbolizedAddressMap &SymbolizedAddresses) {
259+
assert(!DisableSymbolicationFlag && !getenv(DisableSymbolizationEnv) &&
260+
"Debugify origin stacktraces require symbolization to be enabled.");
261+
262+
// Convert Set of Addresses to ordered list.
263+
SmallVector<void *, 0> AddressList(Addresses.begin(), Addresses.end());
264+
if (AddressList.empty())
265+
return;
266+
int NumAddresses = AddressList.size();
267+
llvm::sort(AddressList);
268+
269+
// Use llvm-symbolizer tool to symbolize the stack traces. First look for it
270+
// alongside our binary, then in $PATH.
271+
ErrorOr<std::string> LLVMSymbolizerPathOrErr = std::error_code();
272+
if (const char *Path = getenv(LLVMSymbolizerPathEnv)) {
273+
LLVMSymbolizerPathOrErr = sys::findProgramByName(Path);
274+
}
275+
if (!LLVMSymbolizerPathOrErr)
276+
LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer");
277+
assert(!!LLVMSymbolizerPathOrErr &&
278+
"Debugify origin stacktraces require llvm-symbolizer.");
279+
const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr;
280+
281+
// Try to guess the main executable name, since we don't have argv0 available
282+
// here.
283+
std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr);
284+
285+
BumpPtrAllocator Allocator;
286+
StringSaver StrPool(Allocator);
287+
std::vector<const char *> Modules(NumAddresses, nullptr);
288+
std::vector<intptr_t> Offsets(NumAddresses, 0);
289+
if (!findModulesAndOffsets(AddressList.data(), NumAddresses, Modules.data(),
290+
Offsets.data(), MainExecutableName.c_str(),
291+
StrPool))
292+
return;
293+
int InputFD;
294+
SmallString<32> InputFile, OutputFile;
295+
sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile);
296+
sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile);
297+
FileRemover InputRemover(InputFile.c_str());
298+
FileRemover OutputRemover(OutputFile.c_str());
299+
300+
{
301+
raw_fd_ostream Input(InputFD, true);
302+
for (int i = 0; i < NumAddresses; i++) {
303+
if (Modules[i])
304+
Input << Modules[i] << " " << (void *)Offsets[i] << "\n";
305+
}
306+
}
307+
308+
std::optional<StringRef> Redirects[] = {InputFile.str(), OutputFile.str(),
309+
StringRef("")};
310+
StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",
311+
#ifdef _WIN32
312+
// Pass --relative-address on Windows so that we don't
313+
// have to add ImageBase from PE file.
314+
// FIXME: Make this the default for llvm-symbolizer.
315+
"--relative-address",
316+
#endif
317+
"--demangle"};
318+
int RunResult =
319+
sys::ExecuteAndWait(LLVMSymbolizerPath, Args, std::nullopt, Redirects);
320+
if (RunResult != 0)
321+
return;
322+
323+
// This report format is based on the sanitizer stack trace printer. See
324+
// sanitizer_stacktrace_printer.cc in compiler-rt.
325+
auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str());
326+
if (!OutputBuf)
327+
return;
328+
StringRef Output = OutputBuf.get()->getBuffer();
329+
SmallVector<StringRef, 32> Lines;
330+
Output.split(Lines, "\n");
331+
auto CurLine = Lines.begin();
332+
for (int i = 0; i < NumAddresses; i++) {
333+
assert(!SymbolizedAddresses.contains(AddressList[i]));
334+
std::string &SymbolizedAddr = SymbolizedAddresses[AddressList[i]];
335+
raw_string_ostream OS(SymbolizedAddr);
336+
if (!Modules[i]) {
337+
OS << format_ptr(AddressList[i]) << '\n';
338+
continue;
339+
}
340+
// Read pairs of lines (function name and file/line info) until we
341+
// encounter empty line.
342+
for (bool IsFirst = true;; IsFirst = false) {
343+
if (CurLine == Lines.end())
344+
return;
345+
StringRef FunctionName = *CurLine++;
346+
if (FunctionName.empty())
347+
break;
348+
// Add indentation for lines after the first; we use 3 spaces, because
349+
// currently that aligns with the expected indentation that will be added
350+
// to the first line by Debugify.
351+
if (!IsFirst)
352+
OS << " ";
353+
OS << format_ptr(AddressList[i]) << ' ';
354+
if (!FunctionName.starts_with("??"))
355+
OS << FunctionName << ' ';
356+
if (CurLine == Lines.end()) {
357+
OS << '\n';
358+
return;
359+
}
360+
StringRef FileLineInfo = *CurLine++;
361+
if (!FileLineInfo.starts_with("??"))
362+
OS << FileLineInfo;
363+
else
364+
OS << "(" << Modules[i] << '+' << format_hex(Offsets[i], 0) << ")";
365+
OS << '\n';
366+
}
367+
}
368+
return;
369+
}
370+
#endif
371+
256372
static bool printMarkupContext(raw_ostream &OS, const char *MainExecutableName);
257373

258374
LLVM_ATTRIBUTE_USED

llvm/lib/Support/Unix/Signals.inc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,21 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
507507
return 0;
508508
}
509509

510+
#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING
511+
#if !defined(HAVE_BACKTRACE)
512+
#error DebugLoc origin-tracking currently requires `backtrace()`.
513+
#endif
514+
namespace llvm {
515+
namespace sys {
516+
template <unsigned long MaxDepth>
517+
int getStackTrace(std::array<void *, MaxDepth> &StackTrace) {
518+
return backtrace(StackTrace.data(), MaxDepth);
519+
}
520+
template int getStackTrace<16ul>(std::array<void *, 16ul> &);
521+
} // namespace sys
522+
} // namespace llvm
523+
#endif
524+
510525
/// If this is an ELF platform, we can find all loaded modules and their virtual
511526
/// addresses with dl_iterate_phdr.
512527
static bool findModulesAndOffsets(void **StackTrace, int Depth,

llvm/lib/Support/Windows/Signals.inc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// This file provides the Win32 specific implementation of the Signals class.
1010
//
1111
//===----------------------------------------------------------------------===//
12+
#include "llvm/Config/llvm-config.h"
1213
#include "llvm/Support/ConvertUTF.h"
1314
#include "llvm/Support/ExitCodes.h"
1415
#include "llvm/Support/FileSystem.h"
@@ -542,6 +543,10 @@ void sys::PrintStackTraceOnErrorSignal(StringRef Argv0,
542543
extern "C" VOID WINAPI RtlCaptureContext(PCONTEXT ContextRecord);
543544
#endif
544545

546+
#if LLVM_ENABLE_DEBUGLOC_ORIGIN_TRACKING
547+
#error DebugLoc origin-tracking currently unimplemented for Windows.
548+
#endif
549+
545550
static void LocalPrintStackTrace(raw_ostream &OS, PCONTEXT C) {
546551
STACKFRAME64 StackFrame{};
547552
CONTEXT Context{};

0 commit comments

Comments
 (0)