Skip to content

Commit 14abc9e

Browse files
authored
Merge pull request #9306 from swiftlang/lldb/verbose-trap-interop-to-20240723
[lldb][FrameRecognizer] Make VerboseTrapFrameRecognizer aware of Swift-C++ interop frames
2 parents f9f1453 + 567a5d6 commit 14abc9e

File tree

7 files changed

+131
-9
lines changed

7 files changed

+131
-9
lines changed

lldb/source/Plugins/Language/Swift/SwiftFrameRecognizers.cpp

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "lldb/Symbol/Function.h"
55
#include "lldb/Symbol/SymbolContext.h"
66
#include "lldb/Target/Process.h"
7+
#include "lldb/Target/StackFrameRecognizer.h"
78
#include "lldb/Target/Target.h"
89
#include "lldb/Target/Thread.h"
910

@@ -17,6 +18,7 @@
1718

1819
using namespace lldb;
1920
using namespace lldb_private;
21+
2022
namespace lldb_private {
2123

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

155+
/// Returns true if \ref root represents a Swift name
156+
/// that we want to mark hidden by this recognizer.
157+
///
158+
/// Currently these are:
159+
/// * Async thunks
160+
/// * Auto-conformed protocols in the `std` module
161+
///
162+
bool ShouldHideSwiftName(NodePointer root) {
163+
using namespace swift_demangle;
164+
165+
if (NodeAtPath(root, {Node::Kind::Global,
166+
Node::Kind::AsyncAwaitResumePartialFunction}) &&
167+
(ChildAtPath(root, {Node::Kind::BackDeploymentFallback}) ||
168+
ChildAtPath(root, {Node::Kind::PartialApplyForwarder})))
169+
return true;
170+
171+
if (auto witness_node =
172+
NodeAtPath(root, {Node::Kind::Global, Node::Kind::ProtocolWitness,
173+
Node::Kind::ProtocolConformance}))
174+
if (auto module_node = ChildAtPath(witness_node, {Node::Kind::Module});
175+
module_node && module_node->getText() == "__C_Synthesized")
176+
return true;
177+
;
178+
179+
return false;
180+
}
181+
153182
public:
154183
SwiftHiddenFrameRecognizer() : m_hidden_frame(new SwiftHiddenFrame()) {}
155184

@@ -159,22 +188,34 @@ class SwiftHiddenFrameRecognizer : public StackFrameRecognizer {
159188
RecognizeFrame(lldb::StackFrameSP frame_sp) override {
160189
if (!frame_sp)
161190
return {};
191+
192+
// Hide compiler-generated frames.
193+
if (frame_sp->IsArtificial())
194+
return m_hidden_frame;
195+
162196
const auto &sc = frame_sp->GetSymbolContext(lldb::eSymbolContextFunction);
163197
if (!sc.function)
164198
return {};
165199

200+
FileSpec source_file;
201+
uint32_t line_no;
202+
sc.function->GetStartLineSourceInfo(source_file, line_no);
203+
// FIXME: these <compiler-generated> frames should be marked artificial
204+
// by the Swift compiler.
205+
if (source_file.GetFilename() == "<compiler-generated>" && line_no == 0)
206+
return m_hidden_frame;
207+
166208
auto symbol_name =
167209
sc.function->GetMangled().GetMangledName().GetStringRef();
210+
168211
using namespace swift::Demangle;
169212
using namespace swift_demangle;
170213
Context demangle_ctx;
171-
NodePointer nodes =
172-
SwiftLanguageRuntime::DemangleSymbolAsNode(symbol_name, demangle_ctx);
173-
if (NodeAtPath(nodes, {Node::Kind::Global,
174-
Node::Kind::AsyncAwaitResumePartialFunction}) &&
175-
(ChildAtPath(nodes, {Node::Kind::BackDeploymentFallback}) ||
176-
ChildAtPath(nodes, {Node::Kind::PartialApplyForwarder})))
177-
return m_hidden_frame;
214+
if (NodePointer nodes = SwiftLanguageRuntime::DemangleSymbolAsNode(
215+
symbol_name, demangle_ctx))
216+
if (ShouldHideSwiftName(nodes))
217+
return m_hidden_frame;
218+
178219
return {};
179220
}
180221
};

lldb/source/Target/VerboseTrapFrameRecognizer.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "lldb/Utility/LLDBLog.h"
1111
#include "lldb/Utility/Log.h"
12+
#include "lldb/Utility/RegularExpression.h"
1213

1314
#include "clang/CodeGen/ModuleBuilder.h"
1415

@@ -36,10 +37,15 @@ static StackFrameSP FindMostRelevantFrame(Thread &selected_thread) {
3637
if (!frame_name)
3738
return nullptr;
3839

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

4551
++stack_idx;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SWIFT_SOURCES := main.swift
2+
SWIFT_CXX_INTEROP := 1
3+
SWIFTFLAGS_EXTRAS = -Xcc -I$(SRCDIR)
4+
include Makefile.rules
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
"""
3+
Test that verbose_trap works on forward interop mode.
4+
"""
5+
from lldbsuite.test.lldbtest import *
6+
from lldbsuite.test.decorators import *
7+
8+
9+
class TestSwiftForwardInteropVerboseTrap(TestBase):
10+
11+
@swiftTest
12+
def test(self):
13+
self.build()
14+
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
15+
self.assertTrue(target, VALID_TARGET)
16+
17+
target.BreakpointCreateByName("Break here", "a.out")
18+
process = target.LaunchSimple(None, None, self.get_process_working_directory())
19+
self.assertTrue(process, PROCESS_IS_VALID)
20+
21+
# Make sure we stopped in the first user-level frame.
22+
self.assertTrue(self.frame().name.startswith("a.takes<"))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <iterator>
2+
3+
namespace std {
4+
void function_that_aborts() { __builtin_verbose_trap("Error", "from C++"); }
5+
6+
struct ConstIterator {
7+
private:
8+
int value;
9+
10+
public:
11+
// Make sure this auto-conforms to UnsafeCxxInputIterator
12+
13+
using iterator_category = std::input_iterator_tag;
14+
using value_type = int;
15+
using pointer = int *;
16+
using reference = const int &;
17+
using difference_type = int;
18+
19+
ConstIterator(int value) : value(value) {}
20+
21+
void operator*() const { std::function_that_aborts(); }
22+
23+
ConstIterator &operator++() { return *this; }
24+
ConstIterator operator++(int) { return ConstIterator(value); }
25+
26+
bool operator==(const ConstIterator &other) const { return false; }
27+
bool operator!=(const ConstIterator &other) const { return true; }
28+
};
29+
30+
} // namespace std
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Aborts
2+
3+
func takes(_ t: some UnsafeCxxInputIterator) {
4+
t.pointee
5+
}
6+
7+
func main() {
8+
var x = std.ConstIterator(137);
9+
takes(x);
10+
print("Break here");
11+
}
12+
13+
main()
14+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Aborts {
2+
header "aborts.h"
3+
requires cplusplus
4+
}
5+

0 commit comments

Comments
 (0)