Author: Huang Junming, douyin small program front-end development engineer

This article describes the local and remote debugging technology used in the current douyin applet/game, and describes the overall operation process, including local debugging, remote debugging, small webView debugging and V8 debugging related core classes and implementation.

Basic concept

First of all it is worth noting that implements a set of debugging agreement with chrome V8 itself: chromedevtools. Making. IO/devtools – pr…

For example, if we make a breakpoint in Chrome Devtool, devtool will automatically generate the protocol message, and V8 will parse the protocol message to produce the corresponding feedback. Generate protocol messages to Chrome DevTool, and Chrome DevTool displays the corresponding variable information.

In the figure below, when the WS message is intercepted, you can see that the message generated in the protocol automatically generates the ID sequence number and method (green is the message from Chrome DevTool, red is the message from V8, And Chrome DevTool receives the message).

Local debugging

Small programs/small games in the internal package support local direct connection to USB for local debugging, in the remote debugging link mode is not configured, after the initial V8 engine, the default internal webSocket server will be opened in the local port 9229.

At the same time the websocket server needs to support/json (more can refer to: chromedevtools. Making. IO/devtools – pr… So That Chrome can find the debug in the Chrome ://inspect screen.

Adb forward TCP :9229 TCP :9229 is used to forward the port to the mobile device. In this case, we can access localhost:9229 and see the following data, which describes debugging links and other information.

[ { "description": "helium v8 inspect", "devtoolsFrontendUrl": "Chrome - devtools: / / devtools bundled/js_app HTML? Experiments 8 only = = true&v true&ws = 127.0.0.1:9229 / helium", "title" : "Helium", "id" : "helium", "type" : "node", "url" : "file://", "webSocketDebuggerUrl" : "ws: / / 127.0.0.1:9229 / helium"}]Copy the code

Open Chrome ://inspect to configure the appropriate port number to look for

Chrome ://inspect

Click inspect to open devtoolsFrontendUrl in the JSON structure

chrome-devtools://devtools/bundled/js_app.html? Experiments = 8 only true&v = true&ws = 127.0.0.1:9229 / heliumCopy the code

You can see that ws=127.0.0.1:9229/helium tells Chrome DevTool to connect to the WebSocket server. In this case, WebSocket Server receives the debugging protocol message from DevTool and sends it to V8. V8 generates the protocol message and sends the message to WebSocket Server to DevTool, thus creating the entire debugging channel.

Remote debugging

Local debugging is our internal debugging method, and using USB debugging is troublesome, in order to solve the developer debugging, we add a remote debugging logic, actually the principle is very simple, we pass a remote transfer WebSocket server, the client is no longer as a server, but as a client. Then the client connects to the remote server, and devtool connects to the remote server. Devtool sends breakpoint debugging information to the remote server, and the remote server passes this information to the V8 in the phone, thus forming a path.

In fact, for the tiktok mini program mini game scene, one of the roles is the developer tool IDE (ELECTRON), the IDE includes Chrome DevTool, the overall running process is as follows:

  • Developer development is complete, click developer tools real machine debugging
  • The developer tool connects to the remote server, obtains the debugging room ID, brings the room ID into the QR code information, and generates the QR code
  • The mobile phone with a small program scans the code, obtains the debugging room ID and connects to the remote server in the same room
  • The V8 on the device can then forward communication messages with Chrome DevTool through the remote server room, creating the entire debug path

Small program WebView debugging

For small program scenarios there is also a webView debugging, so we also need to support remote debugging, how to do it? Chrome ://inspect: The webView is debuggable with the debug setting enabled. The webView itself has a service on the phone, so as long as we connect to the service, we can send and receive messages to complete remote debugging.

As follows, Chrome for Android actually turns on the Unix Domain Socket.

So just connect to them, and then add another WebSocket client that will forward DOM debugging information we received from the original remote WebSocket client to that Unix Domain socket. The client receives a message from the Unix Domain socket and forwards it to the remote server. In the end, devtool receives debugging information about the WebView DOM.

As you can see in the diagram above, the WebSocket Client (WebView) and webView Unix Socket modules are added compared to the previous section. Through their forward communication, we implement the page elements of remote debugging applet.

V8 debugging module

The following explains some of the core classes that the V8 debug module accesses. contains V8_inspector: : V8InspectorClient, v8_inspector: : V8Inspector, v8_inspector: : V8InspectorSession, v8_inspector: : V8Inspector: : Ch Annel et al., interested in accessing V8 debugging module services can be taken as a reference. The overall workflow is as follows:Example code:

// Create a Channel // implement a Channel // class ChannelImpl Final: public v8_inspector::V8Inspector::Channel v8_inspector::V8Inspector::Channel channel_ = new ChannelImpl(); v8_inspector::StringView view( ... ) / / create the session / / connect the inspector and the channel v8_inspector: : V8InspectorSession session_ = inspector_. Connect (1, channel, view); V8_inspector ::StringView ctx_name(/*ctx_name*/) Inspector_ ->contextCreated(, v8_inspector::V8ContextInfo(context, 1, ctx_name);Copy the code

v8_inspector::V8InspectorClient

class V8_EXPORT V8InspectorClient {
 public:
  virtual ~V8InspectorClient() = default;

  virtual void runMessageLoopOnPause(int contextGroupId) {}
  virtual void quitMessageLoopOnPause() {}
};
Copy the code

We need to implement runMessageLoopOnPause and quitMessageLoopOnPause

  • runMessageLoopOnPause

When a breakpoint occurs, V8 will trigger runMessageLoopOnPause. At this point, the user needs to synchronize the consumption of all devTool messages. We need to enable our own messageLoop to consume the messages. Otherwise, all messages sent by devTool will be lost, and the console will not respond without the context information

  • quitMessageLoopOnPause

When the breakpoint ends, quitMessageLoopOnPause is automatically triggered, and we can stop MessageLoop without continuing to consume messages synchronously

v8_inspector::V8Inspector

The class is created using two parameters: one is the ISOLATE, and the other is the V8InspectorClient instance. First of all, one v8Inspector is an ISOLATE. An ISOLATE has multiple contexts. So one inspector can debug multiple contexts.

v8_inspector::V8Inspector::Channel
// Connection.
  class V8_EXPORT Channel {
   public:
    virtual ~Channel() = default;
    virtual void sendResponse(int callId,
                              std::unique_ptr<StringBuffer> message) = 0;
    virtual void sendNotification(std::unique_ptr<StringBuffer> message) = 0;
    virtual void flushProtocolNotifications() = 0;
 };
Copy the code

Channel is a channel with v8Inspector, you can get the protocol message generated inside V8, you need to do things to forward it to DevTool, the main need to implement sendResponse and sendNotification, It’s as simple as simply forwarding the message to devtool

void sendResponse(int callId, std::unique_ptr<v8_inspector::StringBuffer> message) override {
    sendToDevtool(message->string());
}
void sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message) override {
    sendToDevtool(message->string());
}
Copy the code

v8_inspector::V8InspectorSession

A debug session is generated by connecting channel and inspect. This session is used to receive devTool messages and send them to V8 via the dispatchProtocolMessage method to drive v8 breakpoints. V8 then calls the channel method and communicates the internal protocol message, and the link is routed.

std::unique_ptr<StringBuffer> &str = messages.front();
session->dispatchProtocolMessage(str->string());
Copy the code

The resources

  • V8. Dev/docs/inspec…
  • Chromedevtools. Making. IO/devtools – pr…
  • medium.com/@hyperandro…