-
Notifications
You must be signed in to change notification settings - Fork 299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
prevent debugpy from getting stuck #7612
Changes from 10 commits
f827552
014082d
737cb03
2b4d0bc
c780500
147ef55
42a0e79
d55d4e9
e1cb175
52fcf32
9889241
7096f8c
ed8b70a
eb19626
58237c8
8c53b34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,13 +57,16 @@ export class RunByLineController implements IDebuggingDelegate { | |
public continue(): void { | ||
if (typeof this.lastPausedThreadId !== 'number') { | ||
traceVerbose(`No paused thread, can't do RBL`); | ||
this.stop(); | ||
return; | ||
} | ||
|
||
void this.debugAdapter.stepIn(this.lastPausedThreadId); | ||
} | ||
|
||
public stop(): void { | ||
// When debugpy gets stuck, running a cell fixes it and allows us to start another debugging session | ||
void this.kernel.executeHidden('pass'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll file a bug in ipykernel, and mention it here, but I don't see what else we can do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It would be good to explain how that happens |
||
this.debugAdapter.disconnect(); | ||
} | ||
|
||
|
@@ -173,10 +176,8 @@ async function cellDebugSetup( | |
): Promise<void> { | ||
// remove this if when https://github.com/microsoft/debugpy/issues/706 is fixed and ipykernel ships it | ||
// executing this code restarts debugpy and fixes https://github.com/microsoft/vscode-jupyter/issues/7251 | ||
if (kernel) { | ||
const code = 'import debugpy\ndebugpy.debug_this_thread()'; | ||
await kernel.executeHidden(code); | ||
} | ||
const code = 'import debugpy\ndebugpy.debug_this_thread()'; | ||
await kernel.executeHidden(code); | ||
|
||
await debugAdapter.dumpCell(debugCell.index); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,8 @@ import { | |
ProgressLocation, | ||
DebugAdapterDescriptor, | ||
Event, | ||
EventEmitter | ||
EventEmitter, | ||
NotebookEditor | ||
} from 'vscode'; | ||
import * as path from 'path'; | ||
import { IKernel, IKernelProvider } from '../../datascience/jupyter/kernels/types'; | ||
|
@@ -51,6 +52,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
private notebookToDebugger = new Map<NotebookDocument, Debugger>(); | ||
private notebookToDebugAdapter = new Map<NotebookDocument, KernelDebugAdapter>(); | ||
private notebookToRunByLineController = new Map<NotebookDocument, RunByLineController>(); | ||
private notebookInProgress = new Set<NotebookDocument>(); | ||
private cache = new Map<PythonEnvironment, boolean>(); | ||
private readonly disposables: IDisposable[] = []; | ||
private _doneDebugging = new EventEmitter<void>(); | ||
|
@@ -81,9 +83,9 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
workspace.onDidCloseNotebookDocument(async (document) => { | ||
const dbg = this.notebookToDebugger.get(document); | ||
if (dbg) { | ||
await dbg.stop(); | ||
this.updateToolbar(false); | ||
this.updateCellToolbar(false); | ||
await dbg.stop(); | ||
} | ||
}), | ||
|
||
|
@@ -94,16 +96,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
|
||
this.commandManager.registerCommand(DSCommands.DebugNotebook, async () => { | ||
const editor = this.vscNotebook.activeNotebookEditor; | ||
if (editor) { | ||
if (await this.checkForIpykernel6(editor.document)) { | ||
this.updateToolbar(true); | ||
void this.startDebugging(editor.document); | ||
} else { | ||
void this.installIpykernel6(); | ||
} | ||
} else { | ||
void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); | ||
} | ||
await this.tryToStartDebugging(KernelDebugMode.Everything, editor); | ||
}), | ||
|
||
this.commandManager.registerCommand(DSCommands.RunByLine, async (cell: NotebookCell | undefined) => { | ||
|
@@ -120,17 +113,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
return; | ||
} | ||
|
||
if (editor) { | ||
if (await this.checkForIpykernel6(editor.document, DataScience.startingRunByLine())) { | ||
this.updateToolbar(true); | ||
this.updateCellToolbar(true); | ||
await this.startDebuggingCell(editor.document, KernelDebugMode.RunByLine, cell); | ||
} else { | ||
void this.installIpykernel6(); | ||
} | ||
} else { | ||
void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); | ||
} | ||
await this.tryToStartDebugging(KernelDebugMode.RunByLine, editor, cell); | ||
}), | ||
|
||
this.commandManager.registerCommand(DSCommands.RunByLineNext, (cell: NotebookCell | undefined) => { | ||
|
@@ -146,6 +129,10 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
return; | ||
} | ||
|
||
if (this.notebookInProgress.has(cell.notebook)) { | ||
return; | ||
} | ||
|
||
const controller = this.notebookToRunByLineController.get(cell.notebook); | ||
if (controller && controller.debugCell.document.uri.toString() === cell.document.uri.toString()) { | ||
controller.continue(); | ||
|
@@ -157,7 +144,9 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
if (editor) { | ||
const controller = this.notebookToRunByLineController.get(editor.document); | ||
if (controller) { | ||
sendTelemetryEvent(DebuggingTelemetry.endedSession, undefined, { reason: 'withKeybinding' }); | ||
sendTelemetryEvent(DebuggingTelemetry.endedSession, undefined, { | ||
reason: 'withKeybinding' | ||
}); | ||
controller.stop(); | ||
} | ||
} | ||
|
@@ -177,16 +166,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
return; | ||
} | ||
|
||
if (editor) { | ||
if (await this.checkForIpykernel6(editor.document)) { | ||
this.updateToolbar(true); | ||
void this.startDebuggingCell(editor.document, KernelDebugMode.Cell, cell); | ||
} else { | ||
void this.installIpykernel6(); | ||
} | ||
} else { | ||
void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); | ||
} | ||
await this.tryToStartDebugging(KernelDebugMode.Cell, editor, cell); | ||
}) | ||
); | ||
} | ||
|
@@ -232,6 +212,59 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
this.runByLineInProgress.set(runningByLine).ignoreErrors(); | ||
} | ||
|
||
private async tryToStartDebugging(mode: KernelDebugMode, editor?: NotebookEditor, cell?: NotebookCell) { | ||
if (!editor) { | ||
void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); | ||
return; | ||
} | ||
|
||
if (this.notebookInProgress.has(editor.document)) { | ||
return; | ||
} | ||
|
||
if (this.isDebugging(editor.document)) { | ||
this.updateToolbar(true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you have to do this here? If you are already debugging, shouldn't it already be in this state? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it should, but that's the buggy state you get trapped in. this would correct the context keys There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure i like this, feels like we're re-executing some code & that seems to fix a problem (but we don't know why). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The root cause is out of our hands, its debugpy freezing inside ipykernel. And I disagree, we should be trying to fix root causes, but since the root cause is not on our code, we should also try to fix it if it happens. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If that's the cause, how can updating the toolbar fix anything in the kernel (i.e. updating a UI will not address anything in ipykernel freezing). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like the root cause is us having the context keys out of sync with the RBL lifecycle. Your latest commit is a good one towards fixing that, I think previously we reset the context keys too early, before debugging actually stopped. Fundamentally, at this point in the code, two things are out of sync. It's hard to point at one and say this one is always right. It should be possible to write the code so that they are always in sync. |
||
if (mode === KernelDebugMode.RunByLine) { | ||
this.updateCellToolbar(true); | ||
} | ||
return; | ||
} | ||
|
||
try { | ||
this.notebookInProgress.add(editor.document); | ||
if ( | ||
await this.checkForIpykernel6( | ||
editor.document, | ||
mode === KernelDebugMode.RunByLine ? DataScience.startingRunByLine() : undefined | ||
) | ||
) { | ||
switch (mode) { | ||
case KernelDebugMode.Everything: | ||
await this.startDebugging(editor.document); | ||
this.updateToolbar(true); | ||
break; | ||
case KernelDebugMode.Cell: | ||
if (cell) { | ||
await this.startDebuggingCell(editor.document, KernelDebugMode.Cell, cell); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sorry, but I'm not convinced we need Would like to have some comments why we must await, e.g. tomorrow what would stop working if we removed it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it helps with keeping the context keys aligned with the status of the debugger. My most recent commit puts them after the awaits. |
||
this.updateToolbar(true); | ||
} | ||
break; | ||
case KernelDebugMode.RunByLine: | ||
if (cell) { | ||
await this.startDebuggingCell(editor.document, KernelDebugMode.RunByLine, cell); | ||
this.updateToolbar(true); | ||
this.updateCellToolbar(true); | ||
} | ||
break; | ||
} | ||
} else { | ||
void this.installIpykernel6(); | ||
} | ||
} finally { | ||
this.notebookInProgress.delete(editor.document); | ||
} | ||
} | ||
|
||
private async startDebuggingCell( | ||
doc: NotebookDocument, | ||
mode: KernelDebugMode.Cell | KernelDebugMode.RunByLine, | ||
|
@@ -286,8 +319,6 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
} | ||
|
||
private async endSession(session: DebugSession) { | ||
void this.updateToolbar(false); | ||
void this.updateCellToolbar(false); | ||
this._doneDebugging.fire(); | ||
for (const [doc, dbg] of this.notebookToDebugger.entries()) { | ||
if (dbg && session.id === (await dbg.session).id) { | ||
|
@@ -296,6 +327,8 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb | |
break; | ||
} | ||
} | ||
this.updateToolbar(false); | ||
this.updateCellToolbar(false); | ||
} | ||
|
||
private async createDebugAdapterDescriptor(session: DebugSession): Promise<DebugAdapterDescriptor | undefined> { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this, if I click the RBL button and then click it again before the first pause, it disconnects. That's not that bad of a problem but could be annoying, especially since it will then run the whole cell, even if I didn't want/expect it to.
I guess this is working around a case where we attach to debugpy and it doesn't respond, but if we have fixed the issue on our end of trying to attach twice at a time, then I don't understand how else we get into that case