Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ The best way to get .NET Core MSBuild is by installing the [.NET Core SDK](https

## Wait in Main

Set the environment variable `MSBUILDDEBUGONSTART` to `2`, then attach a debugger to the process manually after it starts.
Set the environment variable `MSBUILDDEBUGONSTART` to control debugger behavior at startup:
- `1`: Launch debugger for all processes (main MSBuild and TaskHost child processes)
- `2`: Wait for manual debugger attach for all processes
- `3`: Launch debugger for main MSBuild process only, skip TaskHost child processes

For example, set `MSBUILDDEBUGONSTART` to `2`, then attach a debugger to the process manually after it starts.

## Using the repository binaries to perform builds

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ Sometimes it's useful to patch your copy of Visual Studio in order to test or de
### Debugging MSBuild

#### Breakpoints
To break into the [main method](https://github.com/dotnet/msbuild/blob/bd00d6cba24d41efd6f54699c3fdbefb9f5034a1/src/MSBuild/XMake.cs#L493-L506) of MSBuild.exe: set the environment variable `MSBUILDDEBUGONSTART` to 1 (uses `Debugger.Launch()`) or 2 (waits until debugger is attached).
To break into the [main method](https://github.com/dotnet/msbuild/blob/bd00d6cba24d41efd6f54699c3fdbefb9f5034a1/src/MSBuild/XMake.cs#L493-L506) of MSBuild.exe: set the environment variable `MSBUILDDEBUGONSTART` to:
- `1`: Launch debugger for all processes (main MSBuild and TaskHost child processes) using `Debugger.Launch()`
- `2`: Wait for manual debugger attach for all processes
- `3`: Launch debugger for main MSBuild process only using `Debugger.Launch()`, skip TaskHost child processes

To break into MSBuild's [BuildManager.BeginBuild](https://github.com/dotnet/msbuild/blob/bd00d6cba24d41efd6f54699c3fdbefb9f5034a1/src/Build/BackEnd/BuildManager/BuildManager.cs#L414) set the environment variable `MSBuildDebugBuildManagerOnStart` to 1 (uses `Debugger.Launch()`) or 2 (waits until debugger is attached).
This is useful for debugging MSBuild when it is called from other apps that use its APIs instead of its executable (for example Visual Studio). You can also filter which processes trigger the breakpoint by setting `MSBuildDebugProcessName` to a substring of the process name. For example, to trigger the breakpoint only under Visual Studio's top level process you would set `MSBuildDebugProcessName` to the value `devenv`.
Expand Down
9 changes: 9 additions & 0 deletions src/Build.UnitTests/BackEnd/DebugUtils_tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.Debugging;
using Shouldly;
using Xunit;

Expand Down Expand Up @@ -38,5 +39,13 @@ public void DumpExceptionToFileShouldWriteInDebugDumpPath()
File.Delete(exceptionFile);
}
}

[Fact]
public void IsInTaskHostNode_ReturnsFalseForCentralNode()
{
// When running in the main test process (no /nodemode argument),
// we should not be in a TaskHost node
DebugUtils.IsInTaskHostNode().ShouldBeFalse();
}
}
}
7 changes: 7 additions & 0 deletions src/MSBuild/XMake.cs
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,13 @@ private static void DebuggerLaunchCheck()
case "1":
Debugger.Launch();
break;
case "3":
// Value "3" debugs the main MSBuild process but skips debugging child TaskHost processes
if (!DebugUtils.IsInTaskHostNode())
{
Debugger.Launch();
}
break;
#endif
case "2":
// Sometimes easier to attach rather than deal with JIT prompt
Expand Down
4 changes: 4 additions & 0 deletions src/MSBuildTaskHost/OutOfProcTaskHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ internal static ExitType Execute()

Console.ReadLine();
break;
case "3":
// Value "3" skips debugging for TaskHost processes but debugs the main MSBuild process
// This is useful when you want to debug MSBuild but not the child TaskHost processes
break;
}

bool restart = false;
Expand Down
10 changes: 10 additions & 0 deletions src/Shared/Debugging/DebugUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ private static bool CurrentProcessMatchesDebugName()

public static string DebugPath { get; private set; }

/// <summary>
/// Returns true if the current process is an out-of-proc TaskHost node.
/// </summary>
/// <returns>
/// True if this process was launched with /nodemode:2 (indicating it's a TaskHost process),
/// false otherwise. This is useful for conditionally enabling debugging or other behaviors
/// based on whether the code is running in the main MSBuild process or a child TaskHost process.
/// </returns>
public static bool IsInTaskHostNode() => ProcessNodeMode.Value == NodeMode.OutOfProcTaskHostNode;

public static string FindNextAvailableDebugFilePath(string fileName)
{
var extension = Path.GetExtension(fileName);
Expand Down