Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Commit b4b1c0d

Browse files
Fatme HavaluovaFatme Havaluova
Fatme Havaluova
authored and
Fatme Havaluova
committed
Fix gdb server
1 parent bbbfdb6 commit b4b1c0d

File tree

3 files changed

+120
-35
lines changed

3 files changed

+120
-35
lines changed

definitions/mobile.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ declare module Mobile {
262262

263263
interface IGDBServer {
264264
run(argv: string[]): IFuture<void>;
265-
kill(bundleExecutableName: string): IFuture<void>;
265+
kill(argv: string[]): IFuture<void>;
266+
destory(): void;
266267
}
267268

268269
interface INotificationProxyClient {

mobile/ios/ios-application-manager.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as iOSProxyServices from "./ios-proxy-services";
99

1010
export class IOSApplicationManager implements Mobile.IDeviceApplicationManager {
1111
private uninstallApplicationCallbackPtr: NodeBuffer = null;
12+
private _gdbServer: Mobile.IGDBServer = null;
1213

1314
constructor(private device: Mobile.IiOSDevice,
1415
private devicePointer: NodeBuffer,
@@ -80,14 +81,12 @@ export class IOSApplicationManager implements Mobile.IDeviceApplicationManager {
8081
public stopApplication(applicationId: string): IFuture<void> {
8182
let application = this.getApplicationById(applicationId);
8283
let gdbServer = this.createGdbServer();
83-
return gdbServer.kill(application.CFBundleExecutable);
84+
return gdbServer.kill([`${application.Path}`]);
8485
}
8586

8687
public restartApplication(applicationId: string): IFuture<void> {
8788
return (() => {
88-
// This must be executed in separate process because ddb sometimes fails and the cli crashes.
89-
this.$childProcess.spawnFromEvent(`${process.argv[0]}`,
90-
[`${process.argv[1]}`, "device", "stop", `${applicationId}`, `${this.$devicePlatformsConstants.iOS}`], "close", { stdio: 'inherit' }).wait();
89+
this.stopApplication(applicationId).wait();
9190
this.runApplicationCore(applicationId).wait();
9291
}).future<void>()();
9392
}
@@ -119,17 +118,23 @@ export class IOSApplicationManager implements Mobile.IDeviceApplicationManager {
119118
}
120119

121120
private runApplicationCore(applicationId: any) {
121+
if (this._gdbServer) {
122+
this._gdbServer.destory();
123+
this._gdbServer = null;
124+
}
125+
122126
let application = this.getApplicationById(applicationId);
123127
let gdbServer = this.createGdbServer();
124-
return gdbServer.run([`${application.Path}/${application.CFBundleExecutable}`]);
128+
return gdbServer.run([`${application.Path}`]);
125129
}
126130

127131
private createGdbServer(): Mobile.IGDBServer {
128-
let service = this.device.startService(iOSProxyServices.MobileServices.DEBUG_SERVER);
129-
let socket = this.$hostInfo.isWindows ? service : new net.Socket({ fd: service });
130-
let gdbServer = this.$injector.resolve(GDBServer, { socket: socket });
131-
132-
return gdbServer;
132+
if(!this._gdbServer) {
133+
let service = this.device.startService(iOSProxyServices.MobileServices.DEBUG_SERVER);
134+
let socket = this.$hostInfo.isWindows ? service : new net.Socket({ fd: service });
135+
this._gdbServer = this.$injector.resolve(GDBServer, { socket: socket });
136+
}
137+
return this._gdbServer;
133138
}
134139

135140
private getApplicationById(applicationId: string): Mobile.IDeviceApplication {

mobile/ios/ios-core.ts

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,11 +1085,15 @@ class GDBSignalWatcher extends stream.Writable {
10851085
}
10861086

10871087
export class GDBServer implements Mobile.IGDBServer {
1088+
private okResponse = "$OK#";
1089+
private isInitilized = false;
1090+
10881091
constructor(private socket: any, // socket is fd on Windows and net.Socket on mac
10891092
private $injector: IInjector,
10901093
private $hostInfo: IHostInfo,
10911094
private $options: ICommonOptions,
1092-
private $logger: ILogger) {
1095+
private $logger: ILogger,
1096+
private $errors: IErrors) {
10931097
if(this.$hostInfo.isWindows) {
10941098
let winSocket = this.$injector.resolve(WinSocket, {service: this.socket, format: 0});
10951099
this.socket = {
@@ -1098,60 +1102,135 @@ export class GDBServer implements Mobile.IGDBServer {
10981102
}
10991103
};
11001104
}
1105+
1106+
this.socket.on("close", (hadError: boolean) => this.$logger.trace("GDB socket get closed. HadError", hadError.toString()));
11011107
}
11021108

1103-
public init(): IFuture<void> {
1109+
public init(argv: string[]): IFuture<void> {
11041110
return (() => {
1105-
this.send("QStartNoAckMode");
1106-
this.socket.write("+");
1107-
this.send("QEnvironmentHexEncoded:");
1108-
this.send("QSetDisableASLR:1");
1111+
if (!this.isInitilized) {
1112+
this.awaitResponse("QStartNoAckMode", "+").wait();
1113+
this.sendCore("+");
1114+
this.awaitResponse("QEnvironmentHexEncoded:").wait();
1115+
this.awaitResponse("QSetDisableASLR:1").wait();
1116+
let encodedArguments = _.map(argv, (arg, index) => util.format("%d,%d,%s", arg.length * 2, index, this.toHex(arg))).join(",");
1117+
this.awaitResponse("A" + encodedArguments).wait();
1118+
1119+
this.isInitilized = true;
1120+
}
11091121
}).future<void>()();
11101122
}
11111123

11121124
public run(argv: string[]): IFuture<void> {
11131125
return (() => {
1114-
this.init().wait();
1126+
this.init(argv).wait();
11151127

1116-
let encodedArguments = _.map(argv, (arg, index) => util.format("%d,%d,%s", arg.length * 2, index, this.toHex(arg))).join(",");
1117-
this.send("A" + encodedArguments);
1118-
1119-
this.send("qLaunchSuccess");
1128+
this.awaitResponse("qLaunchSuccess").wait();
11201129

11211130
if(this.$hostInfo.isWindows) {
11221131
this.send("vCont;c");
11231132
} else {
11241133
if (this.$options.justlaunch) {
11251134
// Disconnecting the debugger closes the socket and allows the process to quit
1126-
this.send("D");
1135+
this.sendCore(this.encodeData("vCont;c"));
11271136
} else {
11281137
this.socket.pipe(new GDBStandardOutputAdapter()).pipe(process.stdout);
11291138
this.socket.pipe(new GDBSignalWatcher());
1130-
this.send("vCont;c");
1139+
this.sendCore(this.encodeData("vCont;c"));
11311140
}
11321141
}
1133-
this.socket.destroy();
11341142
}).future<void>()();
11351143
}
11361144

1137-
public kill(bundleExecutableName?: string): IFuture<void> {
1145+
public kill(argv: string[]): IFuture<void> {
11381146
return (() => {
1139-
this.init().wait();
1147+
this.init(argv).wait();
1148+
1149+
this.awaitResponse("\x03", "thread", () => this.sendx03Message()).wait();
1150+
this.send("k").wait();
1151+
}).future<void>()();
1152+
}
11401153

1141-
let bundleExecutableNameHex = this.toHex(bundleExecutableName);
1142-
this.send(`vAttachName;${bundleExecutableNameHex}`);
1143-
this.send("k");
1144-
this.socket.destroy();
1154+
public destory(): void {
1155+
this.socket.destroy();
1156+
}
1157+
1158+
private awaitResponse(packet: string, expectedResponse?: string, getResponseAction?: () => IFuture<string>): IFuture<void> {
1159+
return (() => {
1160+
expectedResponse = expectedResponse || this.okResponse;
1161+
let actualResponse = getResponseAction ? getResponseAction.apply(this, []).wait() : this.send(packet).wait();
1162+
if (actualResponse.indexOf(expectedResponse) === -1 || _.startsWith(actualResponse, "$E")) {
1163+
this.$logger.trace(`GDB: actual response: ${actualResponse}, expected response: ${expectedResponse}`);
1164+
this.$errors.failWithoutHelp(`Unable to send ${packet}.`);
1165+
}
11451166
}).future<void>()();
11461167
}
11471168

1148-
private send(packet: string): void {
1149-
let data = this.createData(packet);
1150-
this.$logger.trace(`GDB: sending ${data}`);
1169+
private send(packet: string): IFuture<string> {
1170+
let future = new Future<string>();
1171+
1172+
let dataCallback = (data: any) => {
1173+
this.$logger.trace(`GDB: read packet: ${data}`);
1174+
this.socket.removeListener("data", dataCallback);
1175+
if (!future.isResolved()) {
1176+
future.return(data.toString());
1177+
}
1178+
};
1179+
1180+
this.socket.on("data", dataCallback);
1181+
this.socket.on("error", (error: string) => {
1182+
if (!future.isResolved()) {
1183+
future.throw(new Error(error));
1184+
}
1185+
});
1186+
1187+
this.sendCore(this.encodeData(packet));
1188+
1189+
return future;
1190+
}
1191+
1192+
private sendCore(data: string): void {
1193+
this.$logger.trace(`GDB: send packet ${data}`);
11511194
this.socket.write(data);
11521195
}
11531196

1154-
private createData(packet: string): string {
1197+
private sendx03Message(): IFuture<string> {
1198+
let future = new Future<string>();
1199+
let retryCount = 3;
1200+
let isDataReceived = false;
1201+
1202+
let dataCallback = (data: any) => {
1203+
let dataAsString = data.toString();
1204+
if (dataAsString.indexOf("thread") > -1) {
1205+
isDataReceived = true;
1206+
this.socket.removeListener("data", dataCallback);
1207+
future.return(data.toString());
1208+
}
1209+
};
1210+
1211+
this.socket.on("data", dataCallback);
1212+
this.sendCore("\x03");
1213+
1214+
let timer = setInterval(() => {
1215+
this.sendCore("\x03");
1216+
retryCount--;
1217+
1218+
let secondTimer = setInterval(() => {
1219+
if (isDataReceived || !retryCount) {
1220+
clearInterval(secondTimer);
1221+
clearInterval(timer);
1222+
}
1223+
1224+
if (!retryCount) {
1225+
future.throw(new Error("Unable to kill the application."));
1226+
}
1227+
}, 1000);
1228+
}, 1000);
1229+
1230+
return future;
1231+
}
1232+
1233+
private encodeData(packet: string): string {
11551234
let sum = 0;
11561235
for(let i = 0; i < packet.length; i++) {
11571236
sum += getCharacterCodePoint(packet[i]);

0 commit comments

Comments
 (0)