Skip to content

Commit 3ebed51

Browse files
authored
[Clang][LLDB] Refactor trap reason demangling out of LLDB and into Clang (llvm#165996)
This patch refactors the trap reason demangling logic in `lldb_private::VerboseTrapFrameRecognizer::RecognizeFrame` into a new public function `clang::CodeGen::DemangleTrapReasonInDebugInfo`. There are two reasons for doing this: 1. In a future patch the logic for demangling needs to be used somewhere else in LLDB and thus the logic needs refactoring to avoid duplicating code. 2. The logic for demangling shouldn't really be in LLDB anyway because it's a Clang implementation detail and thus the logic really belongs inside Clang, not LLDB. Unit tests have been added for the new function that demonstrate how to use the new API. The function names recognized by VerboseTrapFrameRecognizer are identical to before. However, this patch isn't NFC because: * The `lldbTarget` library now links against `clangCodeGen` which it didn't previously. * The LLDB logging output is a little different now. The previous code tried to log failures for an invalid regex pattern and for the `Regex::match` API not returning the correct number of matches. These failure conditions are unreachable via unit testing so they have been made assertions failures inside the `DemangleTrapReasonInDebugInfo` implementation instead of trying to log them in LLDB. rdar://163230807
1 parent fa5cd27 commit 3ebed51

File tree

6 files changed

+122
-25
lines changed

6 files changed

+122
-25
lines changed

clang/include/clang/CodeGen/ModuleBuilder.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,23 @@ CodeGenerator *CreateLLVMCodeGen(DiagnosticsEngine &Diags,
120120
llvm::LLVMContext &C,
121121
CoverageSourceInfo *CoverageInfo = nullptr);
122122

123+
namespace CodeGen {
124+
/// Demangle the artificial function name (\param FuncName) used to encode trap
125+
/// reasons used in debug info for traps (e.g. __builtin_verbose_trap). See
126+
/// `CGDebugInfo::CreateTrapFailureMessageFor`.
127+
///
128+
/// \param FuncName - The function name to demangle.
129+
///
130+
/// \return A std::optional. If demangling succeeds the optional will contain
131+
/// a pair of StringRefs where the first field is the trap category and the
132+
/// second is the trap message. These can both be empty. If demangling fails the
133+
/// optional will not contain a value. Note the returned StringRefs if non-empty
134+
/// point into the underlying storage for \param FuncName and thus have the same
135+
/// lifetime.
136+
std::optional<std::pair<StringRef, StringRef>>
137+
DemangleTrapReasonInDebugInfo(StringRef FuncName);
138+
} // namespace CodeGen
139+
123140
} // end namespace clang
124141

125142
#endif

clang/lib/CodeGen/ModuleBuilder.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/IR/DataLayout.h"
2424
#include "llvm/IR/LLVMContext.h"
2525
#include "llvm/IR/Module.h"
26+
#include "llvm/Support/FormatVariadic.h"
2627
#include "llvm/Support/VirtualFileSystem.h"
2728
#include <memory>
2829

@@ -378,3 +379,31 @@ clang::CreateLLVMCodeGen(DiagnosticsEngine &Diags, llvm::StringRef ModuleName,
378379
HeaderSearchOpts, PreprocessorOpts, CGO, C,
379380
CoverageInfo);
380381
}
382+
383+
namespace clang {
384+
namespace CodeGen {
385+
std::optional<std::pair<StringRef, StringRef>>
386+
DemangleTrapReasonInDebugInfo(StringRef FuncName) {
387+
static auto TrapRegex =
388+
llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str());
389+
llvm::SmallVector<llvm::StringRef, 3> Matches;
390+
std::string *ErrorPtr = nullptr;
391+
#ifndef NDEBUG
392+
std::string Error;
393+
ErrorPtr = &Error;
394+
#endif
395+
if (!TrapRegex.match(FuncName, &Matches, ErrorPtr)) {
396+
assert(ErrorPtr && ErrorPtr->empty() && "Invalid regex pattern");
397+
return {};
398+
}
399+
400+
if (Matches.size() != 3) {
401+
assert(0 && "Expected 3 matches from Regex::match");
402+
return {};
403+
}
404+
405+
// Returns { Trap Category, Trap Message }
406+
return std::make_pair(Matches[1], Matches[2]);
407+
}
408+
} // namespace CodeGen
409+
} // namespace clang

