Skip to content

Commit a776c91

Browse files
committed
Add DebugClient to OGShims
1 parent b84178f commit a776c91

File tree

6 files changed

+183
-136
lines changed

6 files changed

+183
-136
lines changed

Sources/OpenGraphCxx/DebugServer/DebugServer.mm

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
#include <errno.h>
2929
#include <assert.h>
3030

31+
OG_EXTERN_C_BEGIN
32+
bool os_variant_has_internal_diagnostics(const char *subsystem);
33+
OG_EXTERN_C_END
34+
3135
// MARK: DebugServer public API Implementation
3236

3337
OG::DebugServer::DebugServer(OGDebugServerMode mode) {
@@ -260,7 +264,7 @@
260264
if (
261265
(mode & OGDebugServerModeValid)
262266
&& !OG::DebugServer::has_shared_server()
263-
/*&& os_variant_has_internal_diagnostics("com.apple.AttributeGraph")*/
267+
// && os_variant_has_internal_diagnostics("org.OpenSwiftUIProject.OpenGraph")
264268
) {
265269
_shared_server = new DebugServer(mode);
266270
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// interpose.c
3+
// OpenGraphCxx
4+
5+
#include "stdio.h"
6+
#include "stdbool.h"
7+
#include "string.h"
8+
9+
#define DYLD_INTERPOSE(_replacement,_replacee) \
10+
__attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee \
11+
__attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee };
12+
13+
extern bool os_variant_has_internal_diagnostics(const char *subsystem);
14+
15+
bool os_variant_has_internal_diagnostics_og_hook(const char *subsystem) {
16+
if (strcmp(subsystem, "org.OpenSwiftUIProject.OpenGraph") == 0) {
17+
return true;
18+
} else {
19+
return os_variant_has_internal_diagnostics(subsystem);
20+
}
21+
}
22+
23+
DYLD_INTERPOSE(os_variant_has_internal_diagnostics_og_hook, os_variant_has_internal_diagnostics)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//
2+
// DebugServerTests.swift
3+
// OpenGraphShims
4+
5+
#if canImport(Darwin)
6+
public import Foundation
7+
import Network
8+
9+
#if canImport(OpenGraphCxx_Private)
10+
public import OpenGraphCxx_Private.DebugServer
11+
#endif
12+
13+
@_spi(Debug)
14+
public final class DebugClient {
15+
private var connection: NWConnection?
16+
private let queue = DispatchQueue(label: "opengraph.debugserver.client.queue")
17+
18+
public func connect(to url: URL) async throws {
19+
guard let host = url.host, let port = url.port else {
20+
throw ClientError.invalidURL
21+
}
22+
23+
let nwHost = NWEndpoint.Host(host)
24+
let nwPort = NWEndpoint.Port(integerLiteral: UInt16(port))
25+
26+
connection = NWConnection(host: nwHost, port: nwPort, using: .tcp)
27+
28+
return try await withCheckedThrowingContinuation { continuation in
29+
connection?.stateUpdateHandler = { state in
30+
switch state {
31+
case .ready:
32+
continuation.resume()
33+
case let .failed(error):
34+
continuation.resume(throwing: error)
35+
case .cancelled:
36+
continuation.resume(throwing: ClientError.connectionCancelled)
37+
default:
38+
break
39+
}
40+
}
41+
connection?.start(queue: queue)
42+
}
43+
}
44+
45+
public func sendMessage(token: UInt32, data: Data) async throws {
46+
guard let connection else {
47+
throw ClientError.notConnected
48+
}
49+
let header = DebugServerMessageHeader(
50+
token: token,
51+
unknown: 0,
52+
length: numericCast(data.count),
53+
unknown2: 0
54+
)
55+
let headerData = withUnsafePointer(to: header) {
56+
Data(bytes: UnsafeRawPointer($0), count: MemoryLayout<DebugServerMessageHeader>.size)
57+
}
58+
try await send(data: headerData, on: connection)
59+
guard header.length > 0 else {
60+
return
61+
}
62+
try await send(data: data, on: connection)
63+
}
64+
65+
public func receiveMessage() async throws -> (header: DebugServerMessageHeader, data: Data) {
66+
guard let connection = connection else {
67+
throw ClientError.notConnected
68+
}
69+
let headerData = try await receive(
70+
length: MemoryLayout<DebugServerMessageHeader>.size,
71+
from: connection
72+
)
73+
let header = headerData.withUnsafeBytes { bytes in
74+
let buffer = bytes.bindMemory(to: UInt32.self)
75+
return DebugServerMessageHeader(
76+
token: buffer[0],
77+
unknown: buffer[1],
78+
length: buffer[2],
79+
unknown2: buffer[3]
80+
)
81+
}
82+
guard header.length > 0 else {
83+
return (header: header, data: Data())
84+
}
85+
let payloadData = try await receive(
86+
length: numericCast(header.length),
87+
from: connection
88+
)
89+
return (header: header, data: payloadData)
90+
}
91+
92+
public func disconnect() {
93+
connection?.cancel()
94+
connection = nil
95+
}
96+
97+
private func send(data: Data, on connection: NWConnection) async throws {
98+
return try await withCheckedThrowingContinuation { continuation in
99+
connection.send(content: data, completion: .contentProcessed { error in
100+
if let error {
101+
continuation.resume(throwing: error)
102+
} else {
103+
continuation.resume()
104+
}
105+
})
106+
}
107+
}
108+
109+
private func receive(length: Int, from connection: NWConnection) async throws -> Data {
110+
return try await withCheckedThrowingContinuation { continuation in
111+
connection.receive(minimumIncompleteLength: length, maximumLength: length) { data, _, isComplete, error in
112+
if let error {
113+
continuation.resume(throwing: error)
114+
} else if let data {
115+
continuation.resume(returning: data)
116+
} else {
117+
continuation.resume(throwing: ClientError.noDataReceived)
118+
}
119+
}
120+
}
121+
}
122+
}
123+
124+
enum ClientError: Error {
125+
case invalidURL
126+
case notConnected
127+
case connectionCancelled
128+
case noDataReceived
129+
}
130+
131+
#endif
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// DebugServerMessageHeader.swift
3+
// OpenGraphShims
4+
5+
@_spi(Debug)
6+
public struct DebugServerMessageHeader {
7+
public let token: UInt32
8+
public let unknown: UInt32
9+
public let length: UInt32
10+
public let unknown2: UInt32
11+
12+
public init(token: UInt32, unknown: UInt32, length: UInt32, unknown2: UInt32) {
13+
self.token = token
14+
self.unknown = unknown
15+
self.length = length
16+
self.unknown2 = unknown2
17+
}
18+
}

Tests/OpenGraphCompatibilityTests/Debug/DebugServerTests.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@ struct DebugServerTests {
1515
#expect(DebugServer.copyURL() == nil)
1616
}
1717

18-
// TODO: hook via private API of dyld
1918
// To make AG start debugServer, we need to pass internal_diagnostics check.
2019
// In debug mode, we can breakpoint on `_ZN2AG11DebugServer5startEj` and
2120
// executable `reg write w0 1` after `internal_diagnostics` call.
22-
// Or we can disable SIP on the target darwinOS and run `sudo sysctl kern.osvariant_status=xx` to workaround
23-
@Test(
24-
.disabled(if: compatibilityTestEnabled, "Skip on AG due to internal_diagnostics check"),
25-
)
21+
// Or we can disable SIP on the target darwinOS and run `sudo sysctl kern.osvariant_status=xx` to workaround.
22+
// Or you can add `breakpoint set -n os_variant_has_internal_diagnostics -C "thread return 1"`
23+
// to your lldbinit or run it before AGDebugServerStart call.
24+
@Test(.disabled(if: compatibilityTestEnabled, "Skip on AG on CI due to internal_diagnostics check"))
2625
func testMode1() throws {
2726
let _ = try #require(DebugServer.start(mode: [.valid]))
2827
let url = try #require(DebugServer.copyURL()) as URL
@@ -33,9 +32,7 @@ struct DebugServerTests {
3332
DebugServer.stop()
3433
}
3534

36-
@Test(
37-
.disabled(if: compatibilityTestEnabled, "Skip on AG due to internal_diagnostics check"),
38-
)
35+
@Test(.disabled(if: compatibilityTestEnabled, "Skip on AG on CI due to internal_diagnostics check"))
3936
func testMode3() throws {
4037
let _ = try #require(DebugServer.start(mode: [.valid, .networkInterface]))
4138
let url = try #require(DebugServer.copyURL()) as URL

Tests/OpenGraphCxxTests/DebugServer/DebugClient.swift

Lines changed: 0 additions & 127 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../Sources/OpenGraphShims/DebugClient.swift

0 commit comments

Comments
 (0)