|
1 | 1 | #if WasmDebuggingSupport |
2 | 2 |
|
3 | | -import GDBRemoteProtocol |
4 | | -import Logging |
5 | | -import NIOCore |
6 | | -import NIOFileSystem |
7 | | -import Synchronization |
8 | | -import SystemPackage |
9 | | -import WasmKit |
10 | | -import WasmKitWASI |
11 | | - |
12 | | -extension BinaryInteger { |
13 | | - init?(hexEncoded: Substring) { |
14 | | - var result = Self.zero |
15 | | - for (offset, element) in hexEncoded.reversed().enumerated() { |
16 | | - guard let digit = element.hexDigitValue else { return nil } |
17 | | - result += Self(digit) << (offset * 4) |
18 | | - } |
| 3 | + import GDBRemoteProtocol |
| 4 | + import Logging |
| 5 | + import NIOCore |
| 6 | + import NIOFileSystem |
| 7 | + import Synchronization |
| 8 | + import SystemPackage |
| 9 | + import WasmKit |
| 10 | + import WasmKitWASI |
| 11 | + |
| 12 | + extension BinaryInteger { |
| 13 | + init?(hexEncoded: Substring) { |
| 14 | + var result = Self.zero |
| 15 | + for (offset, element) in hexEncoded.reversed().enumerated() { |
| 16 | + guard let digit = element.hexDigitValue else { return nil } |
| 17 | + result += Self(digit) << (offset * 4) |
| 18 | + } |
19 | 19 |
|
20 | | - self = result |
| 20 | + self = result |
| 21 | + } |
21 | 22 | } |
22 | | -} |
23 | 23 |
|
24 | | -package actor WasmKitGDBHandler { |
25 | | - enum Error: Swift.Error { |
26 | | - case unknownTransferArguments |
27 | | - case unknownReadMemoryArguments |
28 | | - case entrypointFunctionNotFound |
29 | | - } |
| 24 | + package actor WasmKitGDBHandler { |
| 25 | + enum Error: Swift.Error { |
| 26 | + case unknownTransferArguments |
| 27 | + case unknownReadMemoryArguments |
| 28 | + case entrypointFunctionNotFound |
| 29 | + } |
30 | 30 |
|
31 | | - private let wasmBinary: ByteBuffer |
32 | | - private let module: Module |
33 | | - private let moduleFilePath: FilePath |
34 | | - private let logger: Logger |
35 | | - private let debugger: Debugger |
36 | | - private let instance: Instance |
37 | | - private let entrypointFunction: Function |
38 | | - private let functionsRLE: [(wasmAddress: Int, iSeqAddress: Int)] = [] |
| 31 | + private let wasmBinary: ByteBuffer |
| 32 | + private let module: Module |
| 33 | + private let moduleFilePath: FilePath |
| 34 | + private let logger: Logger |
| 35 | + private let debugger: Debugger |
| 36 | + private let instance: Instance |
| 37 | + private let entrypointFunction: Function |
| 38 | + private let functionsRLE: [(wasmAddress: Int, iSeqAddress: Int)] = [] |
39 | 39 |
|
40 | | - package init(logger: Logger, moduleFilePath: FilePath) async throws { |
41 | | - self.logger = logger |
| 40 | + package init(logger: Logger, moduleFilePath: FilePath) async throws { |
| 41 | + self.logger = logger |
42 | 42 |
|
43 | | - self.wasmBinary = try await FileSystem.shared.withFileHandle(forReadingAt: moduleFilePath) { |
44 | | - try await $0.readToEnd(maximumSizeAllowed: .unlimited) |
45 | | - } |
| 43 | + self.wasmBinary = try await FileSystem.shared.withFileHandle(forReadingAt: moduleFilePath) { |
| 44 | + try await $0.readToEnd(maximumSizeAllowed: .unlimited) |
| 45 | + } |
46 | 46 |
|
47 | | - self.module = try parseWasm(bytes: .init(buffer: self.wasmBinary)) |
48 | | - self.moduleFilePath = moduleFilePath |
49 | | - let store = Store(engine: Engine()) |
50 | | - self.debugger = Debugger(store: store) |
| 47 | + self.module = try parseWasm(bytes: .init(buffer: self.wasmBinary)) |
| 48 | + self.moduleFilePath = moduleFilePath |
| 49 | + let store = Store(engine: Engine()) |
| 50 | + self.debugger = Debugger(store: store) |
51 | 51 |
|
52 | | - var imports = Imports() |
53 | | - let wasi = try WASIBridgeToHost() |
54 | | - wasi.link(to: &imports, store: store) |
55 | | - self.instance = try module.instantiate(store: store, imports: imports) |
| 52 | + var imports = Imports() |
| 53 | + let wasi = try WASIBridgeToHost() |
| 54 | + wasi.link(to: &imports, store: store) |
| 55 | + self.instance = try module.instantiate(store: store, imports: imports) |
56 | 56 |
|
57 | | - guard case .function(let entrypointFunction) = self.instance.exports["_start"] else { |
58 | | - throw Error.entrypointFunctionNotFound |
| 57 | + guard case .function(let entrypointFunction) = self.instance.exports["_start"] else { |
| 58 | + throw Error.entrypointFunctionNotFound |
| 59 | + } |
| 60 | + self.entrypointFunction = entrypointFunction |
59 | 61 | } |
60 | | - self.entrypointFunction = entrypointFunction |
61 | | - } |
62 | 62 |
|
63 | | - package func handle(command: GDBHostCommand) throws -> GDBTargetResponse { |
64 | | - let responseKind: GDBTargetResponse.Kind |
65 | | - logger.trace("handling GDB host command", metadata: ["GDBHostCommand": .string(command.kind.rawValue)]) |
66 | | - |
67 | | - var isNoAckModeActivated = false |
68 | | - switch command.kind { |
69 | | - case .startNoAckMode: |
70 | | - isNoAckModeActivated = true |
71 | | - fallthrough |
72 | | - |
73 | | - case .isThreadSuffixSupported, .listThreadsInStopReply: |
74 | | - responseKind = .ok |
75 | | - |
76 | | - case .hostInfo: |
77 | | - responseKind = .keyValuePairs([ |
78 | | - "arch": "wasm32", |
79 | | - "ptrsize": "4", |
80 | | - "endian": "little", |
81 | | - "ostype": "wasip1", |
82 | | - "vendor": "WasmKit", |
83 | | - ]) |
84 | | - |
85 | | - case .supportedFeatures: |
86 | | - responseKind = .string("qXfer:libraries:read+;PacketSize=1000;") |
87 | | - |
88 | | - case .vContSupportedActions: |
89 | | - responseKind = .vContSupportedActions([.continue, .step]) |
90 | | - |
91 | | - case .isVAttachOrWaitSupported, .enableErrorStrings, .structuredDataPlugins, .readMemoryBinaryData: |
92 | | - responseKind = .empty |
93 | | - |
94 | | - case .processInfo: |
95 | | - responseKind = .keyValuePairs([ |
96 | | - "pid": "1", |
97 | | - "parent-pid": "1", |
98 | | - "arch": "wasm32", |
99 | | - "endian": "little", |
100 | | - "ptrsize": "4", |
101 | | - ]) |
102 | | - |
103 | | - case .currentThreadID: |
104 | | - responseKind = .string("QC1") |
105 | | - |
106 | | - case .firstThreadInfo: |
107 | | - responseKind = .string("m1") |
108 | | - |
109 | | - case .subsequentThreadInfo: |
110 | | - responseKind = .string("l") |
111 | | - |
112 | | - case .targetStatus: |
113 | | - responseKind = .keyValuePairs([ |
114 | | - "T05thread": "1", |
115 | | - "reason": "trace", |
116 | | - ]) |
117 | | - |
118 | | - case .registerInfo: |
119 | | - if command.arguments == "0" { |
| 63 | + package func handle(command: GDBHostCommand) throws -> GDBTargetResponse { |
| 64 | + let responseKind: GDBTargetResponse.Kind |
| 65 | + logger.trace("handling GDB host command", metadata: ["GDBHostCommand": .string(command.kind.rawValue)]) |
| 66 | + |
| 67 | + var isNoAckModeActivated = false |
| 68 | + switch command.kind { |
| 69 | + case .startNoAckMode: |
| 70 | + isNoAckModeActivated = true |
| 71 | + fallthrough |
| 72 | + |
| 73 | + case .isThreadSuffixSupported, .listThreadsInStopReply: |
| 74 | + responseKind = .ok |
| 75 | + |
| 76 | + case .hostInfo: |
120 | 77 | responseKind = .keyValuePairs([ |
121 | | - "name": "pc", |
122 | | - "bitsize": "64", |
123 | | - "offset": "0", |
124 | | - "encoding": "uint", |
125 | | - "format": "hex", |
126 | | - "set": "General Purpose Registers", |
127 | | - "gcc": "16", |
128 | | - "dwarf": "16", |
129 | | - "generic": "pc", |
| 78 | + "arch": "wasm32", |
| 79 | + "ptrsize": "4", |
| 80 | + "endian": "little", |
| 81 | + "ostype": "wasip1", |
| 82 | + "vendor": "WasmKit", |
130 | 83 | ]) |
131 | | - } else { |
132 | | - responseKind = .string("E45") |
133 | | - } |
134 | 84 |
|
135 | | - case .transfer: |
136 | | - if command.arguments.starts(with: "libraries:read:") { |
137 | | - responseKind = .string( |
138 | | - """ |
139 | | - l<library-list> |
140 | | - <library name="\(self.moduleFilePath.string)"><section address="0x4000000000000000"/></library> |
141 | | - </library-list> |
142 | | - """) |
143 | | - } else { |
144 | | - throw Error.unknownTransferArguments |
145 | | - } |
| 85 | + case .supportedFeatures: |
| 86 | + responseKind = .string("qXfer:libraries:read+;PacketSize=1000;") |
146 | 87 |
|
147 | | - case .readMemory: |
148 | | - let argumentsArray = command.arguments.split(separator: ",") |
149 | | - guard |
150 | | - argumentsArray.count == 2, |
151 | | - let address = UInt64(hexEncoded: argumentsArray[0]), |
152 | | - var length = Int(hexEncoded: argumentsArray[1]) |
153 | | - else { throw Error.unknownReadMemoryArguments } |
| 88 | + case .vContSupportedActions: |
| 89 | + responseKind = .vContSupportedActions([.continue, .step]) |
154 | 90 |
|
155 | | - let binaryOffset = Int(address - 0x4000000000000000) |
| 91 | + case .isVAttachOrWaitSupported, .enableErrorStrings, .structuredDataPlugins, .readMemoryBinaryData: |
| 92 | + responseKind = .empty |
156 | 93 |
|
157 | | - if binaryOffset + length > wasmBinary.readableBytes { |
158 | | - length = wasmBinary.readableBytes - binaryOffset |
159 | | - } |
| 94 | + case .processInfo: |
| 95 | + responseKind = .keyValuePairs([ |
| 96 | + "pid": "1", |
| 97 | + "parent-pid": "1", |
| 98 | + "arch": "wasm32", |
| 99 | + "endian": "little", |
| 100 | + "ptrsize": "4", |
| 101 | + ]) |
160 | 102 |
|
161 | | - responseKind = .hexEncodedBinary(wasmBinary.readableBytesView[binaryOffset..<(binaryOffset + length)]) |
| 103 | + case .currentThreadID: |
| 104 | + responseKind = .string("QC1") |
162 | 105 |
|
163 | | - case .wasmCallStack: |
164 | | - print(self.debugger.captureBacktrace()) |
165 | | - responseKind = .empty |
| 106 | + case .firstThreadInfo: |
| 107 | + responseKind = .string("m1") |
166 | 108 |
|
167 | | - case .generalRegisters: |
168 | | - fatalError() |
169 | | - } |
| 109 | + case .subsequentThreadInfo: |
| 110 | + responseKind = .string("l") |
170 | 111 |
|
171 | | - logger.trace("handler produced a response", metadata: ["GDBTargetResponse": .string("\(responseKind)")]) |
| 112 | + case .targetStatus: |
| 113 | + responseKind = .keyValuePairs([ |
| 114 | + "T05thread": "1", |
| 115 | + "reason": "trace", |
| 116 | + ]) |
172 | 117 |
|
173 | | - return .init(kind: responseKind, isNoAckModeActivated: isNoAckModeActivated) |
174 | | - } |
| 118 | + case .registerInfo: |
| 119 | + if command.arguments == "0" { |
| 120 | + responseKind = .keyValuePairs([ |
| 121 | + "name": "pc", |
| 122 | + "bitsize": "64", |
| 123 | + "offset": "0", |
| 124 | + "encoding": "uint", |
| 125 | + "format": "hex", |
| 126 | + "set": "General Purpose Registers", |
| 127 | + "gcc": "16", |
| 128 | + "dwarf": "16", |
| 129 | + "generic": "pc", |
| 130 | + ]) |
| 131 | + } else { |
| 132 | + responseKind = .string("E45") |
| 133 | + } |
| 134 | + |
| 135 | + case .transfer: |
| 136 | + if command.arguments.starts(with: "libraries:read:") { |
| 137 | + responseKind = .string( |
| 138 | + """ |
| 139 | + l<library-list> |
| 140 | + <library name="\(self.moduleFilePath.string)"><section address="0x4000000000000000"/></library> |
| 141 | + </library-list> |
| 142 | + """) |
| 143 | + } else { |
| 144 | + throw Error.unknownTransferArguments |
| 145 | + } |
| 146 | + |
| 147 | + case .readMemory: |
| 148 | + let argumentsArray = command.arguments.split(separator: ",") |
| 149 | + guard |
| 150 | + argumentsArray.count == 2, |
| 151 | + let address = UInt64(hexEncoded: argumentsArray[0]), |
| 152 | + var length = Int(hexEncoded: argumentsArray[1]) |
| 153 | + else { throw Error.unknownReadMemoryArguments } |
| 154 | + |
| 155 | + let binaryOffset = Int(address - 0x4000_0000_0000_0000) |
| 156 | + |
| 157 | + if binaryOffset + length > wasmBinary.readableBytes { |
| 158 | + length = wasmBinary.readableBytes - binaryOffset |
| 159 | + } |
| 160 | + |
| 161 | + responseKind = .hexEncodedBinary(wasmBinary.readableBytesView[binaryOffset..<(binaryOffset + length)]) |
| 162 | + |
| 163 | + case .wasmCallStack: |
| 164 | + print(self.debugger.captureBacktrace()) |
| 165 | + responseKind = .empty |
| 166 | + |
| 167 | + case .generalRegisters: |
| 168 | + fatalError() |
| 169 | + } |
| 170 | + |
| 171 | + logger.trace("handler produced a response", metadata: ["GDBTargetResponse": .string("\(responseKind)")]) |
175 | 172 |
|
176 | | -} |
| 173 | + return .init(kind: responseKind, isNoAckModeActivated: isNoAckModeActivated) |
| 174 | + } |
| 175 | + |
| 176 | + } |
177 | 177 |
|
178 | 178 | #endif |
0 commit comments