clang/unittests/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_clang_unittest(ClangCodeGenTests
22
BufferSourceTest.cpp
33
CodeGenExternalTest.cpp
4+
DemangleTrapReasonInDebugInfo.cpp
45
TBAAMetadataTest.cpp
56
CheckTargetFeaturesTest.cpp
67
CLANG_LIBS
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//=== unittests/CodeGen/DemangleTrapReasonInDebugInfo.cpp -----------------===//
2+
//
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
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/CodeGen/ModuleBuilder.h"
10+
#include "llvm/ADT/StringRef.h"
11+
#include "gtest/gtest.h"
12+
13+
using namespace clang::CodeGen;
14+
15+
void CheckValidCommon(llvm::StringRef FuncName, const char *ExpectedCategory,
16+
const char *ExpectedMessage) {
17+
auto MaybeTrapReason = DemangleTrapReasonInDebugInfo(FuncName);
18+
ASSERT_TRUE(MaybeTrapReason.has_value());
19+
auto [Category, Message] = MaybeTrapReason.value();
20+
ASSERT_STREQ(Category.str().c_str(), ExpectedCategory);
21+
ASSERT_STREQ(Message.str().c_str(), ExpectedMessage);
22+
}
23+
24+
void CheckInvalidCommon(llvm::StringRef FuncName) {
25+
auto MaybeTrapReason = DemangleTrapReasonInDebugInfo(FuncName);
26+
ASSERT_TRUE(!MaybeTrapReason.has_value());
27+
}
28+
29+
TEST(DemangleTrapReasonInDebugInfo, Valid) {
30+
std::string FuncName(ClangTrapPrefix);
31+
FuncName += "$trap category$trap message";
32+
CheckValidCommon(FuncName, "trap category", "trap message");
33+
}
34+
35+
TEST(DemangleTrapReasonInDebugInfo, ValidEmptyCategory) {
36+
std::string FuncName(ClangTrapPrefix);
37+
FuncName += "$$trap message";
38+
CheckValidCommon(FuncName, "", "trap message");
39+
}
40+
41+
TEST(DemangleTrapReasonInDebugInfo, ValidEmptyMessage) {
42+
std::string FuncName(ClangTrapPrefix);
43+
FuncName += "$trap category$";
44+
CheckValidCommon(FuncName, "trap category", "");
45+
}
46+
47+
TEST(DemangleTrapReasonInDebugInfo, ValidAllEmpty) {
48+
// `__builtin_verbose_trap` actually allows this
49+
// currently. However, we should probably disallow this in Sema because having
50+
// an empty category and message completely defeats the point of using the
51+
// builtin (#165981).
52+
std::string FuncName(ClangTrapPrefix);
53+
FuncName += "$$";
54+
CheckValidCommon(FuncName, "", "");
55+
}
56+
57+
TEST(DemangleTrapReasonInDebugInfo, InvalidOnlyPrefix) {
58+
std::string FuncName(ClangTrapPrefix);
59+
CheckInvalidCommon(FuncName);
60+
}
61+
62+
TEST(DemangleTrapReasonInDebugInfo, Invalid) {
63+
std::string FuncName("foo");
64+
CheckInvalidCommon(FuncName);
65+
}
66+
67+
TEST(DemangleTrapReasonInDebugInfo, InvalidEmpty) { CheckInvalidCommon(""); }

lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ add_lldb_library(lldbPluginCPPRuntime
66
lldbCore
77
lldbSymbol
88
lldbTarget
9+
CLANG_LIBS
10+
clangCodeGen
911
)
1012

1113
add_subdirectory(ItaniumABI)

lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.cpp

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -95,33 +95,14 @@ VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
9595
if (func_name.empty())
9696
return {};
9797

98-
static auto trap_regex =
99-
llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str());
100-
SmallVector<llvm::StringRef, 3> matches;
101-
std::string regex_err_msg;
102-
if (!trap_regex.match(func_name, &matches, &regex_err_msg)) {
103-
LLDB_LOGF(GetLog(LLDBLog::Unwind),
104-
"Failed to parse match trap regex for '%s': %s", func_name.data(),
105-
regex_err_msg.c_str());
106-
107-
return {};
108-
}
109-
110-
// For `__clang_trap_msg$category$message$` we expect 3 matches:
111-
// 1. entire string
112-
// 2. category
113-
// 3. message
114-
if (matches.size() != 3) {
115-
LLDB_LOGF(GetLog(LLDBLog::Unwind),
116-
"Unexpected function name format. Expected '<trap prefix>$<trap "
117-
"category>$<trap message>'$ but got: '%s'.",
118-
func_name.data());
119-
98+
auto maybe_trap_reason =
99+
clang::CodeGen::DemangleTrapReasonInDebugInfo(func_name);
100+
if (!maybe_trap_reason.has_value()) {
101+
LLDB_LOGF(GetLog(LLDBLog::Unwind), "Failed to demangle '%s' as trap reason",
102+
func_name.str().c_str());
120103
return {};
121104
}
122-
123-
auto category = matches[1];
124-
auto message = matches[2];
105+
auto [category, message] = maybe_trap_reason.value();
125106

126107
std::string stop_reason =
127108
category.empty() ? "<empty category>" : category.str();

0 commit comments

Comments
 (0)