中对Node程序实行调节,编制程序式调节和测试node程序的利器chrome

爆料浏览器远程调节和测试技术

2016/10/19 · 基本功技术 ·
1 评论 ·
浏览器,
调试,
远程

原稿出处: Taobao前端团队(FED) –
肖焉   

澳门葡京 1

澳门葡京 2

简介

chrome-remote-interface是chrome调节和测试协议的第二方调节客户端完结,该项目开源,提供了命令行工具,且为node程序提供了api。
chrome-remote-interface为根据chrome调节和测试协议编写本身的node调节和测试工具提供了方便人民群众的途径,因为使用它,你不须要基于原始的商业事务通过websocket编制程序去支付调节和测试工具了。
项目地址https://github.com/cyrus-and/chrome-remote-interface。

启动node.js脚本

$ NODE_ENV=production API_KEY=442CC1FE-4333-46CE-80EE-6705A1896832
node server.js

调剂技术的来自

壹玖肆柒 年 9 月 9 日,一名U.S.A.的地工学家格蕾丝.霍普和她的同伙在对 马克 II
总结机进行切磋的时候发现,三只飞蛾粘在一个继电器上,导致电脑不能够平常工作,当她们把飞蛾移除之后,总计机又卷土重来了例行运作。于是他们将那只飞蛾贴在了他们立时记录的日志上,对那件业务进展了详尽的笔录,并在日记最终写了如此一句话:First
actual case of bug being found。那是他们发现的首先个实在意义上的
bug,那也是人类总计机软件历史上,发现的第四个bug,而她们找到飞蛾的点子和进度,就是 debugging 调节和测试技术。

澳门葡京 3

从格蕾丝调节和测试第一个 bug 到明日,69
年的日子里,在电脑世界,硬件、软件各类调节技术都在不断的上进和变异。那么对于方兴日盛的前端来说,调节和测试技术也进一步显得至关心爱惜要。Tmall前端团队也正值使用部分翻新的技巧和手法来化解有线页面调节和测试的难点。今日先跟我们享受下浏览器远程调节和测试技术,本文将用
Chrome/Webview 来作为案例。

Node

应用命令行

Node.js 控制台 REPL

在顶峰敲node进入repl

1+1
a = 1;

调节原理

相信有恒河沙数人和本人一样,习惯了使用chrome调节和测试js程序,不过node刚开端提供的调剂形式只用Debugger,只好通过node
–debug
xxx.js运营命令行调节和测试工具,及其的不便宜。当然也有一对插件在此基础上,使用websocket举行通讯,使其得以在chrome浏览器中调剂。体验和间接在chrome上举行调节和测试还是差了好多。

安装

经过npm举行设置

npm install chrome-remote-interface

基础知识

Node.js是起家在Google Chrome V8引擎和ECMASC宝马X5IPT之上的。

调节格局与权力管理

澳门葡京 4

当下常规浏览器调节和测试目的分为两种:Chrome PC 浏览器和 Chrome
Mobile(Android 4.4 今后,Android WebView 其实便是 Chromium WebView)。

而是在Node v7.x.x后,Node有提供了1个Inspector,能够间接和Chrome
DevTools举行通讯,上面来详细介绍进入调剂的步骤,以及在应用进度中,笔者遇到的标题及化解办法。

启航调节和测试目的

chrome-remote-interface基于chrome调节和测试协议,由此其扶助调节和测试chrome浏览器和node运营条件。
不论是哪类调节和测试指标,其运转时都应有内定调节和测试端口。
如若要调节chrome浏览器,应该在运行chrome时添加–remote-debugging-port参数,如下:

goole-chrome --remote-debugging-port=9222

比方调节和测试node,在运维时添加–inspect参数,如下:

node --inspect=9222 app.js

此时,node会输出:

Debugger listening on port 9222.
Warning: This is an experimental feature and could change at any time.
To start debugging, open the following URL in Chrome:
    chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef

题外话:假诺只是梦想调节和测试node,并不打算开发多个调节和测试node的工具以来,依据提醒中的url,在chrome中开辟就直接能够用chrome的开发工具调节和测试了。

命名

静态变量大概个人函数以_开头

Chrome PC 浏览器

对于调节和测试 Chrome PC
浏览器,恐怕我们经常使用的是用鼠标右键大概连忙方式(mac:option + command

  • J),唤起 Chrome
    的控制台,来对近期页面举行调剂。其实还有其余一种方法,正是应用二个Chrome 浏览器调节和测试另贰个 Chrome 浏览器。Chrome
    运维的时候,私下认可是关门了调节端口的,假诺要对二个对象 Chrome PC
    浏览器实行调节,那么运行的时候,能够透过传递参数来打开 Chrome
    的调剂开关:

JavaScript

# for mac sudo /Applications/Google\
Chrome.app/Contents/MacOS/Google\ Chrome –remote-debugging-port=9222

1
2
# for mac
sudo /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome –remote-debugging-port=9222

目录

  1. 切切实实调节和测试步骤详细介绍
  2. 难题及缓解方法
  3. 其余工具
  4. 相关文书档案

体验命令行

  • 查看全体指令

    在极限输入chrome-remote-interface并回车,可观望如下输出:

Usage: chrome-remote-interface [options] [command]

Commands:

