Skip to content

Commit 8a27ef6

Browse files
authored
[lldb] Add frame recognizer for __builtin_verbose_trap (llvm#80368)
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 rust-lang#1, queue = 'com.apple.main-thread', stop reason = Misc.: Function is not implemented frame rust-lang#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 rust-lang#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 rust-lang#1: 0x0000000100003fa4 a.out`main [inlined] Dummy::func(this=<unavailable>) at verbose_trap.cpp:3:5 [opt] frame rust-lang#2: 0x0000000100003fa4 a.out`main at verbose_trap.cpp:8:13 [opt] frame rust-lang#3: 0x0000000189d518b4 dyld`start + 1988 ```
1 parent 94ed08d commit 8a27ef6

File tree

6 files changed

+201
-0
lines changed

6 files changed

+201
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#ifndef LLDB_TARGET_VERBOSETRAPFRAMERECOGNIZER_H
2+
#define LLDB_TARGET_VERBOSETRAPFRAMERECOGNIZER_H
3+
4+
#include "lldb/Target/StackFrameRecognizer.h"
5+
6+
namespace lldb_private {
7+
8+
void RegisterVerboseTrapFrameRecognizer(Process &process);
9+
10+
/// Holds the stack frame that caused the Verbose trap and the inlined stop
11+
/// reason message.
12+
class VerboseTrapRecognizedStackFrame : public RecognizedStackFrame {
13+
public:
14+
VerboseTrapRecognizedStackFrame(lldb::StackFrameSP most_relevant_frame_sp,
15+
std::string stop_desc);
16+
17+
lldb::StackFrameSP GetMostRelevantFrame() override;
18+
19+
private:
20+
lldb::StackFrameSP m_most_relevant_frame;
21+
};
22+
23+
/// When a thread stops, it checks the current frame contains a
24+
/// Verbose Trap diagnostic. If so, it returns a \a
25+
/// VerboseTrapRecognizedStackFrame holding the diagnostic a stop reason
26+
/// description with and the parent frame as the most relavant frame.
27+
class VerboseTrapFrameRecognizer : public StackFrameRecognizer {
28+
public:
29+
std::string GetName() override {
30+
return "Verbose Trap StackFrame Recognizer";
31+
}
32+
33+
lldb::RecognizedStackFrameSP
34+
RecognizeFrame(lldb::StackFrameSP frame) override;
35+
};
36+
37+
} // namespace lldb_private
38+
39+
#endif // LLDB_TARGET_VERBOSETRAPFRAMERECOGNIZER_H

lldb/source/Target/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ add_lldb_library(lldbTarget
7878
UnixSignals.cpp
7979
UnwindAssembly.cpp
8080
UnwindLLDB.cpp
81+
VerboseTrapFrameRecognizer.cpp
8182

8283
LINK_LIBS
8384
lldbBreakpoint

lldb/source/Target/Process.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include "lldb/Target/ThreadPlanCallFunction.h"
6464
#include "lldb/Target/ThreadPlanStack.h"
6565
#include "lldb/Target/UnixSignals.h"
66+
#include "lldb/Target/VerboseTrapFrameRecognizer.h"
6667
#include "lldb/Utility/AddressableBits.h"
6768
#include "lldb/Utility/Event.h"
6869
#include "lldb/Utility/LLDBLog.h"
@@ -522,7 +523,11 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
522523
if (!value_sp->OptionWasSet() && platform_cache_line_size != 0)
523524
value_sp->SetValueAs(platform_cache_line_size);
524525

526+
// FIXME: Frame recognizer registration should not be done in Target.
527+
// We should have a plugin do the registration instead, for example, a
528+
// common C LanguageRuntime plugin.
525529
RegisterAssertFrameRecognizer(this);
530+
RegisterVerboseTrapFrameRecognizer(*this);
526531
}
527532

528533
Process::~Process() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include "lldb/Target/VerboseTrapFrameRecognizer.h"
2+
3+
#include "lldb/Core/Module.h"
4+
#include "lldb/Symbol/Function.h"
5+
#include "lldb/Symbol/SymbolContext.h"
6+
#include "lldb/Target/Process.h"
7+
#include "lldb/Target/StackFrameRecognizer.h"
8+
#include "lldb/Target/Target.h"
9+
10+
#include "lldb/Utility/LLDBLog.h"
11+
#include "lldb/Utility/Log.h"
12+
13+
#include "clang/CodeGen/ModuleBuilder.h"
14+
15+
using namespace llvm;
16+
using namespace lldb;
17+
using namespace lldb_private;
18+
19+
VerboseTrapRecognizedStackFrame::VerboseTrapRecognizedStackFrame(
20+
StackFrameSP most_relevant_frame_sp, std::string stop_desc)
21+
: m_most_relevant_frame(most_relevant_frame_sp) {
22+
m_stop_desc = std::move(stop_desc);
23+
}
24+
25+
lldb::RecognizedStackFrameSP
26+
VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
27+
if (frame_sp->GetFrameIndex())
28+
return {};
29+
30+
ThreadSP thread_sp = frame_sp->GetThread();
31+
ProcessSP process_sp = thread_sp->GetProcess();
32+
33+
StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(1);
34+
35+
if (!most_relevant_frame_sp) {
36+
Log *log = GetLog(LLDBLog::Unwind);
37+
LLDB_LOG(
38+
log,
39+
"Failed to find most relevant frame: Hit unwinding bound (1 frame)!");
40+
return {};
41+
}
42+
43+
SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
44+
45+
if (!sc.block)
46+
return {};
47+
48+
// The runtime error is set as the function name in the inlined function info
49+
// of frame #0 by the compiler
50+
const InlineFunctionInfo *inline_info = nullptr;
51+
Block *inline_block = sc.block->GetContainingInlinedBlock();
52+
53+
if (!inline_block)
54+
return {};
55+
56+
inline_info = sc.block->GetInlinedFunctionInfo();
57+
58+
if (!inline_info)
59+
return {};
60+
61+
auto func_name = inline_info->GetName().GetStringRef();
62+
if (func_name.empty())
63+
return {};
64+
65+
static auto trap_regex =
66+
llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str());
67+
SmallVector<llvm::StringRef, 3> matches;
68+
std::string regex_err_msg;
69+
if (!trap_regex.match(func_name, &matches, &regex_err_msg)) {
70+
LLDB_LOGF(GetLog(LLDBLog::Unwind),
71+
"Failed to parse match trap regex for '%s': %s", func_name.data(),
72+
regex_err_msg.c_str());
73+
74+
return {};
75+
}
76+
77+
// For `__clang_trap_msg$category$message$` we expect 3 matches:
78+
// 1. entire string
79+
// 2. category
80+
// 3. message
81+
if (matches.size() != 3) {
82+
LLDB_LOGF(GetLog(LLDBLog::Unwind),
83+
"Unexpected function name format. Expected '<trap prefix>$<trap "
84+
"category>$<trap message>'$ but got: '%s'.",
85+
func_name.data());
86+
87+
return {};
88+
}
89+
90+
auto category = matches[1];
91+
auto message = matches[2];
92+
93+
std::string stop_reason =
94+
category.empty() ? "<empty category>" : category.str();
95+
if (!message.empty()) {
96+
stop_reason += ": ";
97+
stop_reason += message.str();
98+
}
99+
100+
return std::make_shared<VerboseTrapRecognizedStackFrame>(
101+
most_relevant_frame_sp, std::move(stop_reason));
102+
}
103+
104+
lldb::StackFrameSP VerboseTrapRecognizedStackFrame::GetMostRelevantFrame() {
105+
return m_most_relevant_frame;
106+
}
107+
108+
namespace lldb_private {
109+
110+
void RegisterVerboseTrapFrameRecognizer(Process &process) {
111+
RegularExpressionSP module_regex_sp = nullptr;
112+
auto symbol_regex_sp = std::make_shared<RegularExpression>(
113+
llvm::formatv("^{0}", ClangTrapPrefix).str());
114+
115+
StackFrameRecognizerSP srf_recognizer_sp =
116+
std::make_shared<VerboseTrapFrameRecognizer>();
117+
118+
process.GetTarget().GetFrameRecognizerManager().AddRecognizer(
119+
srf_recognizer_sp, module_regex_sp, symbol_regex_sp, false);
120+
}
121+
122+
} // namespace lldb_private
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#if !defined(VERBOSE_TRAP_TEST_CATEGORY) || !defined(VERBOSE_TRAP_TEST_MESSAGE)
2+
#error Please define required macros
3+
#endif
4+
5+
struct Dummy {
6+
void func() { __builtin_verbose_trap(VERBOSE_TRAP_TEST_CATEGORY, VERBOSE_TRAP_TEST_MESSAGE); }
7+
};
8+
9+
int main() {
10+
Dummy{}.func();
11+
return 0;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"Foo\" -DVERBOSE_TRAP_TEST_MESSAGE=\"Bar\"
2+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-BOTH
3+
#
4+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"\" -DVERBOSE_TRAP_TEST_MESSAGE=\"Bar\"
5+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-MESSAGE_ONLY
6+
#
7+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"Foo\" -DVERBOSE_TRAP_TEST_MESSAGE=\"\"
8+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-CATEGORY_ONLY
9+
#
10+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap.cpp -o %t.out -DVERBOSE_TRAP_TEST_CATEGORY=\"\" -DVERBOSE_TRAP_TEST_MESSAGE=\"\"
11+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK,CHECK-NONE
12+
13+
run
14+
# CHECK-BOTH: thread #{{.*}}stop reason = Foo: Bar
15+
# CHECK-MESSAGE_ONLY: thread #{{.*}}stop reason = <empty category>: Bar
16+
# CHECK-CATEGORY_ONLY: thread #{{.*}}stop reason = Foo
17+
# CHECK-NONE: thread #{{.*}}stop reason = <empty category>
18+
frame info
19+
# CHECK: frame #{{.*}}`Dummy::func(this={{.*}}) at verbose_trap.cpp
20+
frame recognizer info 0
21+
# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer
22+
q

0 commit comments

Comments
 (0)