使用vscode-chrome-debug-core调试nodejs

顾鸣
2023-12-01

初始化

首先启动一个nodejs的进程,加上参数–inspect-brk=58580,58580是一个自定义的端口,然后该进程会停在开始的位置,并输出类似的log:

Debugger listening on ws://127.0.0.1:58580/96f1cb2b-6a8b-4745-a842-3d91e6ec61c9
For help, see: https://nodejs.org/en/docs/inspector
import {ChromeDebugAdapter, ChromeDebugSession} from 'vscode-chrome-debug-core'
let debugAdapter
//其中log是上述的nodejs进程输出的log,通过分析这个log可以得到port信息
//logFilePath是一个路径,log文件路径,onPaused是回调函数,在收到paused事件时会调用
//可以传给onPaused传一些参数,比如当前变量值,当前监视的表达式的值,当前脚本所在的行,当前脚本的内容等
async function init (log, logFilePath, onPaused) {
  debugAdapter = new ChromeDebugAdapter(
    {
      enableSourceMapCaching: true,
      logFilePath: logPath
    },
    new ChromeDebugSession(true, false, {
      adapter: ChromeDebugAdapter,
      extensionName: 'Debugger',
      logFilePath: logFilePath
    })
  )
  //启动debugger
  await debugAdapter.attach({
    port: getDebugPort(log)
  })
  debugAdapter.chrome.Debugger.on('paused', async (pausedEvent, data) => {
    if (pausedEvent.reason === 'Break on start') {
      //停止原因是在最开始的位置停止了
    } else {
      //其他原因停止
    }
    if (pausedEvent.hitBreakpoints.length > 0) {
       //表示当前停止位置有断点,可以做一些处理
    }
    //在这里可以做一些其他操作
    //获得当前文件的文本信息
    //let scripts = await getScripts(pausedEvent.callFrames[0].url)
    //修改变量的值,其中参数分别是callFrameId,scopeIndex,variable name, variable value
    //就是把当前的callFrame里面的变量a的值设置为了dd
    //await setVariableValue(pausedEvent.callFrames[0].callFrameId, 0, 'a', '"dd"')
    //获取所有的变量值
    //await getVariables(pausedEvent.callFrames)
    //获取表达式a+b的值
    //await evaluateOnCallFrame('a+b', pausedEvent.callFrames[0])
    //再比如,next,stepIn,stepOut,continue等操作
    onPaused()
  })
  debugAdapter.chrome.Debugger.on('resumed', (event) => {
    // console.log(event, '====resumed')
  })
  debugAdapter.chrome.Debugger.on('scriptParsed', async event => {
    // console.log(event, '=====scriptParsed')
  })
}

设置断点

//breakpoints是断点集合,let breakpoints = {'/root/index.js': [{line: 2,id: 0}]}
//key是文件路径,value是断点位置集合,id是唯一的数字
async function resetBreakpoints (breakpoints) {
  if (!debugAdapter) return
  for (let file in breakpoints) {
    let breakpointData = getBreakpoints(file, breakpoints[file])
    await debugAdapter.setBreakpoints(breakpointData.breakpoints, undefined, sessionRequestId++, breakpointData.ids)
  }
}
function getBreakpoints (file, breakpoints) {
  let result = {
    breakpoints: {source: {path: file}, breakpoints: []},
    ids: []
  }
  for (let index in breakpoints) {
    result.breakpoints.breakpoints.push({line: breakpoints[index].line, column: 1})
    result.ids.push(breakpoints[index].id)
  }
  return result
}

变量相关操作

没有找到相关的文档介绍,看源码也没什么注释,我也不是太清楚自己用的是不是完全正确,不过,就获得的结果来看,以下的函数没有发现大的问题,如果哪里写的有问题,欢迎指正。

//设置变量值
async function setVariableValue (callFrameId, scopeIndex, name, value) {
  await debugAdapter.setVariableValue(callFrameId, scopeIndex, name, value)
}
//获取表达式值
async function evaluateOnCallFrame (expression, callFrame) {
  let res = await debugAdapter.evaluateOnCallFrame(expression, callFrame)
  return res
}
//获得指定url的脚本的内容
async function getScripts (url) {
  let script = debugAdapter.getScriptByUrl(url)
  let source = await debugAdapter.scriptToSource(script)
  let str = await debugAdapter.source(source)
  let scripts = str.content.split('\n')}
  return scripts
}
//获取类型为local的所有变量的值(这里我过滤了其他的种类,还有global等,如果想获得所有的话,就取消过滤)
async function getVariables () {
  let scopes = []
  for (let i = 0; i < currentCallFrames[0].scopeChain.length; i++) {
    let scope = currentCallFrames[0].scopeChain[i]
    if (scope.type === 'local') {
      let objectId = scope.object.objectId
      let variables = await debugAdapter.getVariablesForObjectId(objectId)
      scopes.push({
        type: scope.type,
        scopeIndex: i,
        variables: variables,
        frameId: currentCallFrames[0].callFrameId
      })
    }
  }
  return scopes
}

调试步骤相关操作

async function next () {
  await debugAdapter.next()
}
async function stepIn () {
  await debugAdapter.stepIn()
}
async function stepOut () {
  await debugAdapter.stepOut()
}
//起名continue有点问题,就改了个名字
async function resume () {
  await debugAdapter.continue()
}

这里面有些方法在定义的ts文件里面标注为protected,private的,本意可能并不想用户调用,不过js你们了解的了啊,就没有不能调用的,虽然使用的可能有点问题,不过目前看来好用(谁让文档这么坑,读源码我的耐心又有限呢,一切以实现功能为目标,其他的都不重要,,,)。如果大家有更好的实现方法,欢迎分享。

 类似资料: