Skip to content

Debugger breakpoints using incorrect URL for a remote debugging session and therefore failing to pause the process #103

@treffynnon

Description

@treffynnon

Symptoms

This is related to the closed ticket #99, which describes the symptoms of this very problem.

You set a breakpoint, but the process isn't paused when that breakpoint is encountered and carries merrily on.

Environment

This is a remote debugging session between a Windows client (running Chrome devtools) and a remote Linux server (running node with --inspect=0.0.0.0).

What's wrong

By watching the websocket communication with Wireshark it is apparent that the Chrome Dev tools are using an incorrect filepath/URL when calling Debugger.setBreakpointByUrl.

What I am seeing

Request

{
  "id": 17,
  "method": "Debugger.setBreakpointByUrl",
  "params": {
    "lineNumber": 67,
    "url": "EFS\\DEV-efsbastion\\username\\application\\projects\\service\\module\\index.js",
    "columnNumber": 18,
    "condition": ""
  }
}

Response

{
  "id": 17,
  "result": {
    "breakpointId": "EFS\\DEV-efsbastion\\username\\application\\projects\\service\\module\\index.js:67:18",
    "locations": []
  }
}

Note the empty locations array. I am no node debugger expert, but it feels like this locations array should be checked to ensure it is not empty after setting a new breakpoint marker before then showing the marker as active in the UI.

What I should be seeing

Request

{
  "id": 17,
  "method": "Debugger.setBreakpointByUrl",
  "params": {
    "lineNumber": 67,
    "url": "/EFS/DEV-efsbastion/username/application/projects/service/module/index.js",
    "columnNumber": 18,
    "condition": ""
  }
}

Response

{
  "id": 17,
  "result": {
    "breakpointId": "/EFS/DEV-efsbastion/username/application/projects/service/module/index.js:67:18",
    "locations": [{
      "scriptId":"104",
      "lineNumber":67,
      "columnNumber":18
    }]
  }
}

Note the Linux path separators instead of Windows and the initial root slash on the path.

But isn't this a node bug?

No, it is not from what I can tell because of the following two discoveries.

PHPStorm

PHPStorm/WebStorm debugger continues to work just fine using it's URL regexes

Request

{
  "id": 17,
  "method": "Debugger.setBreakpointByUrl",
  "params": {
    "lineNumber": 67,
    "urlRegex": "[/\\\\][iI][nN][dD][eE][xX]\\.[jJ][sS]([;?#!].*)?$"
  }
}

Response

{
  "id": 17,
  "result": {
    "breakpointId": "/[/\\\\][iI][nN][dD][eE][xX]\\.[jJ][sS]([;?#!].*)?$/:67:18",
    "locations": [{
      "scriptId":"104",
      "lineNumber":67,
      "columnNumber":18
    }]
  }
}

Manually recreating the request successfully

If I manually set the breakpoint myself I see a success response back over the websocket. For example consider the following code run from the Chrome console:

let id, key, socket

id = 1000
key = '<debugger-key-goes-here>'

const connect = userSuppliedKey => {
	socket = new WebSocket(`ws://<ip-address-goes-here>:9229/${userSuppliedKey || key}`)
	socket.addEventListener('message', e => {
		const d = JSON.parse(e.data)
		if (d) {
			if (d.method === 'Debugger.scriptParsed') return console.log('scriptParsed', JSON.stringify({ scriptId: d.params.scriptId, url: d.params.url }))
			if (d.method === 'Debugger.breakpointResolved') return console.log('breakpointResolved', JSON.stringify(Object.assign({ breakpointId: d.params.breakpointId }, d.params.location)))
		}
		console.info(`${e.data}`)
	})
}
const send = (method, x) => socket.send(JSON.stringify({ id: ++id, method: `Debugger.${method}`, params: x }))
const _getPossibleBps = (start, end) => send('getPossibleBreakpoints', { start, end })
const getPossibleBps = (scriptId, start, end) => _getPossibleBps({ scriptId, lineNumber: start }, { scriptId, lineNumber: end })
const setBp = (lineNumber, url) => send('setBreakpointByUrl', { lineNumber, url, })
const removeBp = (lineNumber, url, columnNumber) => send('removeBreakpoint', { breakpointId: `${url}:${lineNumber}:${columnNumber || 0}` })
const enable = () => send('enable')

Which gives us this when run in the Chrome console

connect('<debugger-key-goes-here>')
// '<debugger-key-goes-here>'

enable()
// ...
// scriptParsed {"scriptId":"104","url":"/EFS/DEV-efsbastion/username/application/projects/service/module/index.js"}
// ...

getPossibleBps('104', 9, 15)
// {"id":1016,"result":{"locations":[{"scriptId":"104","lineNumber":9,"columnNumber":15},{"scriptId":"104","lineNumber":11,"columnNumber":15},{"scriptId":"104","lineNumber":12,"columnNumber":16},{"scriptId":"104","lineNumber":14,"columnNumber":4},{"scriptId":"104","lineNumber":15,"columnNumber":0},{"scriptId":"104","lineNumber":17,"columnNumber":4},{"scriptId":"104","lineNumber":18,"columnNumber":14}]}}

setBp(11, '/EFS/DEV-efsbastion/username/application/projects/service/module/index.js')
// {"id":1024,"result":{"breakpointId":"/EFS/DEV-efsbastion/username/application/projects/service/module/index.js:11:0","locations":[{"scriptId":"104","lineNumber":11,"columnNumber":15}]}}

removeBp(11, '/EFS/DEV-efsbastion/username/application/projects/service/module/index.js')
// {"id":1019,"result":{}}

Possible introduction of the issue

https://chromium.googlesource.com/chromium/src/+/2140ffeadf3e081e2e0821fc8e1ee040373b205d%5E%21/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js

in combination with https://chromium.googlesource.com/chromium/src/+blame/master/third_party/blink/renderer/devtools/front_end/common/ParsedURL.js#103

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions