1c4ef02aeSEvan Bacon/**
2c4ef02aeSEvan Bacon * Copyright (c) 2021 Expo, Inc.
3c4ef02aeSEvan Bacon * Copyright (c) 2018 Drifty Co.
4c4ef02aeSEvan Bacon *
5c4ef02aeSEvan Bacon * This source code is licensed under the MIT license found in the
6c4ef02aeSEvan Bacon * LICENSE file in the root directory of this source tree.
7c4ef02aeSEvan Bacon */
8c4ef02aeSEvan Baconimport Debug from 'debug';
9c4ef02aeSEvan Baconimport { Socket } from 'net';
10c4ef02aeSEvan Baconimport * as path from 'path';
11c4ef02aeSEvan Bacon
12c4ef02aeSEvan Baconimport { ServiceClient } from './ServiceClient';
13*8a424bebSJames Ideimport { GDBProtocolClient } from '../protocol/GDBProtocol';
14c4ef02aeSEvan Bacon
15c4ef02aeSEvan Baconconst debug = Debug('expo:apple-device:client:debugserver');
16c4ef02aeSEvan Bacon
17c4ef02aeSEvan Baconexport class DebugserverClient extends ServiceClient<GDBProtocolClient> {
18c4ef02aeSEvan Bacon  constructor(public socket: Socket) {
19c4ef02aeSEvan Bacon    super(socket, new GDBProtocolClient(socket));
20c4ef02aeSEvan Bacon  }
21c4ef02aeSEvan Bacon
22c4ef02aeSEvan Bacon  async setMaxPacketSize(size: number) {
23c4ef02aeSEvan Bacon    return this.sendCommand('QSetMaxPacketSize:', [size.toString()]);
24c4ef02aeSEvan Bacon  }
25c4ef02aeSEvan Bacon
26c4ef02aeSEvan Bacon  async setWorkingDir(workingDir: string) {
27c4ef02aeSEvan Bacon    return this.sendCommand('QSetWorkingDir:', [workingDir]);
28c4ef02aeSEvan Bacon  }
29c4ef02aeSEvan Bacon
30c4ef02aeSEvan Bacon  async checkLaunchSuccess() {
31c4ef02aeSEvan Bacon    return this.sendCommand('qLaunchSuccess', []);
32c4ef02aeSEvan Bacon  }
33c4ef02aeSEvan Bacon
34c4ef02aeSEvan Bacon  async attachByName(name: string) {
35c4ef02aeSEvan Bacon    const hexName = Buffer.from(name).toString('hex');
36c4ef02aeSEvan Bacon    return this.sendCommand(`vAttachName;${hexName}`, []);
37c4ef02aeSEvan Bacon  }
38c4ef02aeSEvan Bacon
39c4ef02aeSEvan Bacon  async continue() {
40c4ef02aeSEvan Bacon    return this.sendCommand('c', []);
41c4ef02aeSEvan Bacon  }
42c4ef02aeSEvan Bacon
43c4ef02aeSEvan Bacon  halt() {
44c4ef02aeSEvan Bacon    // ^C
45c4ef02aeSEvan Bacon    debug('Sending ^C to debugserver');
46c4ef02aeSEvan Bacon    return this.protocolClient.socket.write('\u0003');
47c4ef02aeSEvan Bacon  }
48c4ef02aeSEvan Bacon
49c4ef02aeSEvan Bacon  async kill() {
50c4ef02aeSEvan Bacon    debug(`kill`);
51c4ef02aeSEvan Bacon    const msg: any = { cmd: 'k', args: [] };
52c4ef02aeSEvan Bacon    return this.protocolClient.sendMessage(msg, (resp: string, resolve: any) => {
53c4ef02aeSEvan Bacon      debug(`kill:response: ${resp}`);
54c4ef02aeSEvan Bacon      this.protocolClient.socket.write('+');
55c4ef02aeSEvan Bacon      const parts = resp.split(';');
56c4ef02aeSEvan Bacon      for (const part of parts) {
57c4ef02aeSEvan Bacon        if (part.includes('description')) {
58c4ef02aeSEvan Bacon          // description:{hex encoded message like: "Terminated with signal 9"}
59c4ef02aeSEvan Bacon          resolve(Buffer.from(part.split(':')[1], 'hex').toString('ascii'));
60c4ef02aeSEvan Bacon        }
61c4ef02aeSEvan Bacon      }
62c4ef02aeSEvan Bacon    });
63c4ef02aeSEvan Bacon  }
64c4ef02aeSEvan Bacon
65c4ef02aeSEvan Bacon  // TODO support app args
66c4ef02aeSEvan Bacon  // https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#Packets
67c4ef02aeSEvan Bacon  // A arglen,argnum,arg,
68c4ef02aeSEvan Bacon  async launchApp(appPath: string, executableName: string) {
69c4ef02aeSEvan Bacon    const fullPath = path.join(appPath, executableName);
70c4ef02aeSEvan Bacon    const hexAppPath = Buffer.from(fullPath).toString('hex');
71c4ef02aeSEvan Bacon    const appCommand = `A${hexAppPath.length},0,${hexAppPath}`;
72c4ef02aeSEvan Bacon    return this.sendCommand(appCommand, []);
73c4ef02aeSEvan Bacon  }
74c4ef02aeSEvan Bacon
75c4ef02aeSEvan Bacon  async sendCommand(cmd: string, args: string[]) {
76c4ef02aeSEvan Bacon    const msg = { cmd, args };
77c4ef02aeSEvan Bacon    debug(`Sending command: ${cmd}, args: ${args}`);
78c4ef02aeSEvan Bacon    const resp = await this.protocolClient.sendMessage(msg);
79c4ef02aeSEvan Bacon    // we need to ACK as well
80c4ef02aeSEvan Bacon    this.protocolClient.socket.write('+');
81c4ef02aeSEvan Bacon    return resp;
82c4ef02aeSEvan Bacon  }
83c4ef02aeSEvan Bacon}
84