| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 59cca26 commit 92a909c
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ const { | |||
| 25 | 25 | ||
| 26 | 26 | const { clearTimeout, setTimeout } = require('timers'); | |
| 27 | 27 | const { SideEffectFreeRegExpPrototypeSymbolReplace } = require('internal/util'); | |
| 28 | + const debug = require('internal/util/debuglog').debuglog('inspect_probe'); | ||
| 28 | 29 | ||
| 29 | 30 | const InspectClient = require('internal/debugger/inspect_client'); | |
| 30 | 31 | const { | |
@@ -477,6 +478,7 @@ class ProbeInspectorSession { | |||
| 477 | 478 | ||
| 478 | 479 | finish(exitCode, terminal) { | |
| 479 | 480 | if (this.finished) { return; } | |
| 481 | + debug('finish: exitCode=%d, terminal=%s', exitCode, terminal?.event); | ||
| 480 | 482 | this.finished = true; | |
| 481 | 483 | if (this.timeout !== null) { | |
| 482 | 484 | clearTimeout(this.timeout); | |
@@ -523,6 +525,8 @@ class ProbeInspectorSession { | |||
| 523 | 525 | } | |
| 524 | 526 | ||
| 525 | 527 | onChildExit(code, signal) { | |
| 528 | + debug('child exit: code=%s signal=%s connected=%s started=%s finished=%s inFlight=%j', | ||
| 529 | + code, signal, this.connected, this.started, this.finished, this.inFlight); | ||
| 526 | 530 | // Pre-connect exits are deliberately silent: the target never reached | |
| 527 | 531 | // a state where probes could be set, so any report would be empty. | |
| 528 | 532 | if (!this.connected) { return; } | |
@@ -543,6 +547,8 @@ class ProbeInspectorSession { | |||
| 543 | 547 | } | |
| 544 | 548 | ||
| 545 | 549 | onClientClose() { | |
| 550 | + debug('client close: disconnectRequested=%s finished=%s inFlight=%j', | ||
| 551 | + this.disconnectRequested, this.finished, this.inFlight); | ||
| 546 | 552 | if (!this.connected) { return; } | |
| 547 | 553 | if (this.disconnectRequested) { return; } | |
| 548 | 554 | if (this.finished) { return; } | |
@@ -668,13 +674,21 @@ class ProbeInspectorSession { | |||
| 668 | 674 | async callCdp(method, params, probe = null) { | |
| 669 | 675 | if (this.finished) { throw kInspectorFailedSentinel; } | |
| 670 | 676 | this.inFlight = { __proto__: null, method, probe }; | |
| 677 | + debug('CDP -> %s%s', method, probe !== null ? `, probe=${probe.index}` : ''); | ||
| 671 | 678 | try { | |
| 672 | 679 | const result = await this.client.callMethod(method, params); | |
| 673 | 680 | // A timeout or process exit can finish the report while the CDP request | |
| 674 | 681 | // is still outstanding. Ignore the late reply in that case. | |
| 675 | - if (this.finished) { throw kInspectorFailedSentinel; } | ||
| 682 | + if (this.finished) { | ||
| 683 | + debug('CDP <- %s discarded (already finished)', method); | ||
| 684 | + throw kInspectorFailedSentinel; | ||
| 685 | + } | ||
| 686 | + debug('CDP <- %s (success)', method); | ||
| 676 | 687 | return result; | |
| 677 | 688 | } catch (err) { | |
| 689 | + if (err !== kInspectorFailedSentinel) { // Already handled. | ||
| 690 | + debug('CDP <- %s error: %s', method, err?.code); | ||
| 691 | + } | ||
| 678 | 692 | if (this.disconnectRequested) { | |
| 679 | 693 | // Only the in-flight evaluation gets attribution. Other rejections | |
| 680 | 694 | // under disconnect are downstream noise. | |
@@ -722,6 +736,8 @@ class ProbeInspectorSession { | |||
| 722 | 736 | // Records the first inspector-side terminal for the session, later callers are ignored. | |
| 723 | 737 | recordInspectorFailure({ reason, advice, cdpError, internalError }) { | |
| 724 | 738 | if (this.finished) { return; } | |
| 739 | + debug('recordInspectorFailure "%s": inFlight=%j, lastProbeIndex=%s, cdpError=%j', | ||
| 740 | + reason, this.inFlight, this.lastProbeIndex, cdpError); | ||
| 725 | 741 | const child = this.child; | |
| 726 | 742 | const exitedAbnormally = child !== null && | |
| 727 | 743 | (child.signalCode !== null || (child.exitCode !== null && child.exitCode !== 0)); | |
@@ -789,6 +805,8 @@ class ProbeInspectorSession { | |||
| 789 | 805 | ||
| 790 | 806 | startTimeout() { | |
| 791 | 807 | this.timeout = setTimeout(() => { | |
| 808 | + debug('timeout fired: finished=%s, inFlight=%j, lastProbeIndex=%s', | ||
| 809 | + this.finished, this.inFlight, this.lastProbeIndex); | ||
| 792 | 810 | if (this.finished) { return; } | |
| 793 | 811 | if (this.inFlight !== null) { | |
| 794 | 812 | const hasProbeAttribution = | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -52,7 +52,7 @@ function normalizeProbeReport(value) { | |||
| 52 | 52 | } | |
| 53 | 53 | ||
| 54 | 54 | function assertProbeJson(output, expected) { | |
| 55 | - const normalized = JSON.parse(output); | ||
| 55 | + const normalized = typeof output === 'string' ? JSON.parse(output) : output; | ||
| 56 | 56 | const lastResult = normalized.results?.[normalized.results.length - 1]; | |
| 57 | 57 | ||
| 58 | 58 | if (isProbeSegvTeardown(lastResult)) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,7 @@ | |||
| 1 | 1 | // This tests that a probe expression resuming the target through its own | |
| 2 | - // inspector.Session surfaces as probe_failure. | ||
| 2 | + // inspector.Session is surfaced as a probe-side failure. The terminal event | ||
| 3 | + // can be either probe_failure or probe_timeout depending on a race in V8's | ||
| 4 | + // nested pause-loop drain. | ||
| 3 | 5 | 'use strict'; | |
| 4 | 6 | ||
| 5 | 7 | const common = require('../common'); | |
@@ -21,11 +23,11 @@ spawnSyncAndExit(process.execPath, [ | |||
| 21 | 23 | 'inspect', '--json', | |
| 22 | 24 | '--probe', `${fixture}:12`, '--expr', probes[0].expr, | |
| 23 | 25 | fixture, | |
| 24 | - ], { cwd }, { | ||
| 26 | + ], { cwd, env: { ...process.env, NODE_DEBUG: 'inspect_probe' } }, { | ||
| 25 | 27 | status: 1, | |
| 26 | 28 | signal: null, | |
| 27 | 29 | stdout(output) { | |
| 28 | - assertProbeJson(output, { | ||
| 30 | + const expected = { | ||
| 29 | 31 | v: 2, | |
| 30 | 32 | probes, | |
| 31 | 33 | results: [{ | |
@@ -34,7 +36,14 @@ spawnSyncAndExit(process.execPath, [ | |||
| 34 | 36 | hit: 1, | |
| 35 | 37 | location, | |
| 36 | 38 | result: { type: 'number', value: 1, description: '1' }, | |
| 37 | - }, { | ||
| 39 | + }] | ||
| 40 | + }; | ||
| 41 | + | ||
| 42 | + const actual = JSON.parse(output); | ||
| 43 | + | ||
| 44 | + const code = actual.results.at(-1)?.error?.code; | ||
| 45 | + if (code === 'probe_failure') { | ||
| 46 | + expected.results.push({ | ||
| 38 | 47 | event: 'error', | |
| 39 | 48 | pending: [], | |
| 40 | 49 | error: { | |
@@ -50,8 +59,22 @@ spawnSyncAndExit(process.execPath, [ | |||
| 50 | 59 | protocolError: { message: 'Can only perform operation while paused.', code: -32000 }, | |
| 51 | 60 | }, | |
| 52 | 61 | }, | |
| 53 | - }], | ||
| 54 | - }); | ||
| 62 | + }); | ||
| 63 | + } else if (code === 'probe_timeout') { | ||
| 64 | + // On slow CI, the outer Debugger.resume can be picked up in the same drain pass as | ||
| 65 | + // the Debugger.evaluateOnCallFrame, while V8 still considers the context paused. | ||
| 66 | + // In this case both resume calls may succeed and the process can continue running from | ||
| 67 | + // the setInterval until the timeout. | ||
| 68 | + expected.results.push({ | ||
| 69 | + event: 'timeout', | ||
| 70 | + pending: [], | ||
| 71 | + error: { | ||
| 72 | + code: 'probe_timeout', | ||
| 73 | + message: 'Timed out after 30000ms waiting for target completion' | ||
| 74 | + }, | ||
| 75 | + }); | ||
| 76 | + } | ||
| 77 | + assertProbeJson(actual, expected); | ||
| 55 | 78 | }, | |
| 56 | 79 | trim: true, | |
| 57 | 80 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments