Skip to content
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

Merged
merged 16 commits into from
Sep 24, 2021
9 changes: 5 additions & 4 deletions src/client/debugger/jupyter/debugControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Member

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

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');
Copy link
Author

Choose a reason for hiding this comment

The 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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When debugpy gets stuck

It would be good to explain how that happens

this.debugAdapter.disconnect();
}

Expand Down Expand Up @@ -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);
}
90 changes: 57 additions & 33 deletions src/client/debugger/jupyter/debuggingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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>();
Expand Down Expand Up @@ -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) => {
Expand All @@ -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) => {
Expand All @@ -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();
Expand All @@ -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();
}
}
Expand All @@ -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);
})
);
}
Expand Down Expand Up @@ -232,6 +212,50 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb
this.runByLineInProgress.set(runningByLine).ignoreErrors();
}

private async tryToStartDebugging(mode: KernelDebugMode, editor?: NotebookEditor, cell?: NotebookCell) {
if (editor) {
if (this.notebookInProgress.has(editor.document)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checking the case where debugging is starting, but doesn't it still need to check this.notebookToDebugger.has?

return;
}

try {
this.notebookInProgress.add(editor.document);
if (
await this.checkForIpykernel6(
editor.document,
mode === KernelDebugMode.RunByLine ? DataScience.startingRunByLine() : undefined
)
) {
switch (mode) {
case KernelDebugMode.Everything:
this.updateToolbar(true);
void this.startDebugging(editor.document);
break;
case KernelDebugMode.Cell:
if (cell) {
this.updateToolbar(true);
void this.startDebuggingCell(editor.document, KernelDebugMode.Cell, cell);
}
break;
case KernelDebugMode.RunByLine:
if (cell) {
this.updateToolbar(true);
this.updateCellToolbar(true);
await this.startDebuggingCell(editor.document, KernelDebugMode.RunByLine, cell);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in line 233 we're not awaiting, but here we're awaiting.
Confused why that's the case, comments would help.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll be good to await them all

}
break;
}
} else {
void this.installIpykernel6();
}
} finally {
this.notebookInProgress.delete(editor.document);
}
} else {
void this.appShell.showErrorMessage(DataScience.noNotebookToDebug());
}
}

private async startDebuggingCell(
doc: NotebookDocument,
mode: KernelDebugMode.Cell | KernelDebugMode.RunByLine,
Expand Down