Skip to content

[lldb][FrameRecognizer] Make VerboseTrapFrameRecognizer aware of Swift-C++ interop frames #9307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 1, 2024
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
55 changes: 48 additions & 7 deletions lldb/source/Plugins/Language/Swift/SwiftFrameRecognizers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#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/Target/Thread.h"

Expand All @@ -17,6 +18,7 @@

using namespace lldb;
using namespace lldb_private;

namespace lldb_private {

/// Holds the stack frame that caused the runtime failure and the inlined stop
Expand Down Expand Up @@ -150,6 +152,33 @@ class SwiftHiddenFrameRecognizer : public StackFrameRecognizer {
bool ShouldHide() override { return true; }
};

/// Returns true if \ref root represents a Swift name
/// that we want to mark hidden by this recognizer.
///
/// Currently these are:
/// * Async thunks
/// * Auto-conformed protocols in the `std` module
///
bool ShouldHideSwiftName(NodePointer root) {
using namespace swift_demangle;

if (NodeAtPath(root, {Node::Kind::Global,
Node::Kind::AsyncAwaitResumePartialFunction}) &&
(ChildAtPath(root, {Node::Kind::BackDeploymentFallback}) ||
ChildAtPath(root, {Node::Kind::PartialApplyForwarder})))
return true;

if (auto witness_node =
NodeAtPath(root, {Node::Kind::Global, Node::Kind::ProtocolWitness,
Node::Kind::ProtocolConformance}))
if (auto module_node = ChildAtPath(witness_node, {Node::Kind::Module});
module_node && module_node->getText() == "__C_Synthesized")
return true;
;

return false;
}

public:
SwiftHiddenFrameRecognizer() : m_hidden_frame(new SwiftHiddenFrame()) {}

Expand All @@ -159,22 +188,34 @@ class SwiftHiddenFrameRecognizer : public StackFrameRecognizer {
RecognizeFrame(lldb::StackFrameSP frame_sp) override {
if (!frame_sp)
return {};

// Hide compiler-generated frames.
if (frame_sp->IsArtificial())
return m_hidden_frame;

const auto &sc = frame_sp->GetSymbolContext(lldb::eSymbolContextFunction);
if (!sc.function)
return {};

FileSpec source_file;
uint32_t line_no;
sc.function->GetStartLineSourceInfo(source_file, line_no);
// FIXME: these <compiler-generated> frames should be marked artificial
// by the Swift compiler.
if (source_file.GetFilename() == "<compiler-generated>" && line_no == 0)
return m_hidden_frame;

auto symbol_name =
sc.function->GetMangled().GetMangledName().GetStringRef();

using namespace swift::Demangle;
using namespace swift_demangle;
Context demangle_ctx;
NodePointer nodes =
SwiftLanguageRuntime::DemangleSymbolAsNode(symbol_name, demangle_ctx);
if (NodeAtPath(nodes, {Node::Kind::Global,
Node::Kind::AsyncAwaitResumePartialFunction}) &&
(ChildAtPath(nodes, {Node::Kind::BackDeploymentFallback}) ||
ChildAtPath(nodes, {Node::Kind::PartialApplyForwarder})))
return m_hidden_frame;
if (NodePointer nodes = SwiftLanguageRuntime::DemangleSymbolAsNode(
symbol_name, demangle_ctx))
if (ShouldHideSwiftName(nodes))
return m_hidden_frame;

return {};
}
};
Expand Down
10 changes: 8 additions & 2 deletions lldb/source/Target/VerboseTrapFrameRecognizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegularExpression.h"

#include "clang/CodeGen/ModuleBuilder.h"

Expand Down Expand Up @@ -36,10 +37,15 @@ static StackFrameSP FindMostRelevantFrame(Thread &selected_thread) {
if (!frame_name)
return nullptr;

// Found a frame outside of the `std` namespace. That's the
// Find a frame outside of the `std` namespace. That's the
// first frame in user-code that ended up triggering the
// verbose_trap. Hence that's the one we want to display.
if (!frame_name.GetStringRef().starts_with("std::"))
//
// IsHidden will get us to the first non-implementation detail
// frame. But that could still be in the `std` namespace, so
// check the namespace prefix too.
if (!frame_name.GetStringRef().starts_with("std::") &&
!most_relevant_frame_sp->IsHidden())
return most_relevant_frame_sp;

++stack_idx;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SWIFT_SOURCES := main.swift
SWIFT_CXX_INTEROP := 1
SWIFTFLAGS_EXTRAS = -Xcc -I$(SRCDIR)
include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

"""
Test that verbose_trap works on forward interop mode.
"""
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *


class TestSwiftForwardInteropVerboseTrap(TestBase):

@swiftTest
def test(self):
self.build()
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.assertTrue(target, VALID_TARGET)

target.BreakpointCreateByName("Break here", "a.out")
process = target.LaunchSimple(None, None, self.get_process_working_directory())
self.assertTrue(process, PROCESS_IS_VALID)

# Make sure we stopped in the first user-level frame.
self.assertTrue(self.frame().name.startswith("a.takes<"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <iterator>

namespace std {
void function_that_aborts() { __builtin_verbose_trap("Error", "from C++"); }

struct ConstIterator {
private:
int value;

public:
// Make sure this auto-conforms to UnsafeCxxInputIterator

using iterator_category = std::input_iterator_tag;
using value_type = int;
using pointer = int *;
using reference = const int &;
using difference_type = int;

ConstIterator(int value) : value(value) {}

void operator*() const { std::function_that_aborts(); }

ConstIterator &operator++() { return *this; }
ConstIterator operator++(int) { return ConstIterator(value); }

bool operator==(const ConstIterator &other) const { return false; }
bool operator!=(const ConstIterator &other) const { return true; }
};

} // namespace std
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Aborts

func takes(_ t: some UnsafeCxxInputIterator) {
t.pointee
}

func main() {
var x = std.ConstIterator(137);
takes(x);
print("Break here");
}

main()

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Aborts {
header "aborts.h"
requires cplusplus
}