【亚洲必赢官网】中对Node程序举行调节和测试,编制程序式调节和测试node程序的利器chrome

揭秘浏览器远程调节和测试手艺

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

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

亚洲必赢官网 1

亚洲必赢官网 2

简介

chrome-remote-interface是chrome调节和测试协议的第3方调解客户端达成,该类型开源,提供了命令行工具,且为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

调治才能的源于

一玖四6 年 九 月 玖 日,一名美利坚联邦合众国的化学家格蕾丝.霍普和她的同伙在对 马克 II
计算机举办商量的时候开采,2只飞蛾粘在2个继电器上,导致计算机无法寻常专业,当他们把飞蛾移除之后,计算机又复苏了符合规律运营。于是他们将那只飞蛾贴在了他们及时记录的日志上,对那件业务进展了详尽的笔录,并在日记最终写了如此一句话:First
actual case of bug being found。那是他们发觉的率先个实在意义上的
bug,那也是全人类应用软件历史上,开掘的首先个
bug,而他们找到飞蛾的措施和进度,正是 debugging 调节和测试才干。

亚洲必赢官网 3

从格蕾丝调试第三个 bug 到现行反革命,6玖年的小运里,在微型计算机世界,硬件、软件种种调治技能都在频频的开发进取和产生。那么对于如日中天的前端来说,调节和测试技艺也尤其显得主要。天猫商城前端团队也正值选用一些更新的本事和手段来搞定有线页面调节和测试的标题。后天先跟大家大快朵颐下浏览器远程调节和测试技艺,本文将用
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是手无寸铁在谷歌 Chrome V八引擎和ECMASCCR-VIPT之上的。

调理格局与权力管理

亚洲必赢官网 4

现阶段平常浏览器调节和测试目的分为三种:Chrome PC 浏览器和 Chrome
Mobile(Android 四.四 现在,Android WebView 其实正是 Chromium WebView)。

但是在Node v七.x.x后,Node有提供了贰个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 Error2个谬误对象,借使有错误的话

行使样例:

```

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 Error一个谬误对象,假如有不当的话

使用样例:

```

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激活后加载一个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
创造的3个 chrome_devtools_remote 那些 path 的 domain
socket。所以,上边一条命令则是透过 Android 的 adb 将 PC 的端口 922二 通过
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。

壹. 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=1二柒.0.0.一:9222/devtools/page/ebdace60-d4八贰-4340-b622-a61玖八e7aad陆e”,
“id”: “ebdace60-d4八二-4340-b62贰-a61九8e7aad6e”, “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
会为各种页面分配1个唯1的
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 对该页面进行调治。

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

总体步调:先依据具体意况,选拔差异的参数运行脚本;然后访问相应的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调节和测试 11分好用
  4. console.log

怎么样落到实处 JavaScript 调节和测试

在进入 Chrome 的 devtool 之后,我们得以调出调整台,来查阅 devtool 的
WebSocket 数据。那么些里面有许诸多据,作者那里只讲跟 JavaScript
调节和测试相关的。
亚洲必赢官网 7

图中,对于 JavaScript 调节和测试,有一条十三分重大的新闻,笔者银白选中的那条音讯:

JavaScript

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

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

然后选中要调度的 JavaScript 文件,然后设置1个断点,大家再来看看
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": ""
  }
}

那么收到这几条音信之后,V八 做了些什么呢?
咱俩先来差不离的看下 V八 里面包车型的士一小段源码片段:

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;
}

您会意识,V八 有 m_dispatchMap 那样1个 Map。专门用来处理全体JavaScript 调节和测试相关的管理。
里面就有本文就要珍视讲述的:

  • Debuggger.enable
  • Debugger.getScriptSource
  • setBreakpointByUrl

那一个都亟需在 V八 的源码中找到答案。顺便给我们推荐三个翻看 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. 上海图书馆是在v捌.九.一版本时显得的结果,后边会一遍列举出任何版本下的结果。
  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.一,port则是端口号,默感觉922玖,那是Node提供给自由调治工具链接调节和测试的争辩,可以透过命令行参数进行改造参数文书档案地址),会回去相应http请求的元数据,包罗WebSocket
U大切诺基L,UUID,Chrome DevTools UCRUISERL。个中,WebSocket
URL
不畏Node.js和Chrome之间的通讯地方;UUID是一个一定的标志,每贰个进度都会分配2个uuid,由此每一回调用会有出现差异的结果;Chrome
DevTools URL
正是大家的调度页面包车型地铁url。

如访问http://127.0.0.1:9229/json/list,我们将会获取一下结出,在这之中devtoolsFrontendUrl正是大家必要拜访的地方。

亚洲必赢官网 10

