Last Updated Address

debugging

Why debug

Webpack source code is still around, all kinds of callback, recursive and asynchronous jump around, easy to get lost with the code at the beginning. Console. log is a little weak for complex processes, and debugging simple code is fine, but when it comes to asynchronous methods and recursive calls, log-only printouts are hard to understand. It’s important to be flexible with debugging tools when looking at code that requires you to drill down into sub-processes or ignore details to see the whole picture. Of course, the necessary console.log is also required, requiring us to choose the appropriate tool for the situation.

Here we use vscode own debugging tools to track code, basically simple configuration of the box, very easy to use, based the breakpoint function is a good way to make the code execution process control in their own hands, the call stack function can help us keep the context, the variable view functions cooperate can make us at any time in the call stack switch, Let’s first familiarize ourselves with the use of these features.

Run the demo. Js

A little bug icon in the sidebar is the debug panel. The debug console terminal at the bottom can output debugging information and run the code from the debug panel. (Image.jpg)

Start by creating a project folder called debugger, create a demo.js file in the root directory, and simply print out the start command parameters

// demo.js
function printArgv() {
    console.log(process.argv);
}
printArgv()
Copy the code

Next, create the.vscode folder under the root directory and create the launch.json file (this step can be automatically generated by VScode).

/ / launch. Json {" version ":" 0.2.0, "" configurations: [{" type" : "node", "request" : "launch", "name" : "Start the program," "skipFiles" : [" < node_internals > / * * "], "the program" : "${workspaceFolder} / demo. Js", "args" : ["-o", "bundle.js"] } ] }Copy the code

Once created, you can click the Start program button in the debug panel. On success, a statement similar to the following will be printed in the debug console

Array(4) ["/usr/local/bin/node", "~/Desktop/debugger/demo.js", "-o", "bundle.js"]

Basic operation

Now let’s put a breakpoint on demo.js, just put a red dot in front of the line. When we start running (F5), we’ll see the code stop at printArgv(), and the debug panel on the left is enriched.

// demo.js
    function printArgv() {
        console.log(process.argv); / / the breakpoint
    }
    this.count = 9
    printArgv() / / the breakpoint
Copy the code

This is one of the most important debugging functions. It is very simple and intuitive. We will use this content repeatedly in the following use:

  • Local is the current context, which contains the common this object and the custom printArgv function, etc. In addition, moving the mouse over the code can quickly browse to the corresponding variable details.
  • Global is a Global context, which includes Global objects and eval functions.

The call stack

Program tasks are performed by a set of functions calling each other, and the call stack records the call relationships. A function calls another function to produce a push operation, and the topmost function is the currently running function. The top level function returns to the calling function after execution to generate the stack operation.

Click the single step debug button (F11) and the code continues down to console.log(process.argv); If you look at the call stack panel, you’ll see printArgv at the top, which is the currently running function, and demo.js 2:5, which is currently running on line 2 of the demo file, as well as the current stack context in the variables panel.

In line 5 of demo.js, we call printArgv (anonymous function). In line 5 of demo.js, we call printArgv (anonymous function). We wrote demo.js to run in an anonymous function.

If we click on the second line of the call stack panel, we can see that the editor is back to where printArgv() was called and highlighted in green, and that the context variable in the variables panel is also switched to the context of the current line. This gives us a very powerful way to go back to the runtime environment, especially if the call stack is very deep, and it makes it very easy to go back to the previous content.

skipFiles

To be deflected or skipped by ‘skipFiles’. To be deflected or skipped. To be deflected or skipped by ‘skipFiles’. [”

/**”], here we tell the debugger to hide the core code of Node. We don’t care how node is started or which underlying code is called to run console.log, so we added this configuration item to ignore the call stack that doesn’t help us very much. The most important thing is that when you click step, the code doesn’t go into files that are ignored, which makes debugging much easier.

Next we create a new file print_util.js to test the ignore file and add ${workspaceFolder}/print_util.js to the skipFiles of launch.json

// print_util.js
function printArgv() {
    console.log(process.argv); / / the breakpoint
}
module.exports = printArgv

// demo.js
const printArgv = require('./print_util')
printArgv() / / the breakpoint
Copy the code

Running the program shows that the code does not go to the print_util.js file, and the breakpoints panel does not show breakpoints in that file.

Eval and loaded scripts

In addition to executing our written JS files directly, the JS runtime can also execute string code through eval().

// demo.js
function printArgv() {
    eval('console.log(process.argv); ') / / the breakpoint
}
printArgv() / / the breakpoint
Copy the code

Debugging executes the above statement, and after two successive steps, we see that the console does not print and end normally, but that another anonymous function appears in the call stack, located in VMxxx 1:1.

When eval is executed, the virtual machine “creates” a JS file whose contents are the parameters passed into eval. Since it is a JS file, it executes as an anonymous function.

In the loaded script panel, we can see three things: In demo.js,

and

, we can find the file created by the virtual machine. When we open it, we can see the parameter console.log(process.argv); .

The asynchronous call

First add ‘neo-async’ dependency yarn add neo-async

// demo.js
const async = require('neo-async')
var tasks = [
    function(done) {
        console.log('task1 run') / / breakpoint 1
        setTimeout(function() {
            done(null.'task1'); 2 / / points
        }, 1000);
    },
    function(done) {
        console.log('task2 run') / / breakpoint 3
        setTimeout(function() {
            done(null.'task2'); / / breakpoint 4
        }, 2000); },];console.time('task')
async.parallel(tasks, function(err, res) { / / breakpoint 5
  console.timeEnd('task')
  console.log(res); / / breakpoint 6
});
Copy the code

After the command is executed, the console prints the following:

task1 run
task2 run
[ 'task1'.'task2'] task: 2003.954 msCopy the code

Parallel executes two tasks, task1 and task2, at the same time, and starts two delay tasks in the task. Finally, wait 2 seconds for all tasks to complete, and print the results by backtracking.

To avoid entering the neo-async code implementation, add node_modules to skipFiles: “${workspaceFolder}/node_modules/**”. Then run debugging in the following order:

  • Node core running (ignored)
  • Demo.js executes as anonymous functions
  • Async. Parallel execution (ignored)
  • Task1 console. The log ()
  • Task2 console. The log ()
  • Node core Timer starts, wait 1 second (ignored)
  • task1.done
  • Node core Timer starts, wait 1 second (ignored)
  • task2.done
  • Async. Callback execution (ignored)
  • The Tasks callback executes

This gives us a clear view of the flow of the asynchronous function. If you remove the above skipFiles, the debugging process will enter async source code, easy to interfere with our analysis. If the point is not broken in the last callback, then the next debugger will not enter the last callback and will simply end. So asynchronous function breakpoints where we need to play according to the actual situation, according to the source focus you want to focus on analysis.

Debugging webpack

Understand how to use debugging, is to better help us track the source code, the following webpack source code to see how to use debugging tools to help us sort out the order

  1. Clone webpack from github and switch to webPack-4 tag.

  2. Install dependencies using YARN Install

  3. Add the vscode debug environment launch.json, change the configuration item program to “${workspaceFolder}/bin/webpack.js”, and use this file as the startup file.

  4. Modify line 267 of /node_module/webpack_cli/bin/cli.js. Since Webpack uses CLI as the initiator and CLI is the node_modules dependency package, cli finally runs webpack library under node_modules. Now I want CLI to run the Clone webpack library, so I forcibly change the dependency relationship. If there is a better way to improve.

- const webpack = require("webpack");
+ const webpack = require(".. /.. /.. /lib/webpack");
Copy the code
  1. Add a breakpoint at the first line of /bin/webpack.js to run debugging

Matters needing attention

  • Call stack: In Webpack code, function recursions and callbacks are so layered that sometimes you forget where you came from, and it’s easy to find memories by flipping through the call stack.
  • SkipFiles: there are a lot of third-party libraries in Webpack, and sometimes we want to follow them to see the details, and sometimes we want to quickly skip the details, so it’s very convenient to configure this parameter flexibly to control the debugging pace.
  • Eval: The eval method is heavily used in Tapable, the core class in WebPack, so you won’t be confused when you see it in the call stack.
  • Asynchronous call: in webpack a lot of use of async, so understand the asynchronous method to run processes and debugging methods, at the same time with skipFiles will not care about the branch process ignore tuning, it is not easy to be around the source code to dizzy.