揭示浏览器远程调节和测试技艺

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

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

金沙注册送58 1

金沙注册送58 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

调护诊治技能的源点

1九四柒 年 玖 月 九 日,一名米利坚的化学家格蕾丝.霍普和他的同伙在对 马克 II
Computer举办商讨的时候发掘,二只飞蛾粘在3个继电器上,导致计算机不可能符合规律办事,当她们把飞蛾移除之后,Computer又苏醒了符合规律运作。于是他们将那只飞蛾贴在了他们马上记录的日记上,对这件业务进展了详尽的记录,并在日记最后写了那样一句话:First
actual case of bug being found。这是他们挖掘的率先个实在意义上的
bug,那也是全人类Computer软件历史上,开掘的第三个bug,而他们找到飞蛾的办法和进度,就是 debugging 调试本事。

金沙注册送58 3

从格蕾丝调节和测试第二个 bug 到方今,6九年的时刻里,在微型Computer领域,硬件、软件各个调治技巧都在相连的进步和产生。那么对于旭日东升的前端来讲,调试技能也越来越显得至关心珍视要。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 V八引擎和ECMASC牧马人IPT之上的。

调弄整理方式与权力管理

金沙注册送58 4

现阶段例行浏览器调节和测试目标分为三种:Chrome PC 浏览器和 Chrome
Mobile(Android 四.4 以往,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
    的调治开关:

编制程序式调节和测试node程序的利器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表明协议内容是工具自带的商业事务文本,并不是来自调试目标。
    假诺要博得调节和测试目的的交涉内容要抬高-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 Error3个不当对象,要是有不当的话

选择样例:

```

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 (金沙注册送58 ,!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激活后加载2个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.

两者的不2秘籍是不均等的

权限管理

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版本

    金沙注册送58 5

    Chroem版本

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

    金沙注册送58 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二7.0.0.一:9222/devtools/page/ebdace60-d4八贰-4340-b62二-a619八e7aad陆e”,
“id”: “ebdace60-d4捌贰-4340-b622-a61九八e柒aad陆e”, “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"
  }
]

中间,最关键的 二 个参数分别是 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,每1个页面都有谈得来分歧的
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的1个端口
  3. Webstrom IDE调节和测试 卓殊好用
  4. console.log

什么样得以完成 JavaScript 调节和测试

在进入 Chrome 的 devtool 之后,咱们得以调出调控台,来查阅 devtool 的
WebSocket 数据。这么些里面有为数不少数目,小编那边只讲跟 JavaScript
调试相关的。
金沙注册送58 7

图中,对于 JavaScript 调节和测试,有一条十分主要的音信,小编暗绛红选中的那条新闻:

JavaScript

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

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

下一场选中要调整的 JavaScript 文件,然后设置四个断点,大家再来看看
WebSocket 音信:
金沙注册送58 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 那样一个 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')
});

金沙注册送58 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地址,常常为1贰7.0.0.壹,port则是端口号,默感到922玖,那是Node提要求自由调解工具链接调节和测试的情商,能够由此命令行参数实行修改参数文书档案地址),会回来相应http请求的元数据,包罗WebSocket
U智跑L,UUID,Chrome DevTools UGL450L。在那之中,WebSocket
URL
尽管Node.js和Chrome之间的通讯地方;UUID是3个一定的标记,每1个进度都会分配一个uuid,因而每2遍调用会有出现分歧的结果;Chrome
DevTools URL
正是大家的调整页面包车型大巴url。

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

金沙注册送58 10

荧屏快照 2017-1一-2贰 14.34.1三.png

最终,访问那些地点后出示页面:

金沙注册送58 11

调护医治页面

使用Node.js Debugger

node debug hello.js

next 火速键n 跳到下一个言语
cont 快速键c 跳到下1个断点
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 文件,被 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` 正是 V8 再次来到的 JavaScript
    源码,如此的话,我们就可以在 devtool 中看看我们要调度的 JavaScript
    源码了。

情况2(重要)

借使你的脚本运维完事后一贯甘休进度,那么您必要运用–inspect-brk来运维调节和测试器,这样使得脚本能够代码试行在此以前break,不然,整个代码直接运营到代码结尾,停止进度,根本不可能进行调治将养。如:

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

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

    金沙注册送58 12

    直白动用–inspect

一贯利用–inspect,代码推行完成后,进度一贯截止,因而尚未章程在拓展调护诊治。

  1. 使用–inspect-brk:

    金沙注册送58 13

    使用–inspect-brk

金沙注册送58 14

访问127.0.0.1:9229

金沙注册送58 15

调整页面

从地点三张图中得以见见,此时该Node进度并从未直接退出,并且大家得以经过访问http://127.0.0.1:9229/json/list获得页面url,并且在调解页面中我们也发现,程序在率先行break。

有心人的您,大概还开掘,与大家所写的次序区别的是,调试页面中,将大家的次第包裹在二个函数中,因为浏览器中不存在对应的靶子(实际原因能够自动去斟酌)。

使用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 传递了3个 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 给全数的断点,成立一个 breakpointObject。并将那个 braekpointObject 以
的样式存放在一个 Map 里面,而以此 Key,便是其壹 JavaScript 文件的
UPRADOL。看到那里,已经足以表明繁多同室在调节和测试 JavaScript
遭逢的八个题材:,>

稍微同学为了避防万一页面包车型客车 JavaScript 文件不更新,对于部分珍视的
JavaScript 文件的 U景逸SUVL 增加访问时间戳,对于这么些增多了拜访时间戳的
JavaScript 文件实行安装断点然后刷新调节和测试的时候,Chrome 会打字与印刷多个warnning,告诉你断点丢失。

由来很轻松,在调试的时候,V八 发现这些 breakpointMap 里面找不到相应的
breakpointObject,因为 U奥迪Q3L 爆发了变化,那几个 brakpointObject
就不见了,所以 V八 就找不到了,不恐怕开展断点调节和测试。

据书上说大家的例行思维,你可能会以为 V八 会将断点设置在 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。随着对
V八 Debugger 的尤为切磋,小编发掘,V8 实际上对这几个对这一个 breakpointObject
设置了 二 次。二次是通过在 C++ 中调用 m_debuggerScript 的 setBreakpoint
设置到 JavaScript 的 context 里面,也正是上面那段 C++
逻辑做的业务。另1回是,m_debuggerScript 反过来将断点消息设置到了 V捌的 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+),进而不能利用,恐怕会报错(v八.玖.1版本直接报错)。

