Skip to content

Commit bc1eb32

Browse files
committed
Add CallStack: Sequence, Debugger.LocalAddress types
1 parent 6583f1d commit bc1eb32

File tree

3 files changed

+76
-30
lines changed

3 files changed

+76
-30
lines changed

Sources/WasmKit/Execution/Debugger.swift

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
case unknownCurrentFunctionForResumedBreakpoint(UnsafeMutablePointer<UInt64>)
2222
case noInstructionMappingAvailable(Int)
2323
case noReverseInstructionMappingAvailable(UnsafeMutablePointer<UInt64>)
24+
case stackFuncIndexOOB(LocalAddress)
25+
case stackLocalIndexOOB(LocalAddress)
26+
case notStoppedAtBreakpoint
2427
}
2528

2629
private let valueStack: Sp
@@ -52,11 +55,6 @@
5255
/// was not compiled yet in lazy compilation mode).
5356
private let functionAddresses: [(address: Int, instanceFunctionIndex: Int)]
5457

55-
/// Addresses of functions in the original Wasm binary, used for looking up functions when a breakpoint
56-
/// is enabled at an arbitrary address if it isn't present in ``InstructionMapping`` yet (i.e. the
57-
/// was not compiled yet in lazy compilation mode).
58-
private let functionAddresses: [(address: Int, instanceFunctionIndex: Int)]
59-
6058
/// Initializes a new debugger state instance.
6159
/// - Parameters:
6260
/// - module: Wasm module to instantiate.
@@ -244,8 +242,38 @@
244242
try self.run()
245243
}
246244

247-
package func getLocal(frameIndex: Int, localIndex: Int) -> Address {
245+
package struct LocalAddress {
246+
let frameIndex: UInt32
247+
let localIndex: UInt32
248+
249+
package init(frameIndex: Int, localIndex: Int) {
250+
self.frameIndex = frameIndex
251+
self.localIndex = localIndex
252+
}
253+
}
254+
255+
256+
/// Iterates through Wasm call stack to return a local at a given address when
257+
/// debugged module is stopped at a breakpoint.
258+
/// - Parameter address: address of the local to return.
259+
/// - Returns: Raw untyped Wasm value at a given address
260+
package func getLocal(address: LocalAddress) throws(Error) -> UInt64 {
261+
guard case let .stoppedAtBreakpoint(breakpoint) = self.state else {
262+
throw Error.notStoppedAtBreakpoint
263+
}
264+
265+
var i = 0
266+
for frame in Execution.CallStack(sp: breakpoint.sp) {
267+
guard address.frameIndex == i else {
268+
i += 1
269+
continue
270+
}
271+
272+
// TODO: can we guarantee we have no local index OOB here?
273+
return frame.sp[address.localIndex].storage
274+
}
248275

276+
throw Error.stackFuncIndexOOB(address)
249277
}
250278

251279
/// Array of addresses in the Wasm binary of executed instructions on the call stack.

Sources/WasmKit/Execution/Execution.swift

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,35 +42,43 @@ struct Execution: ~Copyable {
4242
sp.currentInstance.unsafelyUnwrapped
4343
}
4444

45-
/// An iterator for the call frames in the VM stack.
46-
struct FrameIterator: IteratorProtocol {
47-
struct Element {
48-
let pc: Pc
49-
let sp: Sp
50-
}
45+
struct CallStack: Sequence {
46+
/// An iterator for the call frames in the VM stack.
47+
struct FrameIterator: IteratorProtocol {
48+
struct Element {
49+
let pc: Pc
50+
let sp: Sp
51+
}
5152

52-
/// The stack pointer currently traversed.
53-
private var sp: Sp?
53+
/// The stack pointer currently traversed.
54+
private var sp: Sp?
5455

55-
init(sp: Sp) {
56-
self.sp = sp
57-
}
56+
init(sp: Sp) {
57+
self.sp = sp
58+
}
5859

59-
mutating func next() -> Element? {
60-
guard let sp = self.sp, let pc = sp.returnPC else {
61-
// Reached the root frame, whose stack pointer is nil.
62-
return nil
60+
mutating func next() -> Element? {
61+
guard let sp = self.sp, let pc = sp.returnPC else {
62+
// Reached the root frame, whose stack pointer is nil.
63+
return nil
64+
}
65+
self.sp = sp.previousSP
66+
return Element(pc: pc, sp: sp)
6367
}
64-
self.sp = sp.previousSP
65-
return Element(pc: pc, sp: sp)
68+
}
69+
70+
let sp: Sp
71+
72+
func makeIterator() -> FrameIterator {
73+
FrameIterator(sp: self.sp)
6674
}
6775
}
6876

6977
static func captureBacktrace(sp: Sp, store: Store) -> Backtrace {
70-
var frames = FrameIterator(sp: sp)
78+
let callStack = CallStack(sp: sp)
7179
var symbols: [Backtrace.Symbol] = []
7280

73-
while let frame = frames.next() {
81+
for frame in callStack {
7482
guard let function = frame.sp.currentFunction else {
7583
symbols.append(.init(name: nil, address: frame.pc))
7684
continue

Sources/WasmKitGDBHandler/WasmKitGDBHandler.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,17 @@
3232
}
3333
}
3434

35-
private let codeOffset = UInt64(0x4000_0000_0000_0000)
35+
36+
extension Debugger.LocalAddress {
37+
package init(raw: UInt64) {
38+
let rawAdjusted = raw - localOffset
39+
self.init(frameIndex: UInt32(truncatingIfNeeded: rawAdjusted >> 32), localIndex: UInt32(truncatingIfNeeded: rawAdjusted))
40+
}
41+
}
42+
43+
private let codeOffset = UInt64(0x4000_0000_0000_0000)
3644
private let stackOffset = UInt64(0x8000_0000_0000_0000)
45+
private let localOffset = UInt64(0xC000_0000_0000_0000)
3746

3847
package actor WasmKitGDBHandler {
3948
enum ResumeThreadsAction: String {
@@ -231,13 +240,14 @@
231240
let argumentsArray = command.arguments.split(separator: ",")
232241
guard
233242
argumentsArray.count == 2,
234-
let hostAddress = UInt64(hexEncoded: argumentsArray[0]),
243+
let address = UInt64(hexEncoded: argumentsArray[0]),
235244
var length = Int(hexEncoded: argumentsArray[1])
236245
else { throw Error.unknownReadMemoryArguments }
237246

238-
if address > stackOffset {
239-
let stackOffset = address - codeOffset
240-
247+
if address > localOffset {
248+
let localRaw = address - localOffset
249+
let localAddress = Debugger.LocalAddress(frameIndex: )
250+
} else if address > stackOffset {
241251
fatalError("Stack reads are not implemented in the debugger yet")
242252
} else if address > codeOffset {
243253
let binaryOffset = address - stackOffset

0 commit comments

Comments
 (0)