inspect [options] [<target>] inspect a target (defaults to the current tab)
list                   list all the available tabs
new [<url>]            create a new tab
activate <id>          activate a tab by id
close <id>             close a tab by id
version                show the browser version
protocol [options]     show the currently available protocol descriptor

Options:

-h, --help         output usage information
-t, --host <host>  HTTP frontend host
-p, --port <port>  HTTP frontend port
```

其中,new和close是针对浏览器的tab的命令,不要针对node来运行。
  • 翻看全体页面实例

    chrome-remote-interface -t 127.0.0.1 -p 9222 list

    输出如下:

    [ { description: 'node.js instance',
    devtoolsFrontendUrl: 'https://chrome-devtools-frontend.appspot.com/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef',
    faviconUrl: 'https://nodejs.org/static/favicon.ico',
    id: '2894362d-f2d1-4f3b-a9e8-4e27da5714ef',
    title: 'app.js',
    type: 'node',
    url: 'file:///Users/renbaogang/git/enzyme.node/app.js',
    webSocketDebuggerUrl: 'ws://localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef' } ]
    

    个中devtoolsFrontendUrl能够在chrome地址栏中回车该url,打开chrome的开发工具,能够一贯用该工具调节和测试。和起步node时,node提醒的url是二个用场。
    id是日前页面标识。
    url是近来页面url。
    webSocketDebuggerUrl是node提供的ws协议的调剂服务,调节和测试客户端须求通过该url连接受调节和测试服务。

  • 翻看调节和测试指标版本

    chrome-remote-interface -t 127.0.0.1 -p 9222 version

    输出:

    [ { Browser: 'node.js/v7.0.0', 'Protocol-Version': '1.1' } ]
    
  • 查看调试目标帮衬的调剂协议

    chrome-remote-interface -t 127.0.0.1 -p 9222 protocol

    输出:

    { 
    remote: false,
    descriptor: {
        { version: { major: '1', minor: '2' },
        domains:[略]
    }
    }
    

    remote:false说明协议内容是工具自带的合计文本,并不是出自调节和测试指标。
    中对Node程序实行调节,编制程序式调节和测试node程序的利器chrome。假使要得到调试目的的磋商内容要添加-r选项,如:

    chrome-remote-interface -t 127.0.0.1 -p 9222 protocol -r

    输出:

    { 
    remote: true,
    descriptor: {
        { version: { major: '1', minor: '1' },
        domains:[略]
    }
    }
    
  • 调剂命令

    chrome-remote-interface -t 127.0.0.1 -p 9222 inspect -r
    -r 表示应用调节和测试指标提供的合计描述文件
    由此inspect能够调节node或chrome所扶助的各样域。

    Console域体验

Console.enable()
{ result: {} }
Console.clearMessages()
{ result: {} }
“`

Debugger域体验

```

Debugger.enable()
{ result: {} }
Debugger.setBreakpointsActive({active:true})
{ result: {} }
“`

HeapProfiler域体验

```

HeapProfiler.enable()
{ result: {} }
HeapProfiler.startTrackingHeapObjects({trackAllocations:true})
{ result: {} }
“`

Profiler域体验

```

Profiler.enable()
{ result: {} }
Profiler.start()
{ result: {} }
Profiler.stop()
{ result:
{ profile:
{ nodes:
[ { id: 1,
callFrame:
{ functionName: ‘(root)’,
scriptId: ‘0’,
url: ”,
lineNumber: -1,
columnNumber: -1 },
hitCount: 0,
children: [ 2 ] },

      ]
    }
 }

}
“`

Runtime域体验  

>>> Runtime.evaluate({expression:"1+1"})
{ result: { result: { type: 'number', value: 2, description: '2' } } }

Schema域体验

>>> Schema.getDomains()
{ result: 
   { domains: 
      [ { name: 'Runtime', version: '1.1' },
        { name: 'Debugger', version: '1.1' },
        { name: 'Profiler', version: '1.1' },
        { name: 'HeapProfiler', version: '1.1' },
        { name: 'Schema', version: '1.1' } ] } }
    ```

    体验事件处理

    scriptParsed是Debugger域提供的脚本解析事件

    ```
>>> Debugger.scriptParsed(params=>params.url)
{ 'Debugger.scriptParsed': 'params=>params.url' }
{ 'Debugger.scriptParsed': '' }
{ 'Debugger.scriptParsed': '/Users/renbaogang/git/enzyme.node/node_modules/negotiator/lib/encoding.js' }
    ```

## API ##
1. module([options], [callback])

    基于chrome调试协议连接调试目标

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - chooseTab 决定调试哪个tab。该参数可以为三种类型:
        - function 提供一个返回tab序号的函数
        - object 正如new 和 list返回的对象一样
        - string websocket url
        默认为一个函数,返回当前激活状态的tab的序号,如:function(tabs){return 0;}
    - protocol 协议描述符,默认由remote选项来定是否使用调试目标提供的协议描述符
    - remote 默认false,如果protocol设置了,该选项不会起作用

  callback   
    如果callback是函数类型,则该函数在connect事件发生后会得到回调,并返回EventEmitter对象,如果不是,则返回Promise对象。

    EventEmitter对象支持如下事件:  

    - connect

        ```
    function (chrome) {}
    ```
    当websocket连接建立后触发,chrome是Chome类型的实例。

    - disconnect

        ```
        function () {}
        ```
        关闭websocket连接时触发

    - error

        ```
        function (err) {}
        ```
    当通过host:port/json无法到达,或websocket连接无法建立时触发  
        err,Error类型的错误对象

  使用样例,针对chrome浏览器:

    ```
const Chrome = require('chrome-remote-interface');
Chrome(function (chrome) {
    with (chrome) {
        Network.requestWillBeSent(function (params) {
            console.log(params.request.url);
        });
        Page.loadEventFired(function () {
            close();
        });
        Network.enable();
        Page.enable();
        once('ready', function () {
            Page.navigate({'url': 'https://github.com'});
        });
    }
}).on('error', function (err) {
    console.error('Cannot connect to Chrome:', err);
});
    ```

2. module.Protocol([options],[callback])

    获取chrome调试协议描述符

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - remote 默认false

    callback 回调函数,在获取到协议内容后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - protocol 包含以下属性
        - remote 是否远程协议
        - descriptor chrome调试协议描述符

  使用样例:

    ```
const Chrome = require('chrome-remote-interface');
Chrome.Protocol(function (err, protocol) {
    if (!err) {
        console.log(JSON.stringify(protocol.descriptor, null, 4));
    }
});
    ```

3. module.List([options], [callback])

    获取所有的tabs,当然在node中只会有一个  

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - remote 默认false

    callback 回调函数,在获取到tabs内容后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - tabs 获取到的tab数组

  使用样例:

    ```
const Chrome = require('chrome-remote-interface');
Chrome.List(function (err, tabs) {
    if (!err) {
        console.log(tabs);
    }
});
    ```

4. module.New([options], [callback])

    创建新的tab

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - url 新tab加载的url 默认about:blank

    callback 回调函数,在新tab创建后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - tab 新增的tab

  使用样例:  

    ```
const Chrome = require('chrome-remote-interface');
Chrome.New(function (err, tab) {
    if (!err) {
        console.log(tab);
    }
});
  1. module.Activate([options], [callback])

    激活钦定tab

    options object类型,具有如下属性:

    • host 默认localhost
    • port 默认9222
    • id 目标tab的id

    callback 回调函数,在新tab创立后调用,函数接收如下参数:

    • err Error多个荒唐对象,倘诺有荒唐的话

运用样例:

```

const Chrome = require(‘chrome-remote-interface’);
Chrome.Activate({‘id’: ‘CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC’}, function
(err) {
if (!err) {
console.log(‘success! tab is closing’);
}
});
“`

  1. module.Close([options], [callback])

    关闭钦命tab

    options object类型,具有如下属性:

    • host 默认localhost
    • port 默认9222
    • id 目标tab的id

    callback 回调函数,在新tab创造后调用,函数接收如下参数:

    • err Error1个荒唐对象,倘诺有错误的话

使用样例:

```

const Chrome = require(‘chrome-remote-interface’);
Chrome.Close({‘id’: ‘CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC’}, function
(err) {
if (!err) {
console.log(‘success! tab is closing’);
}
});
“`

  1. module.Version([options], [callback])

    取得版本新闻

    options object类型,具有如下属性:

    • host 默认localhost
    • port 默认9222

    callback 回调函数,在新tab成立后调用,函数接收如下参数:

    • err Error一个荒唐对象,假如有错误的话
    • info json object

行使样例:

```

const Chrome = require(‘chrome-remote-interface’);
Chrome.Version(function (err, info) {
if (!err) {
console.log(info);
}
});
“`

  1. Chrome 类

    支撑的风云:

    • event

      在中远距离调节和测试指标发送布告时接触,一般是长距离对象实施了客户端提交的格局后

      function (message) {}
      

      message包涵如下属性:

      • method 公告内容,方法名 如:’Network.request威尔BeSent’
      • params 参数内容

    动用样例:

     ```
     chrome.on('event', function (message) {
         if (message.method === 'Network.requestWillBeSent') {
             console.log(message.params);
         }   
     });
     ```
    
    • <method>

      调剂指标通过websocket发送了一个钦定方法名的打招呼

      function (params) {}
      

      选取样例:

      chrome.on('Network.requestWillBeSent', console.log);
      
    • ready

      老是没有调节和测试命令等待调节和测试指标重回时接触

      function () {}
      

      选拔样例:

      只在Network和Page激活后加载3个url

      chrome.Network.enable();
      chrome.Page.enable();
      chrome.once('ready', function () {
          chrome.Page.navigate({'url': 'https://github.com'});
      });
      

支撑的法子

  • chrome.send(method, [params], [callback])

      发送一个调试命令
    
      method 命令名称  
      params 参数  
      callback 当远程对象对该命令发送一个应答后调用,函数具有以下参数:  
      - error boolean 是否成功
      - response 如果error===true,返回一个error对象,{error:...},否则返回一个应答,{result:...}
    
      注意:在chrome调试规范里提到的id字段,在这里被内部管理不会暴露给用户
    

    使用样例:

      ```
      chrome.send('Page.navigate', {'url': 'https://github.com'}, console.log);
      ```
    
    • chrome..([params], [callback])

      是chrome.send(‘.‘, params, callback);的一种变形

      例如:

        chrome.Page.navigate({'url': 'https://github.com'}, console.log);
      
    • chrome..(callback)

      是chrome.on(‘.‘, callback)的变形

      例如:

        chrome.Network.requestWillBeSent(console.log);
      
    • chrome.close([callback])

      关闭与远程调节和测试指标的连接
      callback会在websocket关闭成功后调用

