14
14
15
15
import ArgumentParser
16
16
import GDBRemoteProtocol
17
+ import Logging
17
18
import NIOCore
18
19
import NIOPosix
20
+ import SystemPackage
19
21
import WasmKitGDBHandler
20
22
23
+ #if hasFeature(RetroactiveAttribute)
24
+ extension Logger . Level : @retroactive ExpressibleByArgument { }
25
+ extension FilePath : @retroactive ExpressibleByArgument {
26
+ public init ? ( argument: String ) {
27
+ self . init ( argument)
28
+ }
29
+ }
30
+ #else
31
+ extension Logger . Level : ExpressibleByArgument { }
32
+ extension FilePath : ExpressibleByArgument {
33
+ public init ? ( argument: String ) {
34
+ self . init ( argument)
35
+ }
36
+ }
37
+ #endif
38
+
21
39
@main
22
- struct Entrypoint : ParsableCommand {
40
+ struct Entrypoint : AsyncParsableCommand {
23
41
@Option ( help: " TCP port that a debugger can connect to " )
24
42
var port = 8080
25
43
26
- func run( ) throws {
44
+ @Option ( name: . shortAndLong)
45
+ var logLevel = Logger . Level. info
46
+
47
+ @Argument
48
+ var wasmModulePath : FilePath
49
+
50
+ func run( ) async throws {
51
+ let logger = {
52
+ var result = Logger ( label: " org.swiftwasm.WasmKit " )
53
+ result. logLevel = self . logLevel
54
+ return result
55
+ } ( )
56
+
27
57
let group = MultiThreadedEventLoopGroup ( numberOfThreads: System . coreCount)
28
58
let bootstrap = ServerBootstrap ( group: group)
29
59
// Specify backlog and enable SO_REUSEADDR for the server itself
@@ -38,9 +68,8 @@ struct Entrypoint: ParsableCommand {
38
68
// make sure to instantiate your `ChannelHandlers` inside of
39
69
// the closure as it will be invoked once per connection.
40
70
try channel. pipeline. syncOperations. addHandlers ( [
41
- ByteToMessageHandler ( GDBHostCommandDecoder ( ) ) ,
71
+ ByteToMessageHandler ( GDBHostCommandDecoder ( logger : logger ) ) ,
42
72
MessageToByteHandler ( GDBTargetResponseEncoder ( ) ) ,
43
- WasmKitGDBHandler ( ) ,
44
73
] )
45
74
}
46
75
}
@@ -49,11 +78,38 @@ struct Entrypoint: ParsableCommand {
49
78
. childChannelOption ( . socketOption( . so_reuseaddr) , value: 1 )
50
79
. childChannelOption ( . maxMessagesPerRead, value: 16 )
51
80
. childChannelOption ( . recvAllocator, value: AdaptiveRecvByteBufferAllocator ( ) )
52
- let channel = try bootstrap. bind ( host: " 127.0.0.1 " , port: port) . wait ( )
81
+
82
+ let serverChannel = try await bootstrap. bind ( host: " 127.0.0.1 " , port: port) { childChannel in
83
+ childChannel. eventLoop. makeCompletedFuture {
84
+ try NIOAsyncChannel < GDBPacket < GDBHostCommand > , GDBTargetResponse > (
85
+ wrappingChannelSynchronously: childChannel
86
+ )
87
+ }
88
+ }
53
89
/* the server will now be accepting connections */
54
- print ( " listening on port \( port) " )
90
+ logger. info ( " listening on port \( port) " )
91
+
92
+ let debugger = try WasmKitDebugger ( logger: logger, moduleFilePath: self . wasmModulePath)
93
+
94
+ try await withThrowingDiscardingTaskGroup { group in
95
+ try await serverChannel. executeThenClose { serverChannelInbound in
96
+ for try await connectionChannel in serverChannelInbound {
97
+ group. addTask {
98
+ do {
99
+ try await connectionChannel. executeThenClose { connectionChannelInbound, connectionChannelOutbound in
100
+ for try await inboundData in connectionChannelInbound {
101
+ // Let's echo back all inbound data
102
+ try await connectionChannelOutbound. write ( debugger. handle ( command: inboundData. payload) )
103
+ }
104
+ }
105
+ } catch {
106
+ logger. error ( " Error in GDB remote protocol connection channel " , metadata: [ " error " : " \( error) " ] )
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
55
112
56
- try channel. closeFuture. wait ( ) // wait forever as we never close the Channel
57
- try group. syncShutdownGracefully ( )
113
+ try await group. shutdownGracefully ( )
58
114
}
59
115
}
0 commit comments