Closed targos closed 4 years ago
@nodejs/inspector
This is with V8 lkgr (canary branch of this repo)
FYI, I'm looking into this (couldn't find any PRs referencing this issue, so I assume no one fixed it yet). The child process (the process being inspected) is segfaulting when it receives a Debugger.pause
. I was able to reproduce this outside test with:
// script.js
const { Session } = require('inspector');
const session = new Session();
let done = false;
const interval = setInterval(() => {
process._rawDebug('hey oh');
if (done)
clearInterval(interval);
}, 1000);
session.on('Debugger.paused', () => {
done = true;
});
session.connect();
session.post('Debugger.enable');
console.log('Ready');
// connect.js
'use strict'
const http = require("http");
const WebSocket = require('ws');
function usage() {
console.error('USAGE: node index.js <PID>')
process.exit(1)
}
if (process.argv.length !== 3) usage();
const pid = Number(process.argv[2])
if (pid === NaN) usage();
let first = true;
function connectToInspector(url) {
const ws = new WebSocket(url);
ws.on('message', function incoming(data) {
const res = JSON.parse(data);
if (first && res.method == "Debugger.paused") {
first = false;
ws.send(JSON.stringify({"method": "Debugger.resume", "id": 4}));
}
if (res.method == "Debugger.resumed")
ws.send(JSON.stringify({"method": "Debugger.pause", "id": 3}));
if (res.method != "Debugger.scriptParsed")
console.log(res);
});
ws.on('open', function open() {
ws.send(JSON.stringify({"method": "Debugger.enable", "id": 1}));
ws.send(JSON.stringify({"method": "Runtime.runIfWaitingForDebugger", "id": 2}));
});
}
function getWebSocketPath(cb) {
http.get("http://localhost:9229/json", (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => cb(JSON.parse(data)[0].webSocketDebuggerUrl));
})
}
process.kill(pid, 'SIGUSR1');
getWebSocketPath(connectToInspector);
$ node --inspect-brk script.js &
[1] 13214
Debugger listening on ws://127.0.0.1:9229/6680ec92-a986-4a8c-8f2f-0daee5deabfb
For help, see: https://nodejs.org/en/docs/inspector
$ node connect.js $(pgrep node)
Debugger attached.
{
id: 1,
result: { debuggerId: '-5779516113788299134.3822508824226157107' }
}
{ id: 2, result: {} }
{
method: 'Debugger.paused',
params: {
callFrames: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
],
reason: 'Break on start',
hitBreakpoints: []
}
}
{ id: 4, result: {} }
{ method: 'Debugger.resumed', params: {} }
{ id: 3, result: {} }
[1] + 14390 segmentation fault (core dumped) /home/mmarchini/workspace/nodejs/node-v8/node --inspect-brk fooo.js
Stack from core dump:
* thread nodejs/node-v8#1, name = 'node', stop reason = signal SIGSEGV
* frame #0: 0x0000564beda33b47 node`v8::internal::ScopeIterator::Type() const + 55
frame nodejs/node-v8#1: 0x0000564beda321c0 node`v8::internal::DebugScopeIterator::Advance() + 48
frame nodejs/node-v8#2: 0x0000564bee052d44 node`v8_inspector::V8DebuggerAgentImpl::currentCallFrames(std::unique_ptr<std::vector<std::unique_ptr<v8_inspector::protocol::Debugger::CallFrame, std::default_delete<v8_inspector::protocol::Debugger::CallFrame> >, std::allocator<std::unique_ptr<v8_inspector::protocol::Debugger::CallFrame, std::default_delete<v8_inspector::protocol::Debugger::CallFrame> > > >, std::default_delete<std::vector<std::unique_ptr<v8_inspector::protocol::Debugger::CallFrame, std::default_delete<v8_inspector::protocol::Debugger::CallFrame> >, std::allocator<std::unique_ptr<v8_inspector::protocol::Debugger::CallFrame, std::default_delete<v8_inspector::protocol::Debugger::CallFrame> > > > > >*) + 2404
frame nodejs/node-v8#3: 0x0000564bee059fca node`v8_inspector::V8DebuggerAgentImpl::didPause(int, v8::Local<v8::Value>, std::vector<int, std::allocator<int> > const&, v8::debug::ExceptionType, bool, bool, bool) + 1386
frame nodejs/node-v8#4: 0x0000564bee04185a node`std::_Function_handler<void (v8_inspector::V8InspectorSessionImpl*), v8_inspector::V8Debugger::handleProgramBreak(v8::Local<v8::Context>, v8::Local<v8::Value>, std::vector<int, std::allocator<int> > const&, v8::debug::ExceptionType, bool)::'lambda0'(v8_inspector::V8InspectorSessionImpl*)>::_M_invoke(std::_Any_data const&, v8_inspector::V8InspectorSessionImpl*&&) + 170
frame nodejs/node-v8#5: 0x0000564bee064314 node`v8_inspector::V8InspectorImpl::forEachSession(int, std::function<void (v8_inspector::V8InspectorSessionImpl*)> const&) + 452
frame nodejs/node-v8#6: 0x0000564bee044abf node`v8_inspector::V8Debugger::handleProgramBreak(v8::Local<v8::Context>, v8::Local<v8::Value>, std::vector<int, std::allocator<int> > const&, v8::debug::ExceptionType, bool) + 511
frame nodejs/node-v8#7: 0x0000564beda44386 node`v8::internal::Debug::OnDebugBreak(v8::internal::Handle<v8::internal::FixedArray>) + 486
frame nodejs/node-v8#8: 0x0000564beda44530 node`v8::internal::Debug::HandleDebugBreak(v8::internal::IgnoreBreakMode) + 384
frame nodejs/node-v8#9: 0x0000564bed945f0d node`v8::debug::BreakRightNow(v8::Isolate*) + 45
frame nodejs/node-v8#10: 0x0000564beda877e3 node`v8::internal::Isolate::InvokeApiInterruptCallbacks() + 163
frame nodejs/node-v8#11: 0x0000564bedaa007d node`v8::internal::StackGuard::HandleInterrupts() + 557
frame nodejs/node-v8#12: 0x0000564bede60e9d node`v8::internal::Runtime_StackGuard(int, unsigned long*, v8::internal::Isolate*) + 109
frame nodejs/node-v8#13: 0x0000564bee1c06d9 node`Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit + 57
frame nodejs/node-v8#14: 0x0000564bee159213 node`Builtins_InterpreterEntryTrampoline + 307
frame nodejs/node-v8#15: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#16: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#17: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#18: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#19: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#20: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#21: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#22: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#23: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#24: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#25: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#26: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#27: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#28: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#29: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#30: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#31: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#32: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#33: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#34: 0x0000564bee156dfa node`Builtins_JSEntryTrampoline + 90
frame nodejs/node-v8#35: 0x0000564bee156bd8 node`Builtins_JSEntry + 120
frame nodejs/node-v8#36: 0x0000564beda724ae node`v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) + 446
frame nodejs/node-v8#37: 0x0000564beda732f7 node`v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) + 119
frame nodejs/node-v8#38: 0x0000564bed931863 node`v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 323
frame nodejs/node-v8#39: 0x0000564bed84e22e node`node::inspector::(anonymous namespace)::CallAndPauseOnStart(v8::FunctionCallbackInfo<v8::Value> const&) + 382
frame nodejs/node-v8#40: 0x0000564bed98c17f node`v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) + 367
frame nodejs/node-v8#41: 0x0000564bed98c540 node`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) + 208
frame nodejs/node-v8#42: 0x0000564bed98cdda node`v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) + 266
frame nodejs/node-v8#43: 0x0000564bed98d68a node`v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) + 26
frame nodejs/node-v8#44: 0x0000564bee1c07b9 node`Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 57
frame nodejs/node-v8#45: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#46: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#47: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#48: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#49: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#50: 0x0000564bee15919e node`Builtins_InterpreterEntryTrampoline + 190
frame nodejs/node-v8#51: 0x0000564bee156dfa node`Builtins_JSEntryTrampoline + 90
frame nodejs/node-v8#52: 0x0000564bee156bd8 node`Builtins_JSEntry + 120
frame nodejs/node-v8#53: 0x0000564beda724ae node`v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) + 446
frame nodejs/node-v8#54: 0x0000564beda732f7 node`v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) + 119
frame nodejs/node-v8#55: 0x0000564bed931863 node`v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 323
frame nodejs/node-v8#56: 0x0000564bed71a0fa node`node::ExecuteBootstrapper(node::Environment*, char const*, std::vector<v8::Local<v8::String>, std::allocator<v8::Local<v8::String> > >*, std::vector<v8::Local<v8::Value>, std::allocator<v8::Local<v8::Value> > >*) + 138
frame nodejs/node-v8#57: 0x0000564bed71afc3 node`node::StartExecution(node::Environment*, char const*) + 483
frame nodejs/node-v8#58: 0x0000564bed71b657 node`node::StartMainThreadExecution(node::Environment*) + 1511
frame nodejs/node-v8#59: 0x0000564bed7988fd node`node::NodeMainInstance::Run() + 557
frame nodejs/node-v8#60: 0x0000564bed71d92b node`node::Start(int, char**) + 251
frame nodejs/node-v8#61: 0x00007efe201aa1e3 libc.so.6`__libc_start_main(main=(node`main), argc=3, argv=0x00007fffa32fa788, init=<unavailable>, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffa32fa778) at libc-start.c:308
frame nodejs/node-v8#62: 0x0000564bed67803e node`_start + 46
Interestingly enough, this only happens with --inspect-brk
-> Debugger.resume
-> Debugger.pause
. --inspect
-> Debugger.pause
works just fine. And even more interesting, if I connect Chrome DevTools to node --inspect-brk script.js
, resume and pause afterwards, it doesn't crash.
I'll keep investigating and share any results I find.
My current theory is that we're hitting pause right after a script is loaded, parsed, maybe even compiled, and pushed onto the stack, but it's not quite ready to run yet.
Why this is happening so frequently? When session.on
runs, it will require
files in the inspector thread. When console.log
runs, it will require several files if they were not required yet. Since the script is so small and only requires inspect
, most Node.js modules are not loaded by defautl (on most common cases, those modules will be loaded when a pause happens, thus the crash doesn't happen). This test is, unfortunately (or fortunately), well crafted enough to trigger this crash quite frequently, because it has several requires happening indirectly "during execution" (not as consequence of direct requires).
Why this is not happening on master? No idea
Is this a bug on V8, Node.js or in our test? I'm inclined to say V8, but want to look a bit further before opening a bug anywhere.
There are a few fixes we can apply to the test if we want to unblock the upgrade and investigate this issue in parallel. Let me know if that's something you want, I can send a patch.
@mmarchini thanks a lot for looking into this!
Likely caused by this change: https://chromium-review.googlesource.com/c/v8/v8/+/1876821. Now the NativeContext has a SCRIPT_SCOPE attached to it instead of a FUNCTION_SCOPE. Will keep digging, I think we might be using the NativeContext in an incompatible way compared to the new behavior.
Is this test flaky ? it's not seeing in node-v8 master.
Is this test flaky ?
Yes, but the failing is legit
it's not seeing in node-v8 master.
Do you mean canary
? Let me try it
Do you mean canary? Let me try it
yeap, only 3 test failed, but this one is not one of them.
Still happening:
$ ./node
[test] Connecting to a child Node process
[test] Testing /json/list
[test] Connecting to a child Node process
[test] Testing /json/list
[err] Debugger listening on ws://127.0.0.1:44189/05bc75ec-1ceb-4da4-a207-53573d3b400d
[err] For help, see: https://nodejs.org/en/docs/inspector
[err]
[err] Debugger attached.
[err] Debugger attached.
[err]
[test] Breaking in code and verifying events are fired
Timed out waiting for matching notification (SessionA paused))
1
I also used one of the scripts described in https://github.com/nodejs/node-v8/issues/120#issuecomment-589939128, and it is still segfaulting.
I'm inclined to say this is a bug on V8. I'm trying to come up with a test on V8 for this, but writing a custom inspector test is somewhat investing, so it's taking some time.
I was able to confirm (using bpftrace and some debugging printf's) that the condition for the segfault to happen is:
ScriptCompiler::CompileFunctionInContext
The most likely scenario for this to happen is when we try to pause while requiring an internal module, although in theory we can probably trigger it with vm.compileFunction
.
It also failed on the latest Jenkins run: https://ci.nodejs.org/job/node-test-commit-linux/33109/nodes=ubuntu1804-64/consoleFull
Weird github CI not reporting this error. Any clue ? @cclauss
The test is super flaky (the Debugger.pause
inspector protocol call must happen at the exact "wrong" time), so in the run you linked the test passed. GitHub Actions won't report results from Jenkins.
Is it possible we biselect v8 commit to find the problem ?
The commit where it started is likely https://chromium-review.googlesource.com/c/v8/v8/+/1876821, but I haven't bisected to double check (since I don't have a test on V8 yet, would need to bisect on node-v8, which is too much effort). I think I'm close to have a repro on V8 though.
Ok, got a repro on V8. I had to copy an entire script there (tty.js), so I'm not sure what exactly is triggering the bug. Will start to refactor the test to see where the problem is.
V8 minimal repro: https://chromium-review.googlesource.com/c/v8/v8/+/2080072
Upstream bug: https://bugs.chromium.org/p/v8/issues/detail?id=10287
First V8 commit with the issue (found with git bisect
): https://chromium-review.googlesource.com/c/v8/v8/+/1903440
This is such an edge case I'm wondering if we should mark the test as flaky (and maybe add a test specific for this bug on known_issues
) and remove this from blockers. My reasoning is:
vm.compileFunction
, but I was not able to reproduce it)I am on adding it to known issue
(also not a fan of mutli session in debugging)
also not a fan of mutli session in debugging
FWIW this bug is unrelated to multi-session. It was a coincidence this happened on this test.
FWIW this bug is unrelated to multi-session. It was a coincidence this happened on this test.
Sorry for miss that part. Is there known issue
part for Node.js ? seems we only mark test flaky.
It's a folder there (https://github.com/nodejs/node/tree/master/test/known_issues, I only found out about it a few days ago). Tests there are expected to always fail, so test-inspector-multisession-ws
would stay where it is (but marked as flaky) and we could write another test that can reliably reproduce the issue (I have some ideas, will try later or tomorrow).
test-inspector-multisession-ws
always failed on my machine. It's not on your machine ?
Also, for anyone interested, v8 crashes in here
[err] #
[err] # Fatal error in ../../deps/v8/src/debug/debug-scopes.cc, line 454
[err] # Debug check failed: current_scope_->NeedsContext() implies context_->IsFunctionContext() || context_->IsDebugEvaluateContext().
test-inspector-multisession-ws
always failed on my machine
It failed rather consistently, but it would succeed occasionally.
We should open a tracking issue on nodejs/node, since the underlying bug still exists on V8
We should open a tracking issue on nodejs/node, since the underlying bug still exists on V8
Move this to issue to nodejs/node
?
I thought opening a new issue made more sense, but @targos was faster than me :)