封存的首要词

  1. process
  2. global
  3. module.exports

Chrome Android 浏览器

对于调节和测试 Android 上的 Chrome 或许 WebView 需求延续 USB
线。打开调节和测试端口的主意如下:

JavaScript

adb forward tcp:9222 localabstract:chrome_devtools_remote

1
adb forward tcp:9222 localabstract:chrome_devtools_remote

跟 Chrome PC 浏览器差异的是,对于 Chrome Android
浏览器,由于数量传输是因而 USB 线而不是 WIFI,实际上 Chrome Android
创造的三个 chrome_devtools_remote 这些 path 的 domain
socket。所以,上边一条命令则是通过 Android 的 adb 将 PC 的端口 9222 通过
USB 线与 chrome_devtools_remote 那些 domain socket
建立了多个端口映射。

现实调节和测试步骤详细介绍

__dirname和process.cwd 相对路径 固然像这么起步 $ node ./code/program.js.

双面包车型大巴门道是不等同的

权限管理

谷歌(Google) 为了限制调节和测试端口的连结范围,对于 Chrome PC
浏览器,调节和测试端口只接受来自 127.0.0.1 或者 localhost
的数码请求,所以,你不可能透过你的当地机械 IP 来调节 Chrome。对于 Android
Chrome/WebView,调试端口只接受来自于 shell
这么些用户数量请求,也正是说只好通过 USB 进行调节,而不能够由此 WIFI。

1. Chrome DevTools和Node版本要求

  1. Chrome DevTools: 55+
    在地点栏中输入chrome://settings/help,查看Chrome版本

    澳门葡京 5

    Chroem版本

  2. Node.js: v7.x.x+
    在命令行中输入node –version拓展查看

    澳门葡京 6

    Node.js版本

骨干模块

  1. htpp
  2. util
  3. querystring
  4. url
  5. fs
  6. path
  7. crypto
  8. string_decoder

开班调节和测试

通过以上的调节和测试格局的交接以及调节端口的开辟,那么些时候在浏览器中输入:

JavaScript

1
http://127.0.0.1:9222/json

将晤面到类似下边包车型大巴剧情:

JavaScript

[ { “description”: “”, “devtoolsFrontendUrl”:
“/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e”,
“id”: “ebdace60-d482-4340-b622-a6198e7aad6e”, “title”:
“揭秘浏览器远程调节和测试技术.mdown—/Users/harlen/Documents”, “type”: “page”,
“url”: “”, “webSocketDebuggerUrl”:
“ws://127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e”
} ]

1
2
3
4
5
6
7
8
9
10
11
[
  {
    "description": "",
    "devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e",
    "id": "ebdace60-d482-4340-b622-a6198e7aad6e",
    "title": "揭秘浏览器远程调试技术.mdown—/Users/harlen/Documents",
    "type": "page",
    "url": "http://127.0.0.1:51004/view/61",
    "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e"
  }
]

里头,最珍视的 2 个参数分别是 id 和 webSocketDebuggerUrl。Chrome
会为各种页面分配贰个唯一的
id,作为该页面包车型地铁绝无仅有标识符。大约对指标浏览器的装有操作都是亟需带上这一个id。

Chrome 提供了以下那些 http 接口控制目的浏览器

JavaScript

# 获取当前享有可调式页面新闻 # 获取调节和测试目的WebView/blink 的版本号 # 创设新的
tab,并加载 url # 关闭 id 对应的 tab

1
2
3
4
5
6
7
8
9
10
11
# 获取当前所有可调式页面信息
http://127.0.0.1:9222/json
 
# 获取调试目标 WebView/blink 的版本号
http://127.0.0.1:9222/json/version
 
# 创建新的 tab,并加载 url
http://127.0.0.1:9222/json/new?url
 
# 关闭 id 对应的 tab
http://127.0.0.1:9222/json/close/id

webSocketDebuggerUrl 则在调节该页面要求使用的三个 WebSocket 连接。chrome
的 devtool 的保有调节和测试功效,都是基于 Remote Debugging
Protocol
使用 WebSocket 来进展数量传输的。那么那些 WebSocket,便是地方大家从
http://127.0.0.1:9222/json 获取的
webSocketDebuggerUrl,每种页面都有投机差别的
webSocketDebuggerUrl。这个 webSocketDebuggerUrl是通过 url 的 query
参数字传送递给 chrome devtool 的。

chrome 的 devtool 能够从 Chrome 浏览器中举行领取 devtool 源码恐怕从
blink 源码中收获。在布署好团结的 chrome devtool
代码之后,上边既能够起来对 Chrome 实行调节和测试, 浏览器输入一下剧情:

JavaScript

1
http://path_to_your_devtool/devtool.html?ws=127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e

其中 ws 那几个参数的值即是地方现身的 webSocketDebuggerUrl。Chrome 的
devtool 会使用这么些 url 创制 WebSocket 对该页面进行调节。

2. 运作脚本,并访问调节和测试页面

完全步调:先依据具体意况,采用差别的参数运维脚本;然后访问相应的url获取调节和测试页面包车型的士访问地址;最终访问那一个地方,进入调节和测试页面。

在命令行中运维相应脚本,使用–inspect,或者–inspect-brk敞开调节和测试开关,如node
–inspect path/xxx.js
或者node –inspect-brk
path/xxx.js
。上边依照区别景况开始展览具体分析。

调试node.js程序

  1. Node.js Debugger 并倒霉用
  2. Node Inspector Chrome devtools的一个端口
  3. Webstrom IDE调节和测试 非凡好用
  4. console.log

如何兑现 JavaScript 调节和测试

在进入 Chrome 的 devtool 之后,大家能够调出控制台,来查阅 devtool 的
WebSocket 数据。那几个里面有恒河沙数数目,作者这里只讲跟 JavaScript
调节和测试相关的。
澳门葡京 7

图中,对于 JavaScript 调节和测试,有一条十分关键的消息,作者墨玉绿选中的那条新闻:

JavaScript

{“id”:6,”method”:”Debugger.enable”}

1
{"id":6,"method":"Debugger.enable"}

然后选中要调节的 JavaScript 文件,然后设置三个断点,大家再来看看
WebSocket 新闻:
澳门葡京 8

devtool 像目标 Chrome 发送了 2 条消息

JavaScript

{ “id”: 23, “method”: “Debugger.getScriptSource”, “params”: {
“scriptId”: “103” } }

1
2
3
4
5
6
7
{
  "id": 23,
  "method": "Debugger.getScriptSource",
  "params": {
    "scriptId": "103"
  }
}

JavaScript

{ “id”: 24, “method”: “Debugger.setBreakpointByUrl”, “params”: {
“lineNumber”: 2, “url”:
“”,
“columnNumber”: 0, “condition”: “” } }

1
2
3
4
5
6
7
8
9
10
{
  "id": 24,
  "method": "Debugger.setBreakpointByUrl",
  "params": {
    "lineNumber": 2,
    "url": "https://g.alicdn.com/alilog/wlog/0.2.10/??aplus_wap.js,spm_wap.js,spmact_wap.js",
    "columnNumber": 0,
    "condition": ""
  }
}

那么收到这几条音信随后,V8 做了些什么呢?
咱俩先来大致的看下 V8 里面包车型客车一小段源码片段:

JavaScript

// V8 Debugger.cpp DispatcherImpl(FrontendChannel* frontendChannel,
Backend* backend) : DispatcherBase(frontendChannel),
m_backend(backend) { m_dispatchMap[“Debugger.enable”] =
&DispatcherImpl::enable; m_dispatchMap[“Debugger.disable”] =
&DispatcherImpl::disable;
m_dispatchMap[“Debugger.setBreakpointsActive”] =
&DispatcherImpl::setBreakpointsActive;
m_dispatchMap[“Debugger.setSkipAllPauses”] =
&DispatcherImpl::setSkipAllPauses;
m_dispatchMap[“Debugger.setBreakpointByUrl”] =
&DispatcherImpl::setBreakpointByUrl;
m_dispatchMap[“Debugger.setBreakpoint”] =
&DispatcherImpl::setBreakpoint;
m_dispatchMap[“Debugger.removeBreakpoint”] =
&DispatcherImpl::removeBreakpoint;
m_dispatchMap[“Debugger.continueToLocation”] =
&DispatcherImpl::continueToLocation;
m_dispatchMap[“Debugger.stepOver”] = &DispatcherImpl::stepOver;
m_dispatchMap[“Debugger.stepInto”] = &DispatcherImpl::stepInto;
m_dispatchMap[“Debugger.stepOut”] = &DispatcherImpl::stepOut;
m_dispatchMap[“Debugger.pause”] = &DispatcherImpl::pause;
m_dispatchMap[“Debugger.resume”] = &DispatcherImpl::resume;
m_dispatchMap[“Debugger.searchInContent”] =
&DispatcherImpl::searchInContent;
m_dispatchMap[“Debugger.setScriptSource”] =
&DispatcherImpl::setScriptSource;
m_dispatchMap[“Debugger.restartFrame”] =
&DispatcherImpl::restartFrame;
m_dispatchMap[“Debugger.getScriptSource”] =
&DispatcherImpl::getScriptSource;
m_dispatchMap[“Debugger.setPauseOnExceptions”] =
&DispatcherImpl::setPauseOnExceptions;
m_dispatchMap[“Debugger.evaluateOnCallFrame”] =
&DispatcherImpl::evaluateOnCallFrame;
m_dispatchMap[澳门葡京 ,”Debugger.setVariableValue”] =
&DispatcherImpl::setVariableValue;
m_dispatchMap[“Debugger.setAsyncCallStackDepth”] =
&DispatcherImpl::setAsyncCallStackDepth;
m_dispatchMap[“Debugger.setBlackboxPatterns”] =
&DispatcherImpl::setBlackboxPatterns;
m_dispatchMap[“Debugger.setBlackboxedRanges”] =
&DispatcherImpl::setBlackboxedRanges; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// V8 Debugger.cpp
DispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) : DispatcherBase(frontendChannel), m_backend(backend) {
    m_dispatchMap["Debugger.enable"] = &DispatcherImpl::enable;
    m_dispatchMap["Debugger.disable"] = &DispatcherImpl::disable;
    m_dispatchMap["Debugger.setBreakpointsActive"] = &DispatcherImpl::setBreakpointsActive;
    m_dispatchMap["Debugger.setSkipAllPauses"] = &DispatcherImpl::setSkipAllPauses;
    m_dispatchMap["Debugger.setBreakpointByUrl"] = &DispatcherImpl::setBreakpointByUrl;
    m_dispatchMap["Debugger.setBreakpoint"] = &DispatcherImpl::setBreakpoint;
    m_dispatchMap["Debugger.removeBreakpoint"] = &DispatcherImpl::removeBreakpoint;
    m_dispatchMap["Debugger.continueToLocation"] = &DispatcherImpl::continueToLocation;
    m_dispatchMap["Debugger.stepOver"] = &DispatcherImpl::stepOver;
    m_dispatchMap["Debugger.stepInto"] = &DispatcherImpl::stepInto;
    m_dispatchMap["Debugger.stepOut"] = &DispatcherImpl::stepOut;
    m_dispatchMap["Debugger.pause"] = &DispatcherImpl::pause;
    m_dispatchMap["Debugger.resume"] = &DispatcherImpl::resume;
    m_dispatchMap["Debugger.searchInContent"] = &DispatcherImpl::searchInContent;
    m_dispatchMap["Debugger.setScriptSource"] = &DispatcherImpl::setScriptSource;
    m_dispatchMap["Debugger.restartFrame"] = &DispatcherImpl::restartFrame;
    m_dispatchMap["Debugger.getScriptSource"] = &DispatcherImpl::getScriptSource;
    m_dispatchMap["Debugger.setPauseOnExceptions"] = &DispatcherImpl::setPauseOnExceptions;
    m_dispatchMap["Debugger.evaluateOnCallFrame"] = &DispatcherImpl::evaluateOnCallFrame;
    m_dispatchMap["Debugger.setVariableValue"] = &DispatcherImpl::setVariableValue;
    m_dispatchMap["Debugger.setAsyncCallStackDepth"] = &DispatcherImpl::setAsyncCallStackDepth;
    m_dispatchMap["Debugger.setBlackboxPatterns"] = &DispatcherImpl::setBlackboxPatterns;
    m_dispatchMap["Debugger.setBlackboxedRanges"] = &DispatcherImpl::setBlackboxedRanges;
}

