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

.NET Debugging services don't issue thread create callbacks for single file apps #77574

Closed
gregg-miskelly opened this issue Oct 27, 2022 · 7 comments · Fixed by #84965
Closed

Comments

@gregg-miskelly
Copy link
Contributor

gregg-miskelly commented Oct 27, 2022

Description

When debugging a .NET 7 application published in single-file mode, the .NET debugging services don't issue ICorDebugManagedCallback.CreateThread call backs, causing the threads window in Visual Studio to be missing threads.

For what it is worth, this didn't reproduce in .NET 6.

Reproduction Steps

  1. Create a new .NET 7 console application with the following code
  2. dotnet publish it
  3. Open the single file executable as an exe project in Visual Studio or another managed debugger
  4. Launch
  5. When the Debugger.Break() is hit, open the threads window or the parallel stacks window

Program.cs:

using System.Diagnostics;

void KickOffThreadPoolThread(ManualResetEvent manualResetEvent)
{
    Task.Run(() =>
    {
        manualResetEvent.Set();
        while (true)
        {
            Thread.Sleep(100);
        }
    });
}

var manualResetEvents = new ManualResetEvent[10];
for (int i = 0; i < manualResetEvents.Length; i++)
{
    manualResetEvents[i] = new ManualResetEvent(false);
    KickOffThreadPoolThread(manualResetEvents[i]);
}

ManualResetEvent.WaitAll(manualResetEvents);
Debugger.Break();

Project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <!--Enable single file-->
    <PublishSingleFile>true</PublishSingleFile>
    <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
    <SelfContained>true</SelfContained>
  </PropertyGroup>
</Project>

Expected behavior

.NET Debugging services will issue ICorDebugManagedCallback.CreateThread call backs, so the parallel stacks window will have all the threads

Actual behavior

No CreateThread callback is triggered for the thread pool threads, so the parallel stacks window will only show the main thread:
image

Regression?

This same scenario works in .NET 6.

Known Workarounds

Debugger can call EnumerateThreads

Configuration

Version: .NET 7 RC2
OS: Windows
Architecture: x64

I didn't try other configurations other than .NET 6

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Oct 27, 2022
@ghost
Copy link

ghost commented Oct 27, 2022

Tagging subscribers to this area: @tommcdon
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

When debugging a .NET 7 application published in single-file mode, the .NET debugging services don't issue ICorDebugManagedCallback.CreateThread call backs, causing the threads window in Visual Studio to be missing threads.

For what it is worth, this didn't reproduce in .NET 6.

Reproduction Steps

  1. Create a new .NET 7 console application with the following code
  2. dotnet publish it
  3. Open the single file executable as an exe project in Visual Studio or another managed debugger
  4. Launch
  5. When the Debugger.Break() is hit, open the threads window or the parallel stacks window

Program.cs:

using System.Diagnostics;

void KickOffThreadPoolThread(ManualResetEvent manualResetEvent)
{
    Task.Run(() =>
    {
        manualResetEvent.Set();
        while (true)
        {
            Thread.Sleep(100);
        }
    });
}

var manualResetEvents = new ManualResetEvent[10];
for (int i = 0; i < manualResetEvents.Length; i++)
{
    manualResetEvents[i] = new ManualResetEvent(false);
    KickOffThreadPoolThread(manualResetEvents[i]);
}

ManualResetEvent.WaitAll(manualResetEvents);
Debugger.Break();

Project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <!--Enable single file-->
    <PublishSingleFile>true</PublishSingleFile>
    <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
    <SelfContained>true</SelfContained>
  </PropertyGroup>
</Project>

Expected behavior

.NET Debugging services will issue ICorDebugManagedCallback.CreateThread call backs, so the parallel stacks window will have all the threads

Actual behavior

No CreateThread callback is triggered for the thread pool threads, so the parallel stacks window will fail.

Regression?

This same scenario works in .NET 6. Though

Known Workarounds

Debugger can call EnumerateThreads

Configuration

Version: .NET 7 RC2
OS: Windows
Architecture: x64

I didn't try other configurations other than .NET 6

Other information

No response

Author: gregg-miskelly
Assignees: -
Labels:

area-Diagnostics-coreclr, untriaged

Milestone: -

@tommcdon tommcdon added this to the 8.0.0 milestone Oct 28, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Oct 28, 2022
@tommcdon tommcdon added bug untriaged New issue has not been triaged by the area owner and removed untriaged New issue has not been triaged by the area owner labels Oct 28, 2022
@mikelle-rogers mikelle-rogers self-assigned this Feb 28, 2023
@mikelle-rogers
Copy link
Member

mikelle-rogers commented Feb 28, 2023

The investigation has shown that all Ready2Run (R2R) breakpoints fail. As a workaround, please disable R2R. This is done by placing the <PublishReadyToRun>false</PublishReadyToRun> tag group inside a <Property Group> inside of the app's .csproj file.
We will continue to investigate possible solutions.

@VSadov
Copy link
Member

VSadov commented Mar 1, 2023

The investigation has shown that all Ready2Run (R2R) breakpoints fail.

Was that on Windows11 ?
I have recently heard that the optimized no-copy layout approach that we use on Windows11 could be incompatible with breakpoints in R2R code.

@tommcdon
Copy link
Member

tommcdon commented Mar 1, 2023

@VSadov is there a way to disable the "no-copy" optimization to test whether or not that resolves the problem?

@VSadov
Copy link
Member

VSadov commented Mar 2, 2023

is there a way to disable the "no-copy" optimization to test whether or not that resolves the problem?

No, but we have a fallback path for cases when VirtualAlloc2 API is not available. It is a relatively recent addition. Win11 has it, but I am not sure about Win10

It is possible to add a switch to disable this functionality. There will be some impact on startup as we would need to start copying.

@VSadov
Copy link
Member

VSadov commented Mar 2, 2023

There is also a way to disable R2R. set DOTNET__ReadyToRun=0 , think

Could be interesting at least when trying to narrow the conditions for the bug to happen.

@mikelle-rogers
Copy link
Member

The parallel stacks window shows only the main thread because VS was never notified of the creation of any other thread. This results from the debugger failing to activate thread starter patches. The debugger only notifies VS of the new thread when the patch is activated. The inactive patch results from restricted permissions given to a single file image: PAGE_EXECUTE_READ which prevents VirtualProtect from changing the permission to allow patch activation. The initial permission of PAGE_EXECUTE_READ is set in single file R2R on windows, when MapviewOfFile maps the images at the end of the bundle as ExecuteRead.

@VSadov VSadov assigned VSadov and unassigned mikelle-rogers Mar 29, 2023
@ghost ghost locked as resolved and limited conversation to collaborators May 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants