Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions clang/include/clang/CodeGen/ModuleBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ CodeGenerator *CreateLLVMCodeGen(DiagnosticsEngine &Diags,
llvm::LLVMContext &C,
CoverageSourceInfo *CoverageInfo = nullptr);

namespace CodeGen {
/// Demangle the artificial function name (\param FuncName) used to encode trap
/// reasons used in debug info for traps (e.g. __builtin_verbose_trap). See
/// `CGDebugInfo::CreateTrapFailureMessageFor`.
///
/// \param FuncName - The function name to demangle.
///
/// \return A std::optional. If demangling succeeds the optional will contain
/// a pair of StringRefs where the first field is the trap category and the
/// second is the trap message. These can both be empty. If demangling fails the
/// optional will not contain a value. Note the returned StringRefs if non-empty
/// point into the underlying storage for \param FuncName and thus have the same
/// lifetime.
std::optional<std::pair<StringRef, StringRef>>
DemangleTrapReasonInDebugInfo(StringRef FuncName);
} // namespace CodeGen

} // end namespace clang

#endif
29 changes: 29 additions & 0 deletions clang/lib/CodeGen/ModuleBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <memory>

Expand Down Expand Up @@ -376,3 +377,31 @@ clang::CreateLLVMCodeGen(DiagnosticsEngine &Diags, llvm::StringRef ModuleName,
HeaderSearchOpts, PreprocessorOpts, CGO, C,
CoverageInfo);
}

namespace clang {
namespace CodeGen {
std::optional<std::pair<StringRef, StringRef>>
DemangleTrapReasonInDebugInfo(StringRef FuncName) {
static auto TrapRegex =
llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str());
llvm::SmallVector<llvm::StringRef, 3> Matches;
std::string *ErrorPtr = nullptr;
#ifndef NDEBUG
std::string Error;
ErrorPtr = &Error;
#endif
if (!TrapRegex.match(FuncName, &Matches, ErrorPtr)) {
assert(ErrorPtr && ErrorPtr->empty() && "Invalid regex pattern");
return {};
}

if (Matches.size() != 3) {
assert(0 && "Expected 3 matches from Regex::match");
return {};
}

// Returns { Trap Category, Trap Message }
return std::make_pair(Matches[1], Matches[2]);
}
} // namespace CodeGen
} // namespace clang
1 change: 1 addition & 0 deletions clang/unittests/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_clang_unittest(ClangCodeGenTests
BufferSourceTest.cpp
CodeGenExternalTest.cpp
DemangleTrapReasonInDebugInfo.cpp
TBAAMetadataTest.cpp
CheckTargetFeaturesTest.cpp
CLANG_LIBS
Expand Down
67 changes: 67 additions & 0 deletions clang/unittests/CodeGen/DemangleTrapReasonInDebugInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//=== unittests/CodeGen/DemangleTrapReasonInDebugInfo.cpp -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang/CodeGen/ModuleBuilder.h"
#include "llvm/ADT/StringRef.h"
#include "gtest/gtest.h"

using namespace clang::CodeGen;

void CheckValidCommon(llvm::StringRef FuncName, const char *ExpectedCategory,
const char *ExpectedMessage) {
auto MaybeTrapReason = DemangleTrapReasonInDebugInfo(FuncName);
ASSERT_TRUE(MaybeTrapReason.has_value());
auto [Category, Message] = MaybeTrapReason.value();
ASSERT_STREQ(Category.str().c_str(), ExpectedCategory);
ASSERT_STREQ(Message.str().c_str(), ExpectedMessage);
}

void CheckInvalidCommon(llvm::StringRef FuncName) {
auto MaybeTrapReason = DemangleTrapReasonInDebugInfo(FuncName);
ASSERT_TRUE(!MaybeTrapReason.has_value());
}

TEST(DemangleTrapReasonInDebugInfo, Valid) {
std::string FuncName(ClangTrapPrefix);
FuncName += "$trap category$trap message";
CheckValidCommon(FuncName, "trap category", "trap message");
}

TEST(DemangleTrapReasonInDebugInfo, ValidEmptyCategory) {
std::string FuncName(ClangTrapPrefix);
FuncName += "$$trap message";
CheckValidCommon(FuncName, "", "trap message");
}

TEST(DemangleTrapReasonInDebugInfo, ValidEmptyMessage) {
std::string FuncName(ClangTrapPrefix);
FuncName += "$trap category$";
CheckValidCommon(FuncName, "trap category", "");
}

TEST(DemangleTrapReasonInDebugInfo, ValidAllEmpty) {
// `__builtin_verbose_trap` actually allows this
// currently. However, we should probably disallow this in Sema because having
// an empty category and message completely defeats the point of using the
// builtin (#165981).
std::string FuncName(ClangTrapPrefix);
FuncName += "$$";
CheckValidCommon(FuncName, "", "");
}

TEST(DemangleTrapReasonInDebugInfo, InvalidOnlyPrefix) {
std::string FuncName(ClangTrapPrefix);
CheckInvalidCommon(FuncName);
}

TEST(DemangleTrapReasonInDebugInfo, Invalid) {
std::string FuncName("foo");
CheckInvalidCommon(FuncName);
}

TEST(DemangleTrapReasonInDebugInfo, InvalidEmpty) { CheckInvalidCommon(""); }
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ add_lldb_library(lldbPluginCPPRuntime
lldbCore
lldbSymbol
lldbTarget
CLANG_LIBS
clangCodeGen
)

add_subdirectory(ItaniumABI)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,33 +101,14 @@ VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
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, &regex_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());

auto maybe_trap_reason =
clang::CodeGen::DemangleTrapReasonInDebugInfo(func_name);
if (!maybe_trap_reason.has_value()) {
LLDB_LOGF(GetLog(LLDBLog::Unwind), "Failed to demangle '%s' as trap reason",
func_name.str().c_str());
return {};
}

auto category = matches[1];
auto message = matches[2];
auto [category, message] = maybe_trap_reason.value();

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