forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[lldb] Add frame recognizer for __builtin_verbose_trap (llvm#80368)
Summary: This patch adds a frame recognizer for Clang's `__builtin_verbose_trap`, which behaves like a `__builtin_trap`, but emits a failure-reason string into debug-info in order for debuggers to display it to a user. The frame recognizer triggers when we encounter a frame with a function name that begins with `__clang_trap_msg`, which is the magic prefix Clang emits into debug-info for verbose traps. Once such frame is encountered we display the frame function name as the `Stop Reason` and display that frame to the user. Example output: ``` (lldb) run warning: a.out was compiled with optimization - stepping may behave oddly; variables may not be available. Process 35942 launched: 'a.out' (arm64) Process 35942 stopped * thread llvm#1, queue = 'com.apple.main-thread', stop reason = Misc.: Function is not implemented frame llvm#1: 0x0000000100003fa4 a.out`main [inlined] Dummy::func(this=<unavailable>) at verbose_trap.cpp:3:5 [opt] 1 struct Dummy { 2 void func() { -> 3 __builtin_verbose_trap("Misc.", "Function is not implemented"); 4 } 5 }; 6 7 int main() { (lldb) bt * thread llvm#1, queue = 'com.apple.main-thread', stop reason = Misc.: Function is not implemented frame #0: 0x0000000100003fa4 a.out`main [inlined] __clang_trap_msg$Misc.$Function is not implemented$ at verbose_trap.cpp:0 [opt] * frame llvm#1: 0x0000000100003fa4 a.out`main [inlined] Dummy::func(this=<unavailable>) at verbose_trap.cpp:3:5 [opt] frame llvm#2: 0x0000000100003fa4 a.out`main at verbose_trap.cpp:8:13 [opt] frame llvm#3: 0x0000000189d518b4 dyld`start + 1988 ``` Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D59822390
- Loading branch information
1 parent
9af34fc
commit d90c3f7
Showing
6 changed files
with
201 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#ifndef LLDB_TARGET_VERBOSETRAPFRAMERECOGNIZER_H | ||
#define LLDB_TARGET_VERBOSETRAPFRAMERECOGNIZER_H | ||
|
||
#include "lldb/Target/StackFrameRecognizer.h" | ||
|
||
namespace lldb_private { | ||
|
||
void RegisterVerboseTrapFrameRecognizer(Process &process); | ||
|
||
/// Holds the stack frame that caused the Verbose trap and the inlined stop | ||
/// reason message. | ||
class VerboseTrapRecognizedStackFrame : public RecognizedStackFrame { | ||
public: | ||
VerboseTrapRecognizedStackFrame(lldb::StackFrameSP most_relevant_frame_sp, | ||
std::string stop_desc); | ||
|
||
lldb::StackFrameSP GetMostRelevantFrame() override; | ||
|
||
private: | ||
lldb::StackFrameSP m_most_relevant_frame; | ||
}; | ||
|
||
/// When a thread stops, it checks the current frame contains a | ||
/// Verbose Trap diagnostic. If so, it returns a \a | ||
/// VerboseTrapRecognizedStackFrame holding the diagnostic a stop reason | ||
/// description with and the parent frame as the most relavant frame. | ||
class VerboseTrapFrameRecognizer : public StackFrameRecognizer { | ||
public: | ||
std::string GetName() override { | ||
return "Verbose Trap StackFrame Recognizer"; | ||
} | ||
|
||
lldb::RecognizedStackFrameSP | ||
RecognizeFrame(lldb::StackFrameSP frame) override; | ||
}; | ||
|
||
} // namespace lldb_private | ||
|
||
#endif // LLDB_TARGET_VERBOSETRAPFRAMERECOGNIZER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#include "lldb/Target/VerboseTrapFrameRecognizer.h" | ||
|
||
#include "lldb/Core/Module.h" | ||
#include "lldb/Symbol/Function.h" | ||
#include "lldb/Symbol/SymbolContext.h" | ||
#include "lldb/Target/Process.h" | ||
#include "lldb/Target/StackFrameRecognizer.h" | ||
#include "lldb/Target/Target.h" | ||
|
||
#include "lldb/Utility/LLDBLog.h" | ||
#include "lldb/Utility/Log.h" | ||
|
||
#include "clang/CodeGen/ModuleBuilder.h" | ||
|
||
using namespace llvm; | ||
using namespace lldb; | ||
using namespace lldb_private; | ||
|
||
VerboseTrapRecognizedStackFrame::VerboseTrapRecognizedStackFrame( | ||
StackFrameSP most_relevant_frame_sp, std::string stop_desc) | ||
: m_most_relevant_frame(most_relevant_frame_sp) { | ||
m_stop_desc = std::move(stop_desc); | ||
} | ||
|
||
lldb::RecognizedStackFrameSP | ||
VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) { | ||
if (frame_sp->GetFrameIndex()) | ||
return {}; | ||
|
||
ThreadSP thread_sp = frame_sp->GetThread(); | ||
ProcessSP process_sp = thread_sp->GetProcess(); | ||
|
||
StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(1); | ||
|
||
if (!most_relevant_frame_sp) { | ||
Log *log = GetLog(LLDBLog::Unwind); | ||
LLDB_LOG( | ||
log, | ||
"Failed to find most relevant frame: Hit unwinding bound (1 frame)!"); | ||
return {}; | ||
} | ||
|
||
SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextEverything); | ||
|
||
if (!sc.block) | ||
return {}; | ||
|
||
// The runtime error is set as the function name in the inlined function info | ||
// of frame #0 by the compiler | ||
const InlineFunctionInfo *inline_info = nullptr; | ||
Block *inline_block = sc.block->GetContainingInlinedBlock(); | ||
|
||
if (!inline_block) | ||
return {}; | ||
|
||
inline_info = sc.block->GetInlinedFunctionInfo(); | ||
|
||
if (!inline_info) | ||
return {}; | ||
|
||
auto func_name = inline_info->GetName().GetStringRef(); | ||
if (func_name.empty()) | ||
return {}; | ||
|
||
static auto trap_regex = | ||
llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str()); | ||
SmallVector<llvm::StringRef, 3> matches; | ||
std::string regex_err_msg; | ||
if (!trap_regex.match(func_name, &matches, ®ex_err_msg)) { | ||
LLDB_LOGF(GetLog(LLDBLog::Unwind), | ||
"Failed to parse match trap regex for '%s': %s", func_name.data(), | ||
regex_err_msg.c_str()); | ||
|
||
return {}; | ||
} | ||
|
||
// For `__clang_trap_msg$category$message$` we expect 3 matches: | ||
// 1. entire string | ||
// 2. category | ||
// 3. message | ||
if (matches.size() != 3) { | ||
LLDB_LOGF(GetLog(LLDBLog::Unwind), | ||
"Unexpected function name format. Expected '<trap prefix>$<trap " | ||
"category>$<trap message>'$ but got: '%s'.", | ||
func_name.data()); | ||
|
||
return {}; | ||
} | ||
|
||
auto category = matches[1]; | ||
auto message = matches[2]; | ||
|
||
std::string stop_reason = | ||
category.empty() ? "<empty category>" : category.str(); | ||
if (!message.empty()) { | ||
stop_reason += ": "; | ||
stop_reason += message.str(); | ||
} | ||
|
||
return std::make_shared<VerboseTrapRecognizedStackFrame>( | ||
most_relevant_frame_sp, std::move(stop_reason)); | ||
} | ||
|
||
lldb::StackFrameSP VerboseTrapRecognizedStackFrame::GetMostRelevantFrame() { | ||
return m_most_relevant_frame; | ||
} | ||
|
||
namespace lldb_private { | ||
|
||
void RegisterVerboseTrapFrameRecognizer(Process &process) { | ||
RegularExpressionSP module_regex_sp = nullptr; | ||
auto symbol_regex_sp = std::make_shared<RegularExpression>( | ||
llvm::formatv("^{0}", ClangTrapPrefix).str()); | ||
|
||
StackFrameRecognizerSP srf_recognizer_sp = | ||
std::make_shared<VerboseTrapFrameRecognizer>(); | ||
|
||
process.GetTarget().GetFrameRecognizerManager().AddRecognizer( | ||
srf_recognizer_sp, module_regex_sp, symbol_regex_sp, false); | ||
} | ||
|
||
} // namespace lldb_private |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#if !defined(VERBOSE_TRAP_TEST_CATEGORY) || !defined(VERBOSE_TRAP_TEST_MESSAGE) | ||
#error Please define required macros | ||
#endif | ||
|
||
struct Dummy { | ||
void func() { __builtin_verbose_trap(VERBOSE_TRAP_TEST_CATEGORY, VERBOSE_TRAP_TEST_MESSAGE); } | ||
}; | ||
|
||
int main() { | ||
Dummy{}.func(); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"Foo\" -DVERBOSE_TRAP_TEST_MESSAGE=\"Bar\" | ||
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-BOTH | ||
# | ||
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"\" -DVERBOSE_TRAP_TEST_MESSAGE=\"Bar\" | ||
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-MESSAGE_ONLY | ||
# | ||
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"Foo\" -DVERBOSE_TRAP_TEST_MESSAGE=\"\" | ||
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-CATEGORY_ONLY | ||
# | ||
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"\" -DVERBOSE_TRAP_TEST_MESSAGE=\"\" | ||
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-NONE | ||
|
||
run | ||
# CHECK-BOTH: thread #{{.*}}stop reason = Foo: Bar | ||
# CHECK-MESSAGE_ONLY: thread #{{.*}}stop reason = <empty category>: Bar | ||
# CHECK-CATEGORY_ONLY: thread #{{.*}}stop reason = Foo | ||
# CHECK-NONE: thread #{{.*}}stop reason = <empty category> | ||
frame info | ||
# CHECK: frame #{{.*}}`Dummy::func(this={{.*}}) at verbose_trap.cpp | ||
frame recognizer info 0 | ||
# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer | ||
q |