你会意识,V8 有 m_dispatchMap 那样二个 Map。专门用来拍卖所有JavaScript 调节和测试相关的拍卖。
里头就有本文即将重点讲述的:

  • Debuggger.enable
  • Debugger.getScriptSource
  • setBreakpointByUrl

那几个都必要在 V8 的源码中找到答案。顺便给我们推荐二个翻看 Chromium/V8
最正确的法门是利用
https://cs.chromium.org,比 SourceInsight
还要便宜。

情况1

第②运转脚本,若是您的本子搭建http大概net服务器,你可以直接动用–inspect。如

let net = require('net');

// 创建一个net服务器
const server = net.createServer();

// 连接回调函数
server.on('connection', (conn) => {
    console.log('connection');
});

// 监听8080端口
server.listen(8080);

// 监听回调函数
server.on('listening', () => {
    console.log('listening')
});

澳门葡京 9

选拔–inspect后显得结果

注:

  1. 上海教室是在v8.9.1版本时显得的结果,后面会一回列举出别样版本下的结果。
  2. Debugger listening on
    ws://127.0.0.1:9229/890b7b49-c744-4103-b0cd-6c5e8036be95,当中给定的url并不是提必要大家在Chrome浏览器中做客的地点,而是Node.js和Chrome之间进行通讯的的地点,它们经过websocket通过点名的端口实行通讯,从而将调试结果实时展现在Chrome浏览器中。

然后,访问http://IP:port/json/list(在这之中IP便是主机的IP地址,平常为127.0.0.1,port则是端口号,私下认可为9229,那是Node提须求自由调节工具链接调试的商谈,能够通过命令行参数进行修改参数文书档案地址),会回来相应http请求的元数据,包涵WebSocket
ULANDL,UUID,Chrome DevTools U途乐L。个中,WebSocket
URL
就算Node.js和Chrome之间的通讯地方;UUID是三个一定的标识,每3个进度都会分配多少个uuid,因而每3回调用会有现身分裂的结果;Chrome
DevTools URL
正是我们的调节和测试页面包车型的士url。

如访问http://127.0.0.1:9229/json/list,大家将会拿走一下结出,当中devtoolsFrontendUrl正是大家须求拜访的地址。

澳门葡京 10

显示屏快速照相 2017-11-22 14.34.13.png

最终,访问这一个地方后呈现页面:

澳门葡京 11

调剂页面

使用Node.js Debugger

node debug hello.js

next 火速键n 跳到下三个话语
cont 火速键c 跳到下三个断点
step s 进入function
out o 跳出function
watch

打开浏览器

要么进行 curl

Debugger.enable

JavaScript

void V8Debugger::enable() { if (m_enableCount++) return;
DCHECK(!enabled()); v8::HandleScope scope(m_isolate);
v8::Debug::SetDebugEventListener(m_isolate,
&V8Debugger::v8DebugEventCallback, v8::External::New(m_isolate, this));
m_debuggerContext.Reset(m_isolate,
v8::Debug::GetDebugContext(m_isolate)); compileDebuggerScript(); }

1
2
3
4
5
6
7
8
9
void V8Debugger::enable() {
    if (m_enableCount++) return;
    DCHECK(!enabled());
    v8::HandleScope scope(m_isolate);
    v8::Debug::SetDebugEventListener(m_isolate, &V8Debugger::v8DebugEventCallback,
    v8::External::New(m_isolate, this));
    m_debuggerContext.Reset(m_isolate, v8::Debug::GetDebugContext(m_isolate));
    compileDebuggerScript();
}

其一接口的称谓叫 Debugger.enable,可是接受那条音信,V8
其实就干了两件工作事情:

  • SetDebugEventListener:
    给 JavaScript 调节和测试安装监听器,并安装 v8DebugEventCallback
    那一个回调函数。JavaScript
    全体的调剂事件,都会被这一个监听器捕获,包含:JavaScript
    格外甘休,断点停止,单步调节和测试等等。
  • compileDebuggerScript:
    编译 V8 内置的 JavaScript 文件
    debugger-script.js。由于那文件相比长,我那边就不贴出来了,感兴趣的同班点击这些链接进行查看源码。debugger-script.js
    首假使概念了有的针对性 JavaScript
    断点进行操作的函数,例如设置断点、查找断点以及单步调节和测试相关的函数。那么那个
    debugger-script.js 文件,被 V8 举行编写翻译之后,保存在 global
    对象上,等待对 JavaScript 举行调节的时候,被调用。

    #### Debugger.getScriptSource

    在 Chrome 解析引擎解析到 ` 标签之后,Chrome 将会把 script
    标签对应的 JavaScript 源码扔给 V8 编译执行。同时,V8 将会对所有的
    JavaScript 源码片段进行编号并保存。所以,当 chrome devtool
    需要获取要调试的 JavaScript 文件的时候,只需要通过
    Debugger.getScriptSource,给 V8 传递一个 scriptId,V8 将会把
    JavaScript 源码返回。我们再回头看看这个图中的消息:
    ![](http://jbcdn2.b0.upaiyun.com/2016/10/cc8205b6b73c6aa787046a0a6c634ae7.png)
    上面 id 为 23 的
    scriptSource` 就是 V8 再次回到的 JavaScript
    源码,如此的话,大家就足以在 devtool 中看看我们要调节的 JavaScript
    源码了。

情况2(重要)

假若你的剧本运转完未来平昔甘休进度,那么您须要动用–inspect-brk来运行调节和测试器,那样使得脚本能够代码执行此前break,不然,整个代码间接运转到代码结尾,甘休进程,根本不能够进行调剂。如:

function sayHi(name) {
    console.log('Hello, ' + name);
}

sayHi('yyp');
  1. 使用–inspect:

    澳门葡京 12

    直白动用–inspect

一贯利用–inspect,代码执行完成后,进度一直甘休,由此未曾宗意在展开调剂。

  1. 使用–inspect-brk:

    澳门葡京 13

    使用–inspect-brk

澳门葡京 14

访问127.0.0.1:9229

澳门葡京 15

调节页面

从地方三张图中可以观望,此时该Node进度并从未一贯退出,并且大家得以因而拜访http://127.0.0.1:9229/json/list取得页面url,并且在调节页面中大家也发现,程序在率先行break。

有心人的您,恐怕还发现,与大家所写的次序差异的是,调节和测试页面中,将我们的次第包裹在1个函数中,因为浏览器中不存在对应的对象(实际原因可以自动去钻探)。

使用Node Inspector

npm install -g node-inspector

然后,启动node-inspector
node-inspector

在新的极限打开
node —debug-brk hello-debug.js or node —debug hello-debug.js

打开chrome浏览器 别的浏览器不得以

Debugger.setBreakpointByUrl

有着准备干活都搞好了,现在就足以发轫安装断点了。从上边的几个图中,已经得以很明白的来看,Debugger.setBreakpointByUrl
给指标 Chrome 传递了一个 JavaScript 的 url 和断点的行号。

首先,V8 会去找,是还是不是曾经存在了该 U宝马X3L 对应的 JavaScript 源码了:

JavaScript

for (const auto& script : m_scripts) { if (!matches(m_inspector,
script.second->sourceURL(), url, isRegex)) continue;
std::unique_ptr<protocol::Debugger::Location> location =
resolveBreakpoint( breakpointId, script.first, breakpoint,
UserBreakpointSource); if (location)
(*locations)->addItem(std::move(location)); } *outBreakpointId =
breakpointId;

1
2
3
4
5
6
7
8
9
for (const auto& script : m_scripts) {
  if (!matches(m_inspector, script.second->sourceURL(), url, isRegex))
    continue;
  std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(
    breakpointId, script.first, breakpoint, UserBreakpointSource);
  if (location) (*locations)->addItem(std::move(location));
}
 
*outBreakpointId = breakpointId;

V8 给拥有的断点,创立一个 breakpointObject。并将那一个 braekpointObject 以
的样式存放在多个 Map 里面,而以此 Key,正是这些 JavaScript 文件的
U驭胜L。看到那里,已经得以分解很多同班在调节和测试 JavaScript
碰着的2个难题:,>

有个别同学为了防患页面包车型客车 JavaScript 文件不更新,对于部分重庆大学的
JavaScript 文件的 UMuranoL 添加访问时间戳,对于那个添加了拜访时间戳的
JavaScript 文件实行设置断点然后刷新调试的时候,Chrome 会打字与印刷一个warnning,告诉你断点丢失。