vsCode + webStorm
提供直接调节和测试作用(未采用过)。

检查测试文件改造

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

断点命中

是因为 V捌 对 JavaScript 是立刻编写翻译实践的,未有生成
bytecode,而是直接扭转的 machine code
试行的,所以这么些断点回调函数也会棉被服装置到那一个 machine code 里面。

末尾触发断点事件,也是 V八 的 C++ Runtime。当用户刷新只怕直接执行JavaScript 的逻辑的时候,实际上是 V八 C++ Runtime 在运作 JavaScript
片段发生的 machine code,这么些 machine code
已经包括了断点回调函数了。一旦那几个 machine code
里面包车型大巴回调函数被触发,接着就会接触在此之前 Debugger.enable
设置的调节和测试事件监听器 Debug伊芙ntListener 的回调函数。并再次来到一条消息给
Chrome 的 devtool,告诉 Chrome devtool,当前 JavaScript 被 pause
的行号。到此停止,三个断点就被击中了。

有关 JavaScript
断点命中,其实是多少个很复杂的长河。前边有时光以来,会专程讲讲 JavaScript
断点命中的详细逻辑。

难点及缓慢解决格局

总结

浏览器的调和,最后都落脚到引擎:渲染引擎和 JavaScipt 引擎。那么对于
JavaScript 调节和测试来讲,难题就在于 V八 如何给 JavaScript
某一行开始展览标识然后开展断点,那必要有少数 V八 的学识。

2 赞 3 收藏 1
评论

金沙注册送58 16

一. 网络守旧的过时设置格局访问地址

金沙注册送58 17

老式设置方式

出现的标题,便是依照上述步骤操作之后,我们并从未察觉Node
debugging的选项,此时,就不知晓什么样进展下去了。在那边给出的解说是,在最新版的Chrome浏览器中,Node
debugging不必要手动去运维了,而是默许正是足以采取的

壹. 区别Node版本出现的主题素材

分别在八个大版本下进行了测试(v陆.玖.二,v7.三.0,v8.9.一)

v陆.玖.二 版本下使用景况

金沙注册送58 18

命令行试行结果

金沙注册送58 19

访问

那时候您会开采,命令行已经给你拜访Chrome的url,并且那几个url和访问http://127.0.0.1:9229/json/list获取的devtoolsFrontendUrl属性值同样,可是,当你拜访那么些url时,浏览器却不能显得调试页面,此时,表达您的Node版本太低,供给动用越来越高版本。

v7.三.0 版本下采用景况

金沙注册送58 20

命令行施行结果

金沙注册送58 21

访问

金沙注册送58 22

调护治疗页面

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

v八.九.1 版本下行使状态

金沙注册送58 23

命令行施行结果

金沙注册送58 24

访问

金沙注册送58 25

调度页面

这时候,命令行给定的的url并不是调弄整理页面的url,由此必须通过访问http://127.0.0.1:9229/json/list来赢得调节和测试页面url。

相关文书档案

Debugging
Guide
Debugging Node.js
Apps

相关文章

网站地图xml地图