Skip to content

Commit dccc6af

Browse files
committed
Revert "Remove changes unrelated to the protocol"
This reverts commit 16ac0cf. # Conflicts: # Sources/GDBRemoteProtocol/GDBHostCommand.swift # Sources/GDBRemoteProtocol/GDBPacket.swift
1 parent a1d5183 commit dccc6af

23 files changed

+392
-97
lines changed

[email protected]

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,19 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
178178
exclude: ["Fixtures"]
179179
),
180180

181+
.executableTarget(
182+
name: "wasmkit-gdb-tool",
183+
dependencies: [
184+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
185+
.product(name: "Logging", package: "swift-log"),
186+
.product(name: "NIOCore", package: "swift-nio"),
187+
.product(name: "NIOPosix", package: "swift-nio"),
188+
.product(name: "SystemPackage", package: "swift-system"),
189+
"GDBRemoteProtocol",
190+
"WasmKitGDBHandler",
191+
]
192+
),
193+
181194
.target(
182195
name: "WasmKitGDBHandler",
183196
dependencies: [

Sources/GDBRemoteProtocol/GDBHostCommandDecoder.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
//===----------------------------------------------------------------------===//
2-
//
3-
// This source file is part of the Swift.org open source project
4-
//
5-
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6-
// Licensed under Apache License v2.0 with Runtime Library Exception
7-
//
8-
// See https://swift.org/LICENSE.txt for license information
9-
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10-
//
11-
//===----------------------------------------------------------------------===//
12-
131
import Logging
142
import NIOCore
153

Sources/GDBRemoteProtocol/GDBTargetResponse.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
//===----------------------------------------------------------------------===//
2-
//
3-
// This source file is part of the Swift.org open source project
4-
//
5-
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6-
// Licensed under Apache License v2.0 with Runtime Library Exception
7-
//
8-
// See https://swift.org/LICENSE.txt for license information
9-
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10-
//
11-
//===----------------------------------------------------------------------===//
12-
131
import NIOCore
142

153
/// Actions supported in the `vCont` host command.

Sources/GDBRemoteProtocol/GDBTargetResponseEncoder.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
//===----------------------------------------------------------------------===//
2-
//
3-
// This source file is part of the Swift.org open source project
4-
//
5-
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6-
// Licensed under Apache License v2.0 with Runtime Library Exception
7-
//
8-
// See https://swift.org/LICENSE.txt for license information
9-
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10-
//
11-
//===----------------------------------------------------------------------===//
12-
131
import Foundation
142
import NIOCore
153

Sources/WAT/Encoder.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ extension TableType: WasmEncodable {
158158
struct ElementExprCollector: AnyInstructionVisitor {
159159
typealias Output = Void
160160

161+
var binaryOffset: Int = 0
161162
var isAllRefFunc: Bool = true
162163
var instructions: [Instruction] = []
163164

@@ -443,6 +444,7 @@ extension WatParser.DataSegmentDecl {
443444
}
444445

445446
struct ExpressionEncoder: BinaryInstructionEncoder {
447+
var binaryOffset: Int = 0
446448
var encoder = Encoder()
447449
var hasDataSegmentInstruction: Bool = false
448450

Sources/WAT/Parser/WastParser.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct WastParser {
5454
}
5555

5656
struct ConstExpressionCollector: WastConstInstructionVisitor {
57+
var binaryOffset: Int = 0
5758
let addValue: (Value) -> Void
5859

5960
mutating func visitI32Const(value: Int32) throws { addValue(.i32(UInt32(bitPattern: value))) }
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#if WasmDebuggingSupport
2+
3+
package struct Debugger: ~Copyable {
4+
enum Error: Swift.Error {
5+
case entrypointFunctionNotFound
6+
case noInstructionMappingAvailable(Int)
7+
}
8+
9+
private let valueStack: Sp
10+
private let execution: Execution
11+
private let store: Store
12+
13+
/// Parsed in-memory representation of a Wasm module instantiated for debugging.
14+
private let module: Module
15+
16+
/// Instance of parsed Wasm ``module``.
17+
private let instance: Instance
18+
19+
/// Reference to the entrypoint function of the currently debugged module, for use in ``stopAtEntrypoint``.
20+
private let entrypointFunction: Function
21+
22+
/// Threading model of the Wasm engine configuration cached for a potentially hot path.
23+
private let threadingModel: EngineConfiguration.ThreadingModel
24+
25+
private var breakpoints = [Int: CodeSlot]()
26+
27+
package init(module: Module, store: Store, imports: Imports) throws {
28+
let limit = store.engine.configuration.stackSize / MemoryLayout<StackSlot>.stride
29+
let instance = try module.instantiate(store: store, imports: imports, isDebuggable: true)
30+
31+
guard case .function(let entrypointFunction) = instance.exports["_start"] else {
32+
throw Error.entrypointFunctionNotFound
33+
}
34+
35+
self.instance = instance
36+
self.module = module
37+
self.entrypointFunction = entrypointFunction
38+
self.valueStack = UnsafeMutablePointer<StackSlot>.allocate(capacity: limit)
39+
self.store = store
40+
self.execution = Execution(store: StoreRef(store), stackEnd: valueStack.advanced(by: limit))
41+
self.threadingModel = store.engine.configuration.threadingModel
42+
}
43+
44+
package mutating func stopAtEntrypoint() throws {
45+
try self.enableBreakpoint(address: self.originalAddress(function: entrypointFunction))
46+
}
47+
48+
package func originalAddress(function: Function) throws -> Int {
49+
precondition(function.handle.isWasm)
50+
51+
switch function.handle.wasm.code {
52+
case .debuggable(let wasm, _):
53+
return wasm.originalAddress
54+
case .uncompiled:
55+
try function.handle.wasm.ensureCompiled(store: StoreRef(self.store))
56+
return try self.originalAddress(function: function)
57+
case .compiled:
58+
fatalError()
59+
}
60+
}
61+
62+
package mutating func enableBreakpoint(address: Int) throws {
63+
print("attempt to toggle a breakpoint at \(address)")
64+
65+
guard self.breakpoints[address] == nil else {
66+
print("breakpoint at \(address) already enabled")
67+
return
68+
}
69+
70+
guard let iseq = self.instance.handle.wasmToIseqMapping[address] else {
71+
throw Error.noInstructionMappingAvailable(address)
72+
}
73+
self.breakpoints[address] = iseq.pointee
74+
iseq.pointee = Instruction.breakpoint.headSlot(threadingModel: self.threadingModel)
75+
}
76+
77+
package mutating func disableBreakpoint(address: Int) throws {
78+
print("attempt to toggle a breakpoint at \(address)")
79+
80+
guard let oldCodeSlot = self.breakpoints[address] else {
81+
print("breakpoint at \(address) already disabled")
82+
return
83+
}
84+
85+
guard let iseq = self.instance.handle.wasmToIseqMapping[address] else {
86+
throw Error.noInstructionMappingAvailable(address)
87+
}
88+
iseq.pointee = oldCodeSlot
89+
self.breakpoints[address] = nil
90+
}
91+
92+
mutating func enableBreakpoint(functionIndex: FunctionIndex, offset: Int) {
93+
}
94+
95+
/// Array of addresses in the Wasm binary of executed instructions on the call stack.
96+
package var currentCallStack: [Int] {
97+
let isDebuggable = self.instance.handle.isDebuggable
98+
print("isDebuggable is \(isDebuggable)")
99+
100+
return Execution.captureBacktrace(sp: self.valueStack, store: self.store).symbols.map {
101+
self.instance.handle.iseqToWasmMapping[$0.address]!
102+
}
103+
}
104+
105+
deinit {
106+
valueStack.deallocate()
107+
}
108+
}
109+
110+
#endif

Sources/WasmKit/Execution/Errors.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@ import struct WasmParser.Import
55
/// The backtrace of the trap.
66
struct Backtrace: CustomStringConvertible, Sendable {
77
/// A symbol in the backtrace.
8-
struct Symbol {
8+
struct Symbol: @unchecked Sendable {
99
/// The name of the symbol.
1010
let name: String?
11+
let address: Pc
1112
}
1213

1314
/// The symbols in the backtrace.
14-
let symbols: [Symbol?]
15+
let symbols: [Symbol]
1516

1617
/// Textual description of the backtrace.
1718
var description: String {
18-
symbols.enumerated().map { (index, symbol) in
19-
let name = symbol?.name ?? "unknown"
20-
return " \(index): \(name)"
19+
print("backtrace contains \(symbols.count) symbols")
20+
return symbols.enumerated().map { (index, symbol) in
21+
let name = symbol.name ?? "unknown"
22+
return " \(index): (\(symbol.address)) \(name)"
2123
}.joined(separator: "\n")
2224
}
2325
}

Sources/WasmKit/Execution/Execution.swift

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import _CWasmKit
44
///
55
/// Each new invocation through exported function has a separate ``Execution``
66
/// even though the invocation happens during another invocation.
7-
struct Execution {
7+
struct Execution: ~Copyable {
88
/// The reference to the ``Store`` associated with the execution.
99
let store: StoreRef
1010
/// The end of the VM stack space.
@@ -14,6 +14,13 @@ struct Execution {
1414
/// - Note: If the trap is set, it must be released manually.
1515
private var trap: (error: UnsafeRawPointer, sp: Sp)? = nil
1616

17+
#if WasmDebuggingSupport
18+
package init(store: StoreRef, stackEnd: UnsafeMutablePointer<StackSlot>) {
19+
self.store = store
20+
self.stackEnd = stackEnd
21+
}
22+
#endif
23+
1724
/// Executes the given closure with a new execution state associated with
1825
/// the given ``Store`` instance.
1926
static func with<T>(
@@ -61,18 +68,15 @@ struct Execution {
6168

6269
static func captureBacktrace(sp: Sp, store: Store) -> Backtrace {
6370
var frames = FrameIterator(sp: sp)
64-
var symbols: [Backtrace.Symbol?] = []
71+
var symbols: [Backtrace.Symbol] = []
72+
6573
while let frame = frames.next() {
6674
guard let function = frame.function else {
67-
symbols.append(nil)
75+
symbols.append(.init(name: nil, address: frame.pc))
6876
continue
6977
}
7078
let symbolName = store.nameRegistry.symbolicate(.wasm(function))
71-
symbols.append(
72-
Backtrace.Symbol(
73-
name: symbolName
74-
)
75-
)
79+
symbols.append(.init(name: symbolName, address: frame.pc))
7680
}
7781
return Backtrace(symbols: symbols)
7882
}
@@ -248,7 +252,7 @@ extension Sp {
248252
nonmutating set { self[-1] = UInt64(UInt(bitPattern: newValue)) }
249253
}
250254

251-
fileprivate var currentInstance: InternalInstance? {
255+
var currentInstance: InternalInstance? {
252256
currentFunction?.instance
253257
}
254258
}

Sources/WasmKit/Execution/Function.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ struct WasmFunctionEntity {
243243
switch code {
244244
case .uncompiled(let code):
245245
return try compile(store: store, code: code)
246-
case .compiled(let iseq), .compiledAndPatchable(_, let iseq):
246+
case .compiled(let iseq), .debuggable(_, let iseq):
247247
return iseq
248248
}
249249
}
@@ -280,10 +280,14 @@ extension EntityHandle<WasmFunctionEntity> {
280280
case .uncompiled(let code):
281281
return try self.withValue {
282282
let iseq = try $0.compile(store: store, code: code)
283-
$0.code = .compiled(iseq)
283+
if $0.instance.isDebuggable {
284+
$0.code = .debuggable(code, iseq)
285+
} else {
286+
$0.code = .compiled(iseq)
287+
}
284288
return iseq
285289
}
286-
case .compiled(let iseq), .compiledAndPatchable(_, let iseq):
290+
case .compiled(let iseq), .debuggable(_, let iseq):
287291
return iseq
288292
}
289293
}
@@ -316,7 +320,7 @@ struct InstructionSequence {
316320
enum CodeBody {
317321
case uncompiled(InternalUncompiledCode)
318322
case compiled(InstructionSequence)
319-
case compiledAndPatchable(InternalUncompiledCode, InstructionSequence)
323+
case debuggable(InternalUncompiledCode, InstructionSequence)
320324
}
321325

322326
extension Reference {

0 commit comments

Comments
 (0)