dotnet / vscode-csharp

Official C# support for Visual Studio Code
MIT License
2.86k stars 673 forks source link

End debugged .NET processes gracefully by sending CTRL+C / SIGTERM #4263

Closed ghost closed 3 years ago

ghost commented 3 years ago

Issue Description

When using the Debug Console, it is not possible to send CTRL+C / SIGTERM to the .NET process being debugged. Pressing the Stop button in the debug toolbar seems to kill the process immediately, instead of ending it gracefully (by sending CTRL+C / SIGTERM).

Steps to Reproduce

Start a ASP.NET Core application. Press the Stop button in the debug toolbar.

Expected Behavior

In the optimal case, pressing the Stop button in the debug toolbar once sends CTRL+C / SIGTERM to to process, like this seems be done with the Node.js debugger. Pressing it for a second time kills the process.

If this is not possible, a pragmatic solution would be to provide some means to end the process gracefully manually.

Actual Behavior

The .NET process being debugged is killed.

Logs

OmniSharp log

Post the output from Output-->OmniSharp log here

C# log

Post the output from Output-->C# here

Environment information

VSCode version: 1.51.1 C# Extension: 1.23.6

Mono Information OmniSharp using built-in mono
Dotnet Information .NET SDK (reflecting any global.json): Version: 5.0.100 Commit: 5044b93829 Runtime Environment: OS Name: Mac OS X OS Version: 11.0 OS Platform: Darwin RID: osx.11.0-x64 Base Path: /usr/local/share/dotnet/sdk/5.0.100/ Host (useful for support): Version: 5.0.0 Commit: cf258a14b7 .NET SDKs installed: 1.0.0-preview2-1-003177 [/usr/local/share/dotnet/sdk] 2.0.0 [/usr/local/share/dotnet/sdk] 2.1.302 [/usr/local/share/dotnet/sdk] 3.0.100 [/usr/local/share/dotnet/sdk] 3.1.100-preview2-014569 [/usr/local/share/dotnet/sdk] 3.1.100 [/usr/local/share/dotnet/sdk] 3.1.101 [/usr/local/share/dotnet/sdk] 3.1.301 [/usr/local/share/dotnet/sdk] 5.0.100-preview.6.20318.15 [/usr/local/share/dotnet/sdk] 5.0.100-preview.7.20366.6 [/usr/local/share/dotnet/sdk] 5.0.100-preview.8.20417.9 [/usr/local/share/dotnet/sdk] 5.0.100-rc.1.20452.10 [/usr/local/share/dotnet/sdk] 5.0.100-rc.2.20479.15 [/usr/local/share/dotnet/sdk] 5.0.100 [/usr/local/share/dotnet/sdk] .NET runtimes installed: Microsoft.AspNetCore.All 2.1.2 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.2 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.0-preview2.19528.8 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.5 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0-preview.6.20312.15 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0-preview.7.20365.19 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0-preview.8.20414.8 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0-rc.1.20451.17 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0-rc.2.20475.17 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 1.1.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 2.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.2 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.15 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.0-preview2.19525.6 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-preview.6.20305.6 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-preview.7.20364.11 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-preview.8.20407.11 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-rc.1.20451.14 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-rc.2.20475.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] To install additional .NET runtimes or SDKs: https://aka.ms/dotnet-download
Visual Studio Code Extensions |Extension|Author|Version| |---|---|---| |cpptools|ms-vscode|1.1.2| |csharp|ms-dotnettools|1.23.6| |debugger-for-chrome|msjsdiag|4.12.11| |EditorConfig|EditorConfig|0.16.3| |hexeditor|ms-vscode|1.3.0| |ilspy-vscode|icsharpcode|0.9.0| |markdown-toc|AlanWalk|1.5.6| |prettier-vscode|esbenp|5.8.0| |remote-containers|ms-vscode-remote|0.148.1| |remote-ssh|ms-vscode-remote|0.56.0| |remote-ssh-edit|ms-vscode-remote|0.56.0| |remote-wsl|ms-vscode-remote|0.51.4| |rest-client|humao|0.24.4| |snapshot-tools|asvetliakov|0.4.0| |toggle|rebornix|0.0.1| |vscode-docker|ms-azuretools|1.8.1| |vscode-eslint|dbaeumer|2.1.13| |vscode-remote-extensionpack|ms-vscode-remote|0.20.0| |vscode-typescript-tslint-plugin|ms-vscode|1.2.3|;
gregg-miskelly commented 3 years ago

