I have written three articles about VS Code (startup process, installation, development and debugging) recently, and I will continue to publish them in the research and learning process, hoping to provide some help to those who are interested.

In general, my article style is to continue to explore the underlying principles, break through the pot and ask to the end, until the depth of digging can be understood by the public. Therefore, WHEN making introductions, I will occasionally run to talk about other related technologies, mainly to pave the way for readers to understand.

We often use Chrome to debug JS breakpoints. Have you ever wondered why the program stops when I hit a breakpoint, why VS Code can break without Chrome Devtool, and how it is implemented? That’s what the text is trying to get you to understand.

Node. Js debug

VS Code allows you to debug many languages by installing the corresponding debugging package. In this article, node. js is taken as an example, so you can try to debug other languages by following the example.

First, let’s take a look at how Node.js is debugged. In fact, this blog has covered it several times, and this time we’ll take a closer look at it.

Connect to the Node.js Debugger

Before version 6.3, node. js used a single tool called Node-Inspector, which has a very active community. You can use a few lines of code to create a single Node – Inspector. Here is the code to be debugged:

// file: test.jslet 
http = require('http'); const server = http.createServer((req, res) => { res.write('hello\n');  setTimeout(() => {    debugger;    res.end('world'); }, 0); }); server.listen(8888, () => { console.log('start server'); });Copy the code

With the node –debug test.js launcher, we start an HTTP Server with port 8888.

In this step, use NVM to change the node version to a version lower than 6.3, such as V6.2.2. The differences between nodes of earlier versions and v6.3+ versions will be explained later.

Then make the request as follows:

➜ curl 127.0.0.1:8888Copy the code

You will see that the command line only prints hello\n and then enters the pending state. Let’s understand why this is so:

  • through--debugThe node.js parameter starts, and the application opens a built-inDebuggerThe module
  • Since we didn’t specify a parameter,--debug=PORTBy default, port 5858 is usedDebuggerThe module listens on port 5858
  • At the time of making the request, encountereddebuggerKeyword, the program suspends execution until it receives a “continue to the next step” command

Barretlee/Node-legacy-debug

We can write a program to see what the Debugger module does:

// File: debugClient.jsconst net = require('net'); const socket = net.createConnection(5858,'127.0.0.1'); socket.on('data', (data) => { console.log(data.toString())}); process.stdin.on('data', (data) => {  const json = data.toString();  const msg = `Content-Length: ${Buffer.byteLength(json, 'utf8')}\r\n\r\n${json}`;  socket.write(msg, 'utf8'); });Copy the code

Through the Net module, we connect to the listener that the program just enabled on port 5858. When we first connect, it will print the following message:

Type: connectv8-version: 5.0.71.52 protocol-version: 1Embedding-Host: node v6.2.2Content-Length: 0Copy the code

You are trying to connect to the Debugger module, the connection is successful, the current debug program is using the V8 version 5.0.71.52, the protocol version is 1, the Node version is v6.2.2, this message is telling you, What Protocol should be used to communicate with the Debugger? Version 6.2.2 uses the V8 Debugger Protocol.

To print comprehensive information, use the debug module to start the test.js program, then start the debugclient. js program, and then run the curl command to send a request.

When curl initiates a request, the Debugger sends a message to the debugClient:

Content-Length: 283
{  "seq": 0."type": "event"."event": "break"."body": {    "invocationText": "#<Timeout>._onTimeout()"."sourceLine": 9,    "sourceColumn": 4."sourceLineText": " debugger;"."script": {      "id": 59."name": "/path/to/test.js"."lineOffset": 0."columnOffset": 0."lineCount"18}}} :Copy the code

The curl is pending, and the Debugger is told to go to the next breakpoint:

{"seq": 1,"type":"request"."command":"continue"}Copy the code

In the debugClient we are listening for process.stdin, so you can paste the above content into the command line and press enter, and you will see the feedback from the Debugger:

{"seq": 1,"request_seq": 1,"type":"response"."command":"continue"."success":true."running":true}Copy the code

Curl has successfully entered the next breakpoint. At this point, you will see that curl has output the world.

In the above program, you can enter other commands from the command line, such as querying the url value of req while paused:

{"seq": 2."type":"request"."command":"evaluate"."arguments": {"expression":"req.url"}}Copy the code

You will receive the following reply:

Content-Length: 177
{  "seq": 3."request_seq": 2."type": "response"."command": "evaluate"."success": true."body": {    "handle": 150,    "type": "string"."value": "/"."length": 1,    "text": "/"  },  "refs": []."running": false}Copy the code

If you already have a picture in your mind of debugging Node.js, yes, that’s how it works.

Node-inspector, debug agent

The node-inspector is used to debug a program, but it is difficult to debug the program in this way. You need to know not only the names of the instructions, but also the parameters of the instructions, the protocol specification, the sequence count, and so on. It helps us visually debug Node.js applications in Chrome Devtool, so what does it do?





Simply put, Node Inspector channels Chrome Devtool via Chrome’s Remote Debugging Protocol. The V8 Debugging Protocol is then connected to the Debugger module of the application, so developers can debug Node.js visually in Chrome, so I call it a “debug agent”, which is a Protocol transfer service.

Since Node-Inspector greatly improves the debugging experience for Nodes, it was directly incorporated into Node.js in V6.3. You’ll see that when debugging in Node.js using V6.3 +, it prints a webscoket link adapted to the CRDP protocol:

➜ node - inspect test. JsDebugger listening on the ws: / / 127.0.0.1:9229 / db309268 Abe - 623 - a - 4 - b19a - c4407ed8998d Forhelp see https://nodejs.org/en/docs/inspectorCopy the code

We can directly configure this address in Chrome Devtool to enable visual debugging, so the entire link becomes:






Without debugging agents, developers are much easier to use, and the CRDP specification is used frequently in the community, with a lot of code to refer to.

At this point, if we want to implement a visual debugging interface ourselves, isn’t it a little clear:



You only need to implement the Debug Client in the following figure and link the Debug Client with the IDE view to achieve custom visual debugging.

If you need to customize visual debugging of Node.js, this article will end here, and you will be able to implement a debugging interface using what you have learned above. But in VS Code, there’s a need for more space.

Debug Adaptor Protocol

It is very expensive to implement a Debug Client. You need to understand all the Debugging protocols, such as V8 Debugging Protocol. There are dozens of commands, each of which requires communication adaptation and UI adaptation, and this is just one language. You will need to adapt to multiple debugging protocols, which can be quite different from one protocol to another, and it will completely crash you.

On the other hand, from a community perspective, this construction can be abstractable. If Atom debugging node.js requires implementing a Debug Client, Sublime Text does this, VS Code does this, and your own IDE does the same. Hence the Debug Adaptor Protocol, a set of generic debugging protocols developed by Microsoft that has become the de facto standard in the community.

At what level is this DAP being debugged? We can take a look at this image (source) :

Implement only one DAP protocol communication and UI adaptation on the IDE, you can debug all the languages, you need to do only:

  • Does the community have a DAP implementation for the target language? If so, take it and use it directly. It can be quickly adapted and debugged
  • If not, use the knowledge we have learned above to implement a DA and contribute to the community

This set of protocols specifies five elements:

  • Base Protocol, describes the request, response, event, error and other communication formats
  • Events, describes initialization, complete configuration, output, breakpoint, stop and more than a dozen event standards
  • Request, describes the request format of various instructions for debugging
  • Response, describes the response format of various instructions for debugging
  • Types, describes the types and interface descriptions involved in the above

In principle, everything mentioned in the specification needs to be implemented in DA, even if the underlying engine of the language does not have this capability, an error should be thrown to ensure consistency.

Implement a Debugger for Node.js

In order to understand the protocols mentioned above, I wrote a DEMO that implemented the simplest operation: show the current Debug stack, as shown in the following figure:

The storage address is: Barretlee/Node-debug

You can download it and run it, download the dependency and start it:

npm run init; npm run start; Open http://127.0.0.1:4445Copy the code

The Demo was very low in completion, and the key links were all mocks, just to help me understand the whole process. After understanding, I didn’t continue to improve the details. Interested students can study it.

I did not implement a complete Node.js Debug Adaptor myself, not because it is complicated, but directly to study VS Code open source two Adaptor, respectively:

  • Vscode -node-debug, this is the implementation of v6.3 and below
  • Vscode-node-debug2, this is the implementation of V6.3 and above

Depending on the Embedding-Host information returned at connection time, VS Code seems to install both packages by default. The whole idea is the same as I mentioned above.

Specific implementation of VS Code

There are two ways to debug programs: one is to debug programs that have been started, and the other is to debug programs that have not been started. VS Code will attach the program directly to the former, and the latter will fork a process and launch the program. Here is a brief introduction to the actual operation of VS Code:

The program starts without the debug parameter

If node.js is started without –debug or –inspect, the Node.js Debugger module will not start by default. In this case, debugging is not possible.

# to find the corresponding Node. Js process PID ➜ ps - aux | grep 'Node' # to send the PID SIGUSR1 signal ➜ kill SIGUSR1 NODE_PIDCopy the code

stopOnEntry

When forking a Node.js process, there are two types of input arguments:

  • --debug--inspect, the default execution program
  • --debug-brk--inspect-brkDefaults to a direct breakpoint on the first line

Generally speaking, we will choose the first method, if your program will execute directly, fast, can consider the second method for debugging.

How VS Code connects to the Node.js debugger

Remote Debugging Protocol for Node.js is implemented in the vscode-chrome-debug-core package.

There’s a lot of internal logic, and it looks a bit labored

.

summary

Ok, first write here, there are a lot of details will not be described, not much value, write this article to do a lot of preliminary work, DAP and Node.js Debugger research for several nights, but finally is to understand more, if you encounter any questions in the test, welcome to leave a message below.