显示屏快速照相 2017-11-2贰 1四.3四.壹三.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,可是接到那条音信,V⑧其实就干了两件业务事情:

  • SetDebugEventListener:
    给 JavaScript 调节和测试安装监听器,并安装 v8DebugEventCallback
    那个回调函数。JavaScript
    全部的调节和测试事件,都会被这些监听器捕获,包含:JavaScript
    非凡停止,断点截止,单步调节和测试等等。
  • compileDebuggerScript:
    编译 V8 内置的 JavaScript 文件
    debugger-script.js。由于这文件相比长,小编那边就不贴出来了,感兴趣的同学点击这一个链接实行查看源码。debugger-script.js
    主假如概念了部分针对 JavaScript
    断点举行操作的函数,举个例子设置断点、查找断点以及单步调节和测试相关的函数。那么那么些
    debugger-script.js 文件,被 V八 实行编写翻译之后,保存在 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` 正是 V八 重临的 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。

密切的你,可能还开采,与大家所写的顺序差别的是,调节和测试页面中,将我们的先后包裹在3个函数中,因为浏览器中不设有对应的对象(实际原因能够活动去斟酌)。

使用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 和断点的行号。

首先,V八 会去找,是还是不是曾经存在了该 U揽胜极光L 对应的 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 给具有的断点,创立3个 breakpointObject。并将那几个 braekpointObject 以
的样式存放在三个 Map 里面,而以此 Key,正是以此 JavaScript 文件的
U锐界L。看到那里,已经得以分解许多同班在调节和测试 JavaScript
遇到的二个难点:,>

某个同学为了防备页面包车型地铁 JavaScript 文件不立异,对于部分第3的
JavaScript 文件的 U福特ExplorerL 增添访问时间戳,对于那一个加多了走访时间戳的
JavaScript 文件进行安装断点然后刷新调节和测试的时候,Chrome 会打字与印刷2个warnning,告诉你断点丢失。

由来非常的粗略,在调节和测试的时候,V8 发掘那个 breakpointMap 里面找不到相应的
breakpointObject,因为 ULANDL 发生了变通,那一个 brakpointObject
就丢掉了,所以 V八 就找不到了,不能打开断点调节和测试。

依赖大家的例行思维,你可能会感觉 V八 会将断点设置在 C++
中,其实一齐先自己也是如此认为。随着对 V八的钻探,让本身见到了本身时曾相识的某些函数名:

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。随着对
V八 Debugger 的更为探寻,笔者发觉,V八 实际上对那些对那么些 breakpointObject
设置了 2 次。一遍是因此在 C++ 中调用 m_debuggerScript 的 setBreakpoint
设置到 JavaScript 的 context 里面,也正是上边那段 C++
逻辑做的事务。另三回是,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.七.0+),进而不只怕采纳,或者会报错(v8.9.一版本直接报错)。

vsCode + webStorm
提供第3手调节和测试功用(未利用过)。

检查评定文件退换

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

断点命中

是因为 V8 对 JavaScript 是立即编写翻译推行的,没有生成
bytecode,而是一向生成的 machine code
实践的,所以那个断点回调函数也会棉被服装置到这几个 machine code 里面。

提起底触发断点事件,也是 V八 的 C++ Runtime。当用户刷新恐怕直接试行JavaScript 的逻辑的时候,实际上是 V8 C++ Runtime 在运行 JavaScript
片段发生的 machine code,这几个 machine code
已经包涵了断点回调函数了。1旦那一个 machine code
里面包车型地铁回调函数被触发,接着就会接触以前 Debugger.enable
设置的调治将养事件监听器 Debug伊夫ntListener 的回调函数。并赶回一条音讯给
Chrome 的 devtool,告诉 Chrome devtool,当前 JavaScript 被 pause
的行号。到此甘休,多个断点就被击中了。

有关 JavaScript
断点命中,其实是2个很复杂的长河。前面有时光的话,会特意讲讲 JavaScript
断点命中的详细逻辑。

标题及消除措施

总结

浏览器的调试,最后都落脚到引擎:渲染引擎和 JavaScipt 引擎。那么对于
JavaScript 调节和测试来讲,难题就在于 V8 怎样给 JavaScript
某一行进行标识然后开始展览断点,那须要有有个别 V八 的文化。

2 赞 3 收藏 1
评论

亚洲必赢官网 16

一. 英特网守旧的老式设置方法访问地址

亚洲必赢官网 17

老式设置方式

并发的难题,便是依照上述步骤操作之后,大家并不曾发觉Node
debugging的选项,此时,就不明了哪些进展下去了。在那边给出的分解是,在新式版的Chrome浏览器中,Node
debugging不需要手动去运转了,而是私下认可就是能够选取的

1. 见仁见智Node版本出现的标题

各自在八个大学本科子下进展了测试(v陆.玖.二,v柒.叁.0,v8.9.壹)

v陆.玖.2 版本下使用状态

亚洲必赢官网 18

命令行实践结果

亚洲必赢官网 19

亚洲必赢官网 ,访问

那儿你会意识,命令行已经给您拜访Chrome的url,并且那些url和走访http://127.0.0.1:9229/json/list获取的devtoolsFrontendUrl属性值同样,不过,当你拜访这么些url时,浏览器却不可能显得调节和测试页面,此时,表明你的Node版本太低,要求选用越来越高版本。

v七.三.0 版本下行使情况

亚洲必赢官网 20

命令行试行结果

亚洲必赢官网 21

访问

亚洲必赢官网 22

调治将养页面

那会儿,命令行也提供了访问调节和测试页面包车型客车链接。

v八.玖.1 版本下行使境况

亚洲必赢官网 23

命令行实行结果

亚洲必赢官网 24

访问

亚洲必赢官网 25

调度页面

那儿,命令行给定的的url并不是调解页面包车型大巴url,因而必须透过访问http://127.0.0.1:9229/json/list来获得调节和测试页面url。

有关文档

Debugging
Guide
Debugging Node.js
Apps

网站地图xml地图