It is intentional that clicking the stop button terminates the process.

The best way I can think of achieving what you want would be to add a tasks.json task which calls pkill, and you could execute that instead when you wanted graceful shutdown.

justinmchase commented 3 years ago

I agree it should be an option to send a term signal instead of hard killing it. In my case the process is being killed and various child processes are not properly killed so they're orphaned and hanging onto ports. Still trying to figure out how to handle that but if vscode was sending a term signal it seems like it would be easy to handle it gracefully.

justinmchase commented 3 years ago

At the very least you should be killing the entire process tree not just the top process. Orphaning child-processes with no opportunity to gracefully close them between debug sessions is a bug.

MicahZoltu commented 3 years ago

@gregg-miskelly There was a feature specifically added to the VSCode Debugger to support exactly this behavior, the OmniSharp plugin just needs to add support for it: https://github.com/microsoft/vscode/issues/54384

The first press of the debugger stop button will send a graceful shutdown, a second press of the button will hard kill.

The best way I can think of achieving what you want would be to add a tasks.json task which calls pkill, and you could execute that instead when you wanted graceful shutdown.

I don't believe this works on Windows? I have a process running in a debugger on Windows right now that is connected to a database and I need to gracefully stop it (because I don't want to risk database corruption) and from what I can find I have no options!

MicahZoltu commented 3 years ago

Separate from the debug stop button behavior, it feels like a bug that there is currently no way to send keypresses through to the running process in the debugger. I have a C# Console App that specifically has a CancelKeyPress handler and it is impossible to invoke it, which means I cannot actually debug my shutdown handler at the moment.

using System;
using System.Threading.Tasks;

public class Program
{
    public static async Task Main()
    {
        Console.CancelKeyPress += (sender, args) => Console.WriteLine("This is unreachable while running in omnisharp debugger.");
        Console.WriteLine("Going to sleep for 5 seconds.");
        await Task.Delay(5000);
        Console.WriteLine("Waking up.");
    }
}
winzig commented 2 years ago

The best way I can think of achieving what you want would be to add a tasks.json task which calls pkill, and you could execute that instead when you wanted graceful shutdown.

This seems like a PITA to use on a common basis. Has anyone found a better way? For my money, pressing the stop button once should allow for graceful shutdown of the app, pressing it a second time would kill it like it does now. Has anyone found a way to customize this behavior?

https://github.com/microsoft/vscode/issues/54384 seems like exactly what I was looking for. How hard would it be to implement this within omnisharp-vscode?

justinmchase commented 2 years ago

The solution I figured out is that in the VSCode launch.json you can actually control the settings to have the process launch in a terminal instead of in that weird debugger panel. In the terminal the process is still attached but you'll get colored output and it will respond to stdin like normal.

Also, I made a task which launches the process in the background, in the terminal, then attach vs to that process rather than launching it. If you stop the debugger it just detaches and then you have to press ctrl+c in the terminal to kill the process.

goncalo-oliveira commented 2 years ago

There was a feature specifically added to the VSCode Debugger to support exactly this behavior, the OmniSharp plugin just needs to add support for it: https://github.com/microsoft/vscode/issues/54384

I agree this should be the path to follow. It's very annoying that stopping the debug doesn't do a graceful shutdown. As a workaround I'm doing as @justinmchase suggested, which is using the integratedTerminal instead of the internalConsole, thus enabling stdin.

winzig commented 2 years ago

It doesn't seem like anyone is monitoring this closed issue, so I opened a new one here https://github.com/OmniSharp/omnisharp-vscode/issues/5100.

egbertn commented 11 months ago

I have +/- 10 subprocesses and had to kill them seperately after a debug session, and found this thread which helped me. But VsCode not nicely ending the main program is very annoying.