原因很不难,在调节的时候,V8 发现这些 breakpointMap 里面找不到对应的
breakpointObject,因为 URubiconL 暴发了变动,那一个 brakpointObject
就不见了,所以 V8 就找不到了,不可能举办断点调试。

依照咱们的常规思维,你或然会以为 V8 会将断点设置在 C++
中,其实一开头作者也是这般觉得。随着对 V8
的探讨,让自身看到了自身时曾相识的一部分函数名:

JavaScript

v8::Local<v8::Function> setBreakpointFunction =
v8::Local<v8::Function>::Cast( m_debuggerScript.Get(m_isolate)
->Get(context, toV8StringInternalized(m_isolate, “setBreakpoint”))
.ToLocalChecked()); v8::Local<v8::Value> breakpointId =
v8::Debug::Call(debuggerContext(), setBreakpointFunction, info)
.ToLocalChecked();

1
2
3
4
5
6
7
v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(
    m_debuggerScript.Get(m_isolate)
    ->Get(context, toV8StringInternalized(m_isolate, "setBreakpoint"))
      .ToLocalChecked());
v8::Local<v8::Value> breakpointId =
  v8::Debug::Call(debuggerContext(), setBreakpointFunction, info)
    .ToLocalChecked();

其中,m_debuggerScript,便是本人日前提到的 debugger-script.js。随着对
V8 Debugger 的更为商量,我意识,V8 实际上对这几个对那个 breakpointObject
设置了 2 次。2次是透过在 C++ 中调用 m_debuggerScript 的 setBreakpoint
设置到 JavaScript 的 context 里面,也便是下边这段 C++
逻辑做的事体。另3次是,m_debuggerScript 反过来将断点新闻设置到了 V8
的 C++ Runtime 中,为要调节的 JavaScript 的某一行设置三个 JavaScript
的回调函数。

任何工具

node-inspector
在行使那一个工具在此以前,小编直接在动用node-inspector,只需求运用npm install -g
node-inspector安装node-inspector命令行工具,然后在采纳node-inspector
path/xxx.js即可运行调节和测试。
不足之处:调节体验相比差,相对Chrome
DevTools使用,感觉不够流畅,并且在较高版本中,debugger被撇下(v7.7.0+),进而无法选用,恐怕会报错(v8.9.1版本直接报错)。

vsCode + webStorm
提供第叁手调节和测试成效(未使用过)。

检查和测试文件改动

  1. forever
  2. nodemon
  3. supervisor
  4. up

断点命中

鉴于 V8 对 JavaScript 是即时编写翻译执行的,没有生成
bytecode,而是径直扭转的 machine code
执行的,所以这些断点回调函数也会被安装到那一个 machine code 里面。

最后触发断点事件,也是 V8 的 C++ Runtime。当用户刷新大概直接执行
JavaScript 的逻辑的时候,实际上是 V8 C++ Runtime 在运行 JavaScript
片段暴发的 machine code,这几个 machine code
已经包含了断点回调函数了。一旦那个 machine code
里面包车型地铁回调函数被触发,接着就会触发在此之前 Debugger.enable
设置的调剂事件监听器 Debug伊芙ntListener 的回调函数。并赶回一条新闻给
Chrome 的 devtool,告诉 Chrome devtool,当前 JavaScript 被 pause
的行号。到此甘休,1个断点就被击中了。

至于 JavaScript
断点命中,其实是三个很复杂的进度。前面有时光的话,会越发讲讲 JavaScript
断点命中的详细逻辑。

题材及缓解办法

总结

浏览器的调节,最后都落脚到引擎:渲染引擎和 JavaScipt 引擎。那么对于
JavaScript 调试来说,问题就在于 V8 如何给 JavaScript
某一行开始展览标记然后开始展览断点,那须求有好几 V8 的知识。

2 赞 3 收藏 1
评论

澳门葡京 16

1. 网上古板的不合时宜设置情势走访地址

澳门葡京 17

老式设置情势

并发的题材,就是依据以上步骤操作之后,大家并不曾意识Node
debugging的选项,此时,就不知情什么样开始展览下去了。在那里给出的解释是,在最新版的Chrome浏览器中,Node
debugging不要求手动去运维了,而是暗许便是足以采纳的

1. 两样Node版本出现的题材

个别在多少个大版本下进展了测试(v6.9.2,v7.3.0,v8.9.1)

v6.9.2 版本下使用状态

澳门葡京 18

命令行执行结果

澳门葡京 19

访问

此刻您会发觉,命令行已经给你拜访Chrome的url,并且这几个url和做客http://127.0.0.1:9229/json/list得到的devtoolsFrontendUrl属性值一样,不过,当你拜访这么些url时,浏览器却不能够显得调节和测试页面,此时,表明你的Node版本太低,须求使用更高版本。

v7.3.0 版本下接纳情状

澳门葡京 20

命令行执行结果

澳门葡京 21

访问

澳门葡京 22

调剂页面

此时,命令行也提供了访问调节和测试页面包车型大巴链接。

v8.9.1 版本下采纳状态

澳门葡京 23

命令行执行结果

澳门葡京 24

访问

澳门葡京 25

调剂页面

那儿,命令行给定的的url并不是调节页面包车型客车url,由此必须通过访问http://127.0.0.1:9229/json/list来博取调节和测试页面url。

连带文书档案

Debugging
Guide
Debugging Node.js
Apps

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website