Skip to content

[Release] Snap main branch for release#5786

Merged
hoyosjs merged 51 commits intorelease/stablefrom
main
Apr 2, 2026
Merged

[Release] Snap main branch for release#5786
hoyosjs merged 51 commits intorelease/stablefrom
main

Conversation

@hoyosjs
Copy link
Copy Markdown
Member

@hoyosjs hoyosjs commented Apr 2, 2026

Copilot aided release notes for user facing changes


Breaking Changes

dotnet-dump / SOS

New features:

Bug fixes:

dotnet-trace / collect-linux

dotnet-counters

Diagnostic Tools (general)

Libraries

steveisok and others added 30 commits January 28, 2026 07:19
test.sh|ps1 -methodfilter "Namespace.ClassName.MethodName" 
test.sh|ps1 -classfilter "Namespace.ClassName"

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Max Charlamb <44248479+max-charlamb@users.noreply.github.com>
…st failures (#5700)

## Improve Console Output Visibility in CollectCommand Tests

This PR addresses issue #5698 by making console output visible when
tests fail.

### Changes completed:
- [x] Modified MockConsole to accept optional ITestOutputHelper
parameter
- [x] Updated MockConsole.Write methods to multiplex output to
ITestOutputHelper when provided
- [x] Injected ITestOutputHelper into CollectCommandFunctionalTests
- [x] Injected ITestOutputHelper into CollectLinuxCommandFunctionalTests
- [x] Injected ITestOutputHelper into ConsoleExporterTests
- [x] Updated all test methods to pass ITestOutputHelper to MockConsole
- [x] Optimized implementation to avoid unnecessary string allocations
- [x] Made exception handling more specific with exception message check
- [x] Added FlushTestLogging method to ensure buffered output is written
- [x] Simplified comments per code review feedback
- [x] Fixed case-sensitive exception filter to use OrdinalIgnoreCase
comparison
- [x] Refactored test logging logic into BufferTestLogging helper method
- [x] Renamed FlushOutput to FlushTestLogging for clarity
- [x] Verified backward compatibility with existing tests
- [x] All tests pass successfully

### Key features:
- **Intelligent buffering**: Accumulates text until complete lines are
ready to avoid fragmenting output
- **Performance optimized**: Only processes buffer when incoming text
contains newlines
- **Robust exception handling**: Uses case-insensitive comparison to
catch InvalidOperationException properly
- **Backward compatible**: Optional parameter means existing tests don't
need changes
- **FlushTestLogging method**: Ensures any remaining buffered output is
written before assertions
- **Clean separation**: Test logging logic extracted into dedicated
helper methods

### Result:
Console output including error messages and exceptions is now visible in
test output under "Standard Output Messages:", making it much easier to
diagnose issues when tests fail.

<!-- START COPILOT CODING AGENT SUFFIX -->



<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> Can you extend the CollectCommandFunctionalTests and
CollectLinuxCommandFunctionalTests to increase visibility on the console
output observed whenever there are failures. Currently they're swalled
by MockConsole, and so we don't know what exception occurred. See
#5698 for an example. There
is also a suggestion to inject ITestOutputHelper,


</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mdh1418 <16830051+mdh1418@users.noreply.github.com>
Co-authored-by: noahfalk <6243776+noahfalk@users.noreply.github.com>
- Add -all flag to DumpMT that includes class details and method
descriptors
- -all implies -MD but -MD remains independently usable
- Create DisplayClassDetails() helper to avoid duplication between
DumpMT -all and DumpClass
- Simplify Canonical MethodTable display: only show when it differs from
current MT
- Update PrintVC and PrintObj to use same simplified logic
- Keep DumpClass as separate command for backward compatibility
- Update documentation for both Windows and Unix
This pull request updates the following dependencies

[marker]: <> (Begin:39a983c9-68a4-46ae-bbba-6795aab4810d)
## From https://github.com/dotnet/arcade
- **Subscription**:
[39a983c9-68a4-46ae-bbba-6795aab4810d](https://maestro.dot.net/subscriptions?search=39a983c9-68a4-46ae-bbba-6795aab4810d)
- **Build**:
[20260130.4](https://dev.azure.com/dnceng/internal/_build/results?buildId=2891960)
([299615](https://maestro.dot.net/channel/8394/github:dotnet:arcade/build/299615))
- **Date Produced**: January 31, 2026 3:24:21 AM UTC
- **Commit**:
[b0f891cf7c1febc0ebbae7dd787f8a0b4c0cbc95](dotnet/arcade@b0f891c)
- **Branch**:
[release/10.0](https://github.com/dotnet/arcade/tree/release/10.0)

[DependencyUpdate]: <> (Begin)

- **Dependency Updates**:
  - From [10.0.0-beta.26066.3 to 10.0.0-beta.26080.4][1]
     - Microsoft.DotNet.Arcade.Sdk
     - Microsoft.DotNet.CodeAnalysis

[1]: dotnet/arcade@af17297...b0f891c

[DependencyUpdate]: <> (End)


[marker]: <> (End:39a983c9-68a4-46ae-bbba-6795aab4810d)

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
…#5684)

- [x] Analyze the usage of $(RepoRootDir) in Debugger.Tests.Config.txt
files
- [x] Add MSBuild targets to copy AuxMsbuildFiles and debuggee sources
to artifacts directory:
- [x] CommonTestRunner.csproj: Copy debuggees (Tracee, EventPipeTracee,
StackTracee, ExitCodeTracee) and `eng/AuxMsbuildFiles`
- [x] DbgShim.UnitTests.csproj: Copy
`src/tests/DbgShim.UnitTests/Debuggees` and `eng/AuxMsbuildFiles`
- [x] SOS.UnitTests.csproj: Copy `src/tests/SOS.UnitTests/Debuggees`,
`eng/AuxMsbuildFiles`, and `lldbplugin.tests`
- [x] Update Debugger.Tests.Config.txt files to use $(ArtifactsDir)
paths:
  - [x] CommonTestRunner Unix/Windows config files
  - [x] DbgShim.UnitTests Unix/Windows config files
  - [x] SOS.UnitTests Unix/Windows config files
- [x] Remove RepoRootDir from generated Debugger.Tests.Common.txt files
- [x] Move free-floating debuggees (Tracee, EventPipeTracee,
StackTracee, ExitCodeTracee) under CommonTestRunner/Debuggees
- [x] Add ExitCodeTracee (previously missing)
- [x] Build and verify the changes work correctly
- [x] Remove accidentally included nuget.exe binary file
- [x] Fix RepoRootDir references in SOS.cs for lldb tests by using
ArtifactsDir paths

<!-- START COPILOT CODING AGENT SUFFIX -->



<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> Our general goal is to run the diagnostics tests on Helix. This
requires packaging up the built test binaries then running them on a
remote machine.
> 
> Currently, the unit tests Debugger.Tests.Config.txt files rely on the
$(RepoRootDir). Which is not consistent when moved to another machine.
We've already reduced out dependency on this by copying files to the
artifacts dir.
> 
> Can you update the Debugger.Tests.Config.txt files in
CommonTestRunner, DbgShim.UnitTests, and SOS.UnitTests to no longer user
$(RepoRootDir).
> 
> If we need access to the files referenced by $(RepoRootDir), we should
copy them to the artifacts dir and have a path relative to the
$(ArtifactsDir).


</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
Context:
dotnet/runtime#124309 (comment)

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Minimum supported runtime is now .NET 8, so `COMPLUS_*` env var
fallbacks are unnecessary — all config uses `DOTNET_*`.

- **`ProcessRunner.WithRuntimeConfiguration`**: Remove `COMPlus_`
fallback (the existing comment explicitly called for this)
- **`documentation/FAQ.md`**: Remove stale note about .NET 6 requiring
`COMPlus_` prefix

Files in `src/shared/` (`clrconfignocache.h`, `resource.h`) are synced
from dotnet/runtime and left unchanged — `BFA_BAD_COMPLUS_SIG` refers to
COM+ metadata signatures, not env vars.

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for
you](https://github.com/dotnet/diagnostics/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: max-charlamb <44248479+max-charlamb@users.noreply.github.com>
This pull request updates the following dependencies

[marker]: <> (Begin:39a983c9-68a4-46ae-bbba-6795aab4810d)
## From https://github.com/dotnet/arcade
- **Subscription**:
[39a983c9-68a4-46ae-bbba-6795aab4810d](https://maestro.dot.net/subscriptions?search=39a983c9-68a4-46ae-bbba-6795aab4810d)
- **Build**:
[20260210.1](https://dev.azure.com/dnceng/internal/_build/results?buildId=2900018)
([301121](https://maestro.dot.net/channel/8394/github:dotnet:arcade/build/301121))
- **Date Produced**: February 10, 2026 10:29:57 AM UTC
- **Commit**:
[4bf37ce670528cf2aef4d9b1cd892554b1b02d9d](dotnet/arcade@4bf37ce)
- **Branch**:
[release/10.0](https://github.com/dotnet/arcade/tree/release/10.0)

[DependencyUpdate]: <> (Begin)

- **Dependency Updates**:
  - From [10.0.0-beta.26080.4 to 10.0.0-beta.26110.1][3]
     - Microsoft.DotNet.Arcade.Sdk
     - Microsoft.DotNet.CodeAnalysis

[3]: dotnet/arcade@b0f891c...4bf37ce

[DependencyUpdate]: <> (End)


[marker]: <> (End:39a983c9-68a4-46ae-bbba-6795aab4810d)

---------

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
…#5721)

Both ShowLocals switch statements in strike.cpp cast 2-byte values to
signed 'short' before assigning to ULONG64, causing sign extension for
values >= 0x8000. Change both to 'unsigned short' to match the unsigned
types used in cases 1, 4, 8.

Fixes #5218.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes #4665.

Also, there were some stray characters at the end of some lines that my
editor removed, hence the seemingly larger PR than it really is.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The !u command unconditionally called GetILAddress() and aborted if it
failed, even when IL interleaving was not needed. This was introduced in
commit 6c4dbb3 ("Complete the !u -il SOS command", Sep 2019) which made
IL retrieval a hard requirement for all disassembly.

This change checks explicitly for a nil metadata token, it also
continues with disassembly if we have an md token but fail to get the IL
for other reasons. It also now checks for the `-il` parameter before we
even try to load the metadata import. This was happening unconditionally
before. Fixed some odd wording around dynamic methods.

Tested `!u` and `!u -il` on both il stubs and regular methods.

Fixes #1907

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Rachel <rachel.jarvi@gmail.com>
## Fix terminal cursor visibility in dotnet-trace collect-linux

### Problem
The `dotnet-trace collect-linux` command hides the terminal cursor
during operation but fails to restore it on exit, leaving users with an
invisible cursor that requires manual restoration via `tput cnorm` or
`reset`.

### Solution
Added cursor visibility restoration in the `finally` block with proper
state tracking to ensure the cursor is restored correctly on all exit
paths.

### Changes Made
1. **CollectLinuxCommand.cs**: Enhanced cursor visibility handling
   - Cache original cursor visibility state before hiding
   - Only hide cursor when `!Console.IsOutputRedirected`
   - Restore original cursor state in `finally` block only if changed
   - Prevents changing cursor on early return paths (e.g., `--probe`)

2. **CollectLinuxCommandFunctionalTests.cs**: Updated and improved tests
- Renamed test to
`CollectLinuxCommand_DoesNotChangeCursorVisibility_WhenOutputIsRedirected`
to better reflect behavior
- Converted to theory with `InlineData` to test both `true` and `false`
initial cursor states
   - Verifies cursor remains in original state when output is redirected
   - Removed brittle line number reference from comment

### Testing
- All 32 CollectLinuxCommand tests passing (31 passed, 1 skipped for
platform)
- Theory now runs twice: once with cursor initially visible, once with
cursor initially hidden
- All cursor visibility tests passing
- No regressions in existing functionality

### Code Changes
```csharp
bool cursorVisibilityChanged = false;
bool originalCursorVisible = false;

// Only hide cursor if output is not redirected
if (!Console.IsOutputRedirected)
{
    originalCursorVisible = Console.CursorVisible;
    Console.CursorVisible = false;
    cursorVisibilityChanged = true;
}

// In finally block:
if (cursorVisibilityChanged)
{
    Console.CursorVisible = originalCursorVisible;
}
```

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>`dotnet-trace collect-linux` leaves terminal cursor
hidden after tracing completes</issue_title>
> <issue_description>### Description
> 
> After `dotnet-trace collect-linux` finishes collecting a trace and
exits, the terminal cursor remains hidden. The cursor is not restored to
its visible state on exit, leaving the user with an invisible cursor in
their terminal session.
> 
> ### Steps to Reproduce
> 
> 1. Open a terminal session.
> 2. Run `dotnet-trace collect-linux -p <pid>` (or any valid
collect-linux invocation).
> 3. Wait for trace collection to complete, or stop it with `Ctrl+C` /
`Enter`.
> 4. Observe that the terminal cursor is no longer visible after the
tool exits.
> 5. The terminal is still functional (typing works), but the cursor is
invisible.
> 
> ### Expected Behavior
> 
> After `dotnet-trace collect-linux` exits — whether through normal
completion, user-initiated stop, or signal — the terminal cursor should
be restored to its visible state. The tool should ensure terminal state
cleanup happens on all exit paths.
> 
> ### Actual Behavior
> 
> The cursor remains hidden after the tool exits. Users must manually
restore it by running `tput cnorm` or `reset` to get their cursor back.
> 
> ### Additional Context
> 
> - The tool likely hides the cursor (e.g., via ANSI escape `\e[?25l`)
during collection for TUI/progress rendering but does not emit the
corresponding show-cursor sequence (`\e[?25h`) on exit.
> - The fix should ensure cursor restoration on all exit paths: normal
exit, `SIGINT`, `SIGTERM`, and any error/panic paths.
> - Consider using a cleanup guard or `defer`/`finally` pattern to
guarantee terminal state is restored regardless of how the tool
exits.</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes #5706

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: hoyosjs <19413848+hoyosjs@users.noreply.github.com>
Co-authored-by: mdh1418 <16830051+mdh1418@users.noreply.github.com>
Add a warning when loading a Linux/macOS dump that was not collected by
the .NET runtime's createdump tool. System dumps may be missing memory
regions required by SOS commands.

The warning is triggered by checking for the absence of the
DIAGINFOHEADER signature at the well-known diagnostic page address. This
is implemented for both the dotnet-dump analyzer and the native debugger
(dbgeng/lldb) host paths.

Fixes #5632

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com>
Move name2ee and token2ee to C# to make them easier to maintain.
Implement suggestions in #4993 to reduce the amount of spam output when
searching.

Fixes #4993.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
When a Linux thread is on an alternate signal stack (e.g. stack overflow
handler), SP points to the handler stack while managed objects live on
the normal thread stack. The existing code only scans SP + 0xFFFF and
misses everything.

This change adds GetStackRangeWithAltStackDetection() which collects
frame SPs from the managed stack walk, sorts them, and detects a
disjoint gap (>4MB) indicating two separate stack regions. When found,
both the handler stack and normal thread stack are scanned for objects.

macOS uses Mach exceptions rather than sigaltstack, so this scenario
does not happen there. No platform check is needed because the frame-
based gap detection is harmless when stacks are contiguous.

Fixes #4814

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
IL stubs (P/Invoke and Reverse P/Invoke marshaling stubs) have a nil
metadata token (0x06000000) and no IL body. The DumpIL command now
detects this case early via IsNilToken and prints a clear message
instead of the cryptic 'error decoding IL' / 'ilAddr is 0'. Also
improved the fallback error message when GetILAddress fails for non-nil
tokens.

This is the right fix, as IL for stubs is freed after JIT'ing in
FreeCompileTimeState. Sometimes they persist if the IL was put on the
loader heap for a couple of specific stubs, but it's not worth building
a big feature to display these in SOS, so we'll improve the message and
be done.

Fixes #3005

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes issue #675, but also just improves the amount of symbol lookups we
do.
Updates `Microsoft.OneCollect.RecordTrace` from `0.1.32221` to
`0.1.33304` in `eng/Versions.props`.

- **NativeAOT tracing fix**: The new package version fixes tracing
nativeAOT apps with `collect-linux` (dotnet/runtime#123697)
- **Logging support**: `0.1.33304` adds `--log-mode`, `--log-filter`,
and `--log-path` to `librecordtrace`, which were absent in `0.1.32221`.
This unblocks #5709 (adding diagnostic logging to `collect-linux`)

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mdh1418 <16830051+mdh1418@users.noreply.github.com>
Don't truncate if we are asked not to.

Fixes #4415

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Customer requested allowing some e_type values (0xFE00-0xFEFF) in the
symbol server key generated. This allows key generation for ELF binaries
produced by custom toolchains that use the ET_LOOS..ET_HIOS range
defined in the ELF spec.

There's no way for us to test this, but it's a completely reasonable
request and easy/low risk of regression. I suggest we fix it for his
internal symbol server. Otherwise close the original bug as won't fix.
That's fine by me too.

Fixes #5089
This one is a bit outside of my wheelhouse. I had to lean into copilot
for building and testing to fix this. This gist explains the overall
change, reasoning, and testing:
https://gist.github.com/leculver/3b97b312ff5217441e35cf7c57d21a17.

The underlying issue does legitimately break tools trying to use SOS
within them. The best example for me personally is lldb-dap, hosting
lldb as a subprocess does strange things with SOS where the command
output comes as events instead of text output like 99.99% of everything
else.

This change makes LLDB produce the right output in the right
circumstances.

I generally write the fixes myself, even with llm help, but the
following is (unfortunately) copilot generated, and the fix too. But it
does work quite well in the tests I built and fixes the issue I have in
lldb-dap in my personal project.

Fixes #4086.

--

SOS commands invoked via SBCommandInterpreter.HandleCommand() were
producing console output but returning 0 bytes in SBCommandReturnObject.
This broke LLDB Python scripts that capture SOS output programmatically.

Root cause: LLDBServices::OutputString() only wrote to file handles via
fputs(), completely bypassing the LLDB result object.

Fix: Add m_currentResult pointer to LLDBServices. sosCommand::DoExecute
sets it before calling the SOS command function and clears it after.
OutputString() now writes to the SBCommandReturnObject via Printf() when
a result is set, or falls back to fputs() for direct console use. This
avoids double output — LLDB auto-prints the result object content after
DoExecute returns, so fputs must be skipped when capturing.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Quick fix to add David's requested -stat to threadpool. The formatting
code already exists in DumpHeapService, we just turn the print into an
enumeration and either dump the stats or the raw work items.

Fixes #531

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This pull request updates the following dependencies

[marker]: <> (Begin:39a983c9-68a4-46ae-bbba-6795aab4810d)
## From https://github.com/dotnet/arcade
- **Subscription**:
[39a983c9-68a4-46ae-bbba-6795aab4810d](https://maestro.dot.net/subscriptions?search=39a983c9-68a4-46ae-bbba-6795aab4810d)
- **Build**:
[20260223.2](https://dev.azure.com/dnceng/internal/_build/results?buildId=2910551)
([302932](https://maestro.dot.net/channel/8394/github:dotnet:arcade/build/302932))
- **Date Produced**: February 23, 2026 12:42:20 PM UTC
- **Commit**:
[4d898652733deb7dd274237ac06d27ee2ad85b36](dotnet/arcade@4d89865)
- **Branch**:
[release/10.0](https://github.com/dotnet/arcade/tree/release/10.0)

[DependencyUpdate]: <> (Begin)

- **Dependency Updates**:
  - From [10.0.0-beta.26110.1 to 10.0.0-beta.26123.2][3]
     - Microsoft.DotNet.Arcade.Sdk
     - Microsoft.DotNet.CodeAnalysis

[3]: dotnet/arcade@4bf37ce...4d89865

[DependencyUpdate]: <> (End)


[marker]: <> (End:39a983c9-68a4-46ae-bbba-6795aab4810d)

---------

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
## Summary

When diagnostic tools (dotnet-trace, dotnet-counters, dotnet-stack,
dotnet-dump) run in a sidecar container with `pid: host`, they cannot
find the target process's diagnostic IPC socket. The socket is named
using the container-internal PID (NSpid) and located under
`/proc/{hostPid}/root/{tmpdir}/`, but the tools only look in local
`/tmp/`.

This PR adds cross-namespace IPC resolution to
`Microsoft.Diagnostics.NETCore.Client` so all diagnostic tools work
transparently across PID namespaces. It also adds TMPDIR-aware
resolution for same-namespace processes with a non-default TMPDIR.

Fixes #5694

## Changes

### Commit 1: Core IPC transport support
- Add `TryGetNamespacePid()` — reads `/proc/{pid}/status` NSpid to
detect cross-namespace processes
- Add `GetProcessTmpDir()` — reads `/proc/{pid}/environ` for TMPDIR
(returns `out bool environReadable` for error messaging)
- Parameterize `TryResolveAddress()` — handles both Windows named pipes
and Unix sockets from any search directory
- Simplify `GetDefaultAddress()` flow: resolve one (searchDirectory,
searchPid) pair, single `TryResolveAddress` call
- All Linux-specific methods self-guard, returning safe defaults on
other platforms

### Commit 2: Process enumeration
- Add `GetCrossNamespacePublishedProcesses()` — scans `/proc` for
processes in different namespaces with diagnostic sockets
- Refactor `GetLocalPublishedProcesses()` to be self-contained (handles
its own file listing and exceptions)
- `GetPublishedProcesses()` is a clean union of both methods
- Introduce `ProcPath` constant to avoid hardcoding

### Commit 3: Unit tests
- Extract `TryParseNamespacePid()` and `ParseTmpDir()` for testable
parsing logic
- 9 parsing tests with synthetic data (NSpid variants, TMPDIR parsing)
- 4 behavioral tests with `[ConditionalFact]` (same-namespace detection,
child process TMPDIR reading, platform guards, error paths)

### Commit 4: Address IPC transport review feedback
- Rename `basePath` → `searchDirectory` in `TryResolveAddress`, update
doc comment
- Make `TryResolveAddress` internal for reuse in enumeration
- Change `GetProcessTmpDir` to return `out bool environReadable` for
conditional error messaging
- Narrow try/catch: `DirectoryNotFoundException` for process-exit races,
`UnauthorizedAccessException` for `/proc/{pid}/environ` (owner-only
0400)
- Add `CheckProcessExists` helper (catches `ArgumentException` from
`Process.GetProcessById`)
- Use `Path.Combine` for cross-namespace path construction to handle
relative TMPDIR
- Simplify `GetDefaultAddress` to single `TryResolveAddress` call with
Linux-guarded path resolution
- Add same-namespace different-TMPDIR support: on Linux, use target's
TMPDIR instead of `IpcRootPath`
- Fix error message: reference target's search directory, conditional
TMPDIR hint when environ is unreadable, restore original 108-char socket
path guidance

### Commit 5: Address enumeration review feedback
- Rename `GetCrossNamespacePublishedProcesses` →
`GetProcPublishedProcesses` (now discovers both cross-namespace AND
same-namespace different-TMPDIR)
- Use `TryResolveAddress` instead of `Directory.GetFiles` for robust
socket discovery (handles stale sockets, dsrouter)
- Use `Directory.EnumerateDirectories` for streaming `/proc` iteration
- Use `CheckProcessExists` helper in both enumeration methods

### Commit 6: Update tests for feedback changes
- Handle `hidepid` fallback in child process TMPDIR test (accept default
when environ unreadable)
- Remove `GetDiagnosticSocketSearchPattern` tests (method removed —
`TryResolveAddress` used instead)

## How it works

```
GetDefaultAddress(pid):
  1. CheckProcessExists(pid) or throw
  2. On Linux:
     a. GetProcessTmpDir(pid) → target's TMPDIR (falls back to Path.GetTempPath())
     b. TryGetNamespacePid(pid) → cross-namespace?
        If yes: searchDirectory = /proc/{pid}/root/{targetTmpDir}, searchPid = nsPid
        If no:  searchDirectory = targetTmpDir, searchPid = pid
     On other platforms:
        searchDirectory = IpcRootPath, searchPid = pid
  3. TryResolveAddress(searchDirectory, searchPid)
  4. Error with platform-specific guidance
```

## Docker Validation

Tested with two containers: target-app (PID 1 inside, host PID 2209) and
tracer (`privileged: true`, `pid: host`).

**Baseline (stock tools v9.0.661903):** All tools fail with
`ServerNotAvailableException`.

| Tool | Command | Result |
|------|---------|--------|
| dotnet-trace | `ps` | ❌ No processes found |
| dotnet-trace | `collect -p 2209` | ❌ ServerNotAvailableException |
| dotnet-trace | `collect-linux --probe -p 2209` | ❌
ServerNotAvailableException |
| dotnet-trace | `collect-linux -p 2209` | ❌ ServerNotAvailableException
|
| dotnet-counters | `ps` | ❌ No processes found |
| dotnet-counters | `monitor -p 2209` | ❌ ServerNotAvailableException |
| dotnet-stack | `report -p 2209` | ❌ ServerNotAvailableException |
| dotnet-dump | `collect -p 2209` | ❌ ServerNotAvailableException |

**Fix (this branch):** All tools work.

| Tool | Command | Result |
|------|---------|--------|
| dotnet-trace | `ps` | ✅ Discovers process |
| dotnet-trace | `collect -p 2209 --duration 5s` | ✅ Trace completed
(404KB nettrace) |
| dotnet-trace | `collect-linux --probe -p 2209` | ✅ Connected
successfully (runtime version check) |
| dotnet-trace | `collect-linux -p 2209 --duration 5s` | ✅ Connected
successfully (runtime version check) |
| dotnet-counters | `ps` | ✅ Discovers process |
| dotnet-counters | `monitor -p 2209 --duration 5` | ✅ Collected
counters |
| dotnet-stack | `report -p 2209` | ✅ Thread stacks printed |
| dotnet-dump | `collect -p 2209` | ✅ Dump written |

> **Note on collect-linux:** The `collect-linux` tests connect
successfully (proving cross-namespace IPC works) but report a runtime
version mismatch because the target runs .NET 10.0.0-preview.7 and
`collect-linux` requires 10.0.0 RTM. This is expected — the baseline
fails with `ServerNotAvailableException` (can't connect at all), while
the fix gets past the connection to the version check.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…_CHANGE_VERSION (#5746)

## Summary

Matching PR for dotnet/runtime#125231, which fixes
`DefaultCOMImpl::Release()` reference counting bugs in the DAC.

### Changes

**1. Bump `SOS_BREAKING_CHANGE_VERSION` from 5 to 6** (`sospriv.h`,
`sospriv.idl`)

The runtime PR bumps this version. Without updating SOS's copy, users
would see a spurious "SOS needs to be upgraded" warning when debugging
newer runtimes.

**2. Fix `SOSMethodEnum` simulator COM ref counting** (`util.cpp`)

The `SOSDacInterface15` simulator's `GetMethodTableSlotEnumerator` had
the same bug pattern fixed in the runtime PR — raw pointer assignment
without `QueryInterface`, and the null check occurred after the
dereference. Fixed to:
- Initialize `refCount` to 0 (matching `DefaultCOMImpl` convention)
- Use `QueryInterface` to return the interface, properly calling
`AddRef`
- Check for null before dereferencing the out parameter
- Use `delete` on failure (since `QueryInterface` wasn't called at
refCount 0)

**3. Fix `ISOSMethodEnum` leak in `strike.cpp`**

Changed raw `ISOSMethodEnum*` to `ToRelease<ISOSMethodEnum>` so
`Release()` is called when the pointer goes out of scope, matching the
pattern used everywhere else (e.g., `ToRelease<ISOSHandleEnum>`).

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary

Matching PR for dotnet/runtime#125231, which fixes
`DefaultCOMImpl::Release()` reference counting bugs in the DAC.

### Changes

**1. Fix `SOSMethodEnum` simulator COM ref counting** (`util.cpp`)

The `SOSDacInterface15` simulator's `GetMethodTableSlotEnumerator` had
the same bug pattern fixed in the runtime PR — raw pointer assignment
without `QueryInterface`, and the null check occurred after the
dereference. Fixed to:
- Initialize `refCount` to 0 (matching `DefaultCOMImpl` convention)
- Use `QueryInterface` to return the interface, properly calling
`AddRef`
- Check for null before dereferencing the out parameter
- Use `delete` on failure (since `QueryInterface` wasn't called at
refCount 0)

**2. Fix `ISOSMethodEnum` leak in `strike.cpp`**

Changed raw `ISOSMethodEnum*` to `ToRelease<ISOSMethodEnum>` so
`Release()` is called when the pointer goes out of scope, matching the
pattern used everywhere else (e.g., `ToRelease<ISOSHandleEnum>`).

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
hoyosjs and others added 19 commits March 6, 2026 12:23
On Linux, the in-framework Console.CursorVisible getter always throws
PlatformNotSupportedException while the setter works via ANSI escape
codes. The collect-linux command was reading Console.CursorVisible to
save and restore cursor state, which caused an unhandled exception on
every invocation. Since the tests were mocking console, nothing got
caught.

The getter is unsupported on Linux (the only platform collect-linux runs
on) - the most sensible thing was to remove the read entirely and always
restore cursor visibility to true on exit.
`Console.KeyAvailable` and `LineRewriter.RewriteConsoleLine` throw
exceptions when input and output are redirected.

`dotnet-trace collect` works with both redirected and has similar code
to what's proposed here.
This record-trace version should contain the rbp chain walking fix and
nested container nspid resolution fix.
… events in collect-linux (#5756)

`dotnet-trace collect-linux` creates provider flags with
`new_dotnet_provider_flags()` which defaults callstacks to false. This
causes `perf_event` to skip callchain capture for .NET runtime events
(exceptions, GC, etc.), resulting in stacks that only show the kernel's
`user_events_write_core` frame with no user-space context.

This adds `with_callstacks()` to the provider flags so `perf_event`
captures callchains for runtime events. Without this fix, the exception
stacks view in PerfView shows:
```
Name
 ROOT
+ Process32 dotnet-sample (2672) Args: dotnet-sample
 + Thread (2683)
  + BROKEN
   + vmlinux!user_events_write_core.isra.0
    + Throw() 
```

With this fix, the physical call stack is captured including resolved
runtime frames:
```
Name
 ROOT
+ Process32 dotnet-sample (9808) Args: dotnet-sample
 + Thread (9819)
  + BROKEN
   + libcoreclr.so!DispatchManagedException(Object*, _CONTEXT*, _EXCEPTION_RECORD*)
    + libcoreclr.so!DispatchCallSimple(unsigned long*, unsigned int, unsigned long, unsigned int)
     + libcoreclr.so!CallDescrWorkerInternal
      + ManagedModule!EH.DispatchEx
      |+ libcoreclr.so!SfiInit
      | + libcoreclr.so!SfiInitWorker(StackFrameIterator*, _CONTEXT*, bool, bool*)
      |  + libcoreclr.so!ETW::ExceptionLog::ExceptionThrown(CrawlFrame*, int, int)
      |   + libcoreclr.so!EventPipeWriteEventExceptionThrown_V1
      |    + libcoreclr.so!ep_write_event(_EventPipeEvent*, unsigned char*, unsigned int, unsigned char const*, unsigned char const*)
      |     + libcoreclr.so!write_event_2(Thread*, _EventPipeEvent*, _EventPipeEventPayload*, unsigned char const*, unsigned char const*, Thread*, _EventPipeStackContents*)
      |      + libcoreclr.so!ep_session_write_event(_EventPipeSession*, Thread*, _EventPipeEvent*, _EventPipeEventPayload*, unsigned char const*, unsigned char const*, Thread*, _EventPipeStackContents*)
      |       + libc.so.6!writev
      |        + vmlinux!entry_SYSCALL_64_after_hwframe
      |         + vmlinux!do_syscall_64
      |          + vmlinux!x64_sys_call
      |           + vmlinux!__x64_sys_writev
      |            + vmlinux!do_writev
      |             + vmlinux!vfs_writev
      |              + vmlinux!do_iter_write
      |               + vmlinux!do_iter_readv_writev
      |                + vmlinux!user_events_write_iter
      |                 + vmlinux!user_events_write_core.isra.0
      |                  + Throw() 

```
Note: collect-linux captures the physical CPU call stack at the
tracepoint fire site, which includes runtime internals. The clean
managed-only stacks shown by dotnet-trace collect (e.g.,
`ExceptionWorker` -> `ThrowNested` -> `Throw`) require the runtime's
internal managed stack walker, which is not yet included in user_events
tracepoint data.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…failure (#5751)

Investigating #5750 

The test fails on Ubuntu 22.04 CI with environ readable but TMPDIR not
matching the custom value. Add diagnostic output to the assertion
messages to capture: environ byte size, env var count, raw TMPDIR entry,
parent TMPDIR, ProcessStartInfo TMPDIR, file permissions, child exit
state, current user, and first 200 bytes of environ content.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…adDotNetExtensions (#5759)

## Summary

Contributes to #5757

CDB 10.0.26100.1 introduced `SecureLoadDotNetExtensions` which verifies
digital signatures of DAC DLLs before loading them. Both the .NET
Framework `mscordacwks.dll` (from local Framework directory and symbol
server) and servicing .NET Core DAC DLLs fail this verification, causing
16 test failures across all Windows legs:

```
Failed to verify signature of file: C:\Windows\Microsoft.Net\Framework64\v4.0.30319\mscordacwks.dll
Error code - 0x000021BE
Debugger.Settings.EngineInitialization.SecureLoadDotNetExtensions is enabled, set to false to disable.
Failed to load data access module, 0x80004002
```

## Root Cause

The test infrastructure conditionally enables
`SecureLoadDotNetExtensions=true` for non-nightly, non-private builds.
CDB then rejects the DAC DLLs because their signatures cannot be
verified:
- `.NET Framework DAC` — `mscordacwks.dll` from
`C:\Windows\Microsoft.Net\Framework64\v4.0.30319\` and from the symbol
server
- `Servicing .NET Core DAC` — for .NET 8.0 servicing runtimes

This affects three test categories:
- **13 `desktop.cli` tests** — desktop CLR DAC signature verification
fails
- **2 `projectk.sdk.prebuilt` tests** (DualRuntimes) — desktop CLR
portion of dual-runtime dump fails
- **1 `projectk.cli.singlefile` test** (StackAndOtherTests) — .NET 8.0
servicing DAC fails

## Changes

All changes are in `SOS.cs` — disable the affected tests until the CDB
signing issue is resolved:

1. **Exclude desktop CLR configurations** from `SOS.Configurations` —
filters out `IsDesktop` configs that rely on `mscordacwks.dll`
2. **Skip `StackAndOtherTests`** for single-file on Windows — .NET 8.0
servicing DAC signature verification fails under CDB
3. **Skip `DualRuntimes`** on Windows — desktop CLR DAC portion of
dual-runtime dump fails

## Impact

- Disables **16 tests** blocked by CDB DAC signature verification
failures
- Tests can be re-enabled once the signing issue with the CDB package is
resolved (tracked by #5757)
- Test-only change — no product code or test infrastructure changes

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This pull request updates the following dependencies

[marker]: <> (Begin:39a983c9-68a4-46ae-bbba-6795aab4810d)
## From https://github.com/dotnet/arcade
- **Subscription**:
[39a983c9-68a4-46ae-bbba-6795aab4810d](https://maestro.dot.net/subscriptions?search=39a983c9-68a4-46ae-bbba-6795aab4810d)
- **Build**:
[20260318.1](https://dev.azure.com/dnceng/internal/_build/results?buildId=2929531)
([306738](https://maestro.dot.net/channel/8394/github:dotnet:arcade/build/306738))
- **Date Produced**: March 18, 2026 10:20:24 AM UTC
- **Commit**:
[3907f62e877e105b6196b1bd9c309203d6362a0a](dotnet/arcade@3907f62)
- **Branch**:
[release/10.0](https://github.com/dotnet/arcade/tree/release/10.0)

[DependencyUpdate]: <> (Begin)

- **Dependency Updates**:
  - From [10.0.0-beta.26123.2 to 10.0.0-beta.26168.1][1]
     - Microsoft.DotNet.Arcade.Sdk
     - Microsoft.DotNet.CodeAnalysis

[1]: dotnet/arcade@4d89865...3907f62

[DependencyUpdate]: <> (End)


[marker]: <> (End:39a983c9-68a4-46ae-bbba-6795aab4810d)

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
https://dev.azure.com/dnceng-public/public/_build/results?buildId=1348854
For an unknown reason, the cDAC SOS test runs have better !clrstack
output than that of the DAC in these three tests - the DAC output
resolves the bottom of the stack to the start of the method, whereas the
cDAC is able to resolve the exact line. The tests currently expect the
start of the method, so they fail on the cDAC. This just loosens the
tests to accept either the exact line or the start of the method.
…een enumeration and usage. (#5778)

Fixes #5776
Fixes issues seen in CollectLinuxCommand_Probe_ListsProcesses_WhenNoArgs and CollectLinuxCommand_Probe_Csv
OneCollect has some fixes that will improve `dotnet-trace
collect-linux`'s tracing experience.
- microsoft/one-collect#239
- microsoft/one-collect#240
…s, literal output assertions (#5773)

- [x] Address review comment 1: refactor `ProgressCallback` as optional
method parameter on `EnumerateFilteredObjects` (remove from
`HeapWithFilters` class property)
- [x] Address review comment 2: rewrite byte-tracking in
`EnumerateFilteredObjects` to use segment position (`obj.Address -
segment.Start`, 16KB threshold, segment start/end callbacks)
- [x] Refactor `ProgressReporter` to be the timed console writer
registered as callback (takes `Action<string>` writeMessage, receives
`(scannedBytes, totalBytes)` via `Report` method)
- [x] Update `DumpHeapCommand` and `VerifyHeapCommand` to use new API
- [x] Update `ProgressReporterTests` to match new `ProgressReporter` API
(7/7 tests pass)

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> Modify Dan Moseley's PR #5763 (branch: danmoseley:heap-progress) to do
the following:
> 
> 1. Add a flag to opt out of progress reporting for heap enumeration.
> 2. Update the unit test cases so that they validate literal output
from progress lines, rather than simply inferring correctness using
text.Contains("5").
> 3. Refactor progress timing tests to use TimeProvider, so that the
test does not rely on sleep() or time delays, and instead
deterministically validates timing.
> 
> Requirements:
> - Show the full `git diff` of changes before pushing to the PR branch.
> - Do not push any changes until the user explicitly approves after
seeing the diff.
> 
> The following is the prior conversation context from the user's chat
exploration (may be truncated):
> 
> User: Can you push commits to this PR?
> 
> Assistant: **Yes, you should be able to push commits to this PR.**
Here's why:
> 
> 1. **`maintainer_can_modify` is `true`** — The PR author (@danmoseley)
enabled "Allow edits from maintainers" when creating this PR from their
fork (`danmoseley/diagnostics` → `dotnet/diagnostics`).
> 
> 2. **You have write access** — As long as you have push/write
permissions to the base repository (`dotnet/diagnostics`), GitHub allows
you to push commits directly to the PR's head branch (`heap-progress` on
`danmoseley/diagnostics`).
> 
> You can push to the branch by adding the fork as a remote and pushing
to the `heap-progress` branch:
> 
> ```bash
> git remote add danmoseley
https://github.com/danmoseley/diagnostics.git
> git push danmoseley HEAD:heap-progress
> ```
> 
> This is documented in GitHub's guide on [Committing changes to a pull
request branch created from a
fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/committing-changes-to-a-pull-request-branch-created-from-a-fork).
> 
> User: Do you have permissions to modify this PR?
> 
> Assistant: I'm waiting for your confirmation to proceed with the tool
call that was just proposed.
> 
> User: @Copilot Accepted Confirmation: Confirm agent session
> 
> Assistant: I don't have the ability to directly check my own
permissions on a repository. However, I've kicked off a task that will
attempt to interact with the PR, which will effectively test whether I
have write access:
> 
> ````list type="task"
> data:
>     - title: '[WIP] Check push permissions to PR #5763'
>       repository: dotnet/diagnostics
> url:
https://github.com/copilot/tasks/pull/PR_kwDOB4aLFs7MAicv?session_id=88a58d80-0bba-4b9d-bf31-fcfdb05e5cf0
>       state: queued
> 
> ````
> 
> That said, to answer your question more directly: **I don't know for
certain whether I (Copilot) have write permissions to
`dotnet/diagnostics`**. That depends on:
> 
> 1. **Whether GitHub Copilot is installed** on the `dotnet/diagnostics`
repository (or the `dotnet` org) with write permissions.
> 2. **Whether the org/repo settings** allow Copilot to push to forks
via `maintainer_can_modify`.
> 
> If the task above fails with a permissions error, that will confirm I
don't have the necessary access. In that case, you (hoyosjs) would need
to push the commits yourself, assuming you have write access to
`dotnet/diagnostics`.
> 
> User: Modify Dan Moseleys pr to:
> 
> - add a flag to opt out of progress report 
> -we actually see some literal output in the test cases rather than
just infering the whole thing is good from text.Contains("5")
> - use TimeProvider to validate the progress part without relying on
sleep() in the test (or the current state that doesn't actually validate
progress timing)
> 
> Show me the diff before pushing to the PR
> 
> Assistant: I'll prepare a diff that modifies Dan Moseley's PR as you
requested—adding a flag to opt out of progress reporting, improving test
assertions to check the actual output, and refactoring timing
verification to use TimeProvider instead of sleep.
> 
> Once the changes are ready, I will show you the complete diff so you
can review it before anything is pushed.
> 
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Send tasks to Copilot coding agent from
[Slack](https://gh.io/cca-slack-docs) and
[Teams](https://gh.io/cca-teams-docs) to turn conversations into code.
Copilot posts an update in your thread when it's finished.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Dan Moseley <danmose@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: hoyosjs <19413848+hoyosjs@users.noreply.github.com>
Co-authored-by: noahfalk <6243776+noahfalk@users.noreply.github.com>
This pull request updates the following dependencies

[marker]: <> (Begin:39a983c9-68a4-46ae-bbba-6795aab4810d)
## From https://github.com/dotnet/arcade
- **Subscription**:
[39a983c9-68a4-46ae-bbba-6795aab4810d](https://maestro.dot.net/subscriptions?search=39a983c9-68a4-46ae-bbba-6795aab4810d)
- **Build**:
[20260327.7](https://dev.azure.com/dnceng/internal/_build/results?buildId=2937446)
([308097](https://maestro.dot.net/channel/8394/github:dotnet:arcade/build/308097))
- **Date Produced**: March 27, 2026 3:14:28 PM UTC
- **Commit**:
[62dc2defffeadabf6761a9ed7e142692107330c0](dotnet/arcade@62dc2de)
- **Branch**:
[release/10.0](https://github.com/dotnet/arcade/tree/release/10.0)

[DependencyUpdate]: <> (Begin)

- **Dependency Updates**:
  - From [10.0.0-beta.26168.1 to 10.0.0-beta.26177.7][1]
     - Microsoft.DotNet.Arcade.Sdk
     - Microsoft.DotNet.CodeAnalysis

[1]: dotnet/arcade@3907f62...62dc2de

[DependencyUpdate]: <> (End)


[marker]: <> (End:39a983c9-68a4-46ae-bbba-6795aab4810d)

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
…mpDir tests (#5782)

There's a short period of time between `Process.Start` returning, and
the pinvokes into fork and exec. During this period, the target might
not have a populated environ procfs file. Loop with a sleep and try
again.
…bing (#5705)

## Summary

Handle exceptions in `CollectLinuxCommand` process probing to gracefully
handle processes that cannot be resolved or connected to.

Builds on #5778 (Juan's simple catch-and-skip fix) by adding structured
error reporting and covering an additional failure mode (`IOException`
from mid-response disconnection).

## Problem

When `collect-linux --probe` enumerates all published .NET processes,
several exceptions can occur between discovery and probing:

- **`ServerNotAvailableException`** — diagnostic socket not found
(process exited before connection)
- **`IOException`** ("Connection reset by peer") — process exited
mid-IPC response (#5778's `catch (DiagnosticToolException or
DiagnosticsClientException)` does not cover this)
- **`UnsupportedCommandException`** — runtime too old to support
`GetProcessInfo`
- **`DiagnosticToolException`** — process exited between
`GetPublishedProcesses` and `ResolveProcess`

Previously (before #5778), these propagated to the outer `catch
(Exception)`, printing a full stack trace to stderr and returning
`UnknownError`. #5778 added a catch-and-skip for
`DiagnosticToolException` and `DiagnosticsClientException`, but silently
drops the processes and doesn't cover `IOException`.

## What this PR adds on top of #5778

1. **Structured probe results** — `UserEventsProbeResult` enum
(`Supported`, `NotSupported`, `ConnectionFailed`) replaces boolean,
enabling distinct handling per outcome
2. **"Could not be probed" reporting** — instead of silently skipping,
processes that fail probing are reported in a dedicated section
(console) or as `unknown` (CSV), so users know these processes existed
3. **`IOException` catch** — covers the "Connection reset by peer"
scenario observed in CI that #5778 misses
4. **Single-process graceful handling** — `--probe -p PID` and
`collect-linux -p PID` now handle connection failures with clean error
messages instead of stack traces
5. **`UnsupportedCommandException` handling** — ancient runtimes that
don't support `GetProcessInfo` are reported as `NotSupported` instead of
crashing
6. **Preserved original error messages** — argument validation errors
(`-1 is not a valid process ID`, `Only one of --name or --process-id`,
etc.) still propagate with their original specific messages and
`ArgumentError` return code

## Behavior

**Non-probe mode** (`dotnet-trace collect-linux -p <pid>`):
- Connection failure: `[ERROR] Unable to connect to process '<name>
(<pid>)'. The process may have exited, or it doesn't have an accessible
.NET diagnostic port.` → `TracingError`
- Argument errors: original messages preserved → `ArgumentError`

**Single-process probe mode** (`dotnet-trace collect-linux --probe -p
<pid>`):
- Connection failure: `Could not probe process '<name> (<pid>)'. The
process may have exited, or it doesn't have an accessible .NET
diagnostic port.` → `Ok`
- Argument errors: original messages preserved → `ArgumentError`

**Multi-process probe mode** (`dotnet-trace collect-linux --probe`):
- Console output shows ".NET processes that could not be probed" section
when applicable
- CSV output includes `unknown` value for unprobed processes
- Processes that exit during name resolution are silently skipped
- Processes that exit during probing are reported with name in the
"could not be probed" section

---------

Co-authored-by: mdh1418 <mitchhwang1418@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Mitchell Hwang <16830051+mdh1418@users.noreply.github.com>
…sWriter (#5771)

When CursorTop is 0 (e.g., in environments where cursor positioning
isn't available), LineToClear was set to -1, causing SetCursorPosition
to throw ArgumentOutOfRangeException on progress callbacks.

Extract the progress display concern from OutputHandler into a nested
ProgressWriter class that:
  - Probes console capability on first Update() call
  - Interactive: rewrites status line in-place with 1s throttle
  - Non-interactive: prints a static message once, silently no-ops after
  - Owns LineRewriter and LineToClear internally

OutputHandler's status block reduces to progressWriter.Update(), fully
decoupling it from LineRewriter. Matches CollectCommand's pattern of
probing capability upfront and gating on it.

Also adds bounds validation to MockConsole.SetCursorPosition and a
regression test for the CursorTop=0 case.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hoyosjs hoyosjs requested a review from a team as a code owner April 2, 2026 06:57
Copilot AI and others added 2 commits April 2, 2026 00:29
…indows (#5593)

## Summary

Fixes #5020 - `DiagnosticsClient.WriteDump()` now correctly handles dump
file paths containing spaces on Windows.

## Problem

When calling `WriteDump()` with a path containing spaces on Windows, the
operation would fail with the error:
```
[createdump] The pid argument is no longer supported
```

This occurred because the runtime's `createdump` utility on Windows was
parsing the unquoted path as multiple command-line arguments. For
example, a path like `C:\my dumps\dump.dmp` would be split into `C:\my`
and `dumps\dump.dmp`, causing the latter part to be misinterpreted as
additional arguments.

This issue is specific to Windows due to how the runtime builds the
command line for createdump. Linux and macOS do not have this problem.

## Solution

The fix wraps the dump path in quotes before sending it to the runtime
**on Windows only**:
- Paths are now automatically quoted on Windows: `C:\my dumps\dump.dmp`
→ `"C:\my dumps\dump.dmp"`
- Logic prevents double-quoting if the path is already quoted (handles
the workaround some users may have implemented)
- Non-Windows platforms (Linux/macOS) are unaffected and paths are
passed through unchanged

## Changes

Modified the two `CreateWriteDumpMessage` method overloads in
`DiagnosticsClient.cs` to:
1. Check the platform using
`RuntimeInformation.IsOSPlatform(OSPlatform.Windows)`
2. Quote the dump path before serialization only on Windows
3. Check if the path is already quoted to avoid double-quoting

```csharp
// Quote the path to handle spaces correctly in createdump on Windows only
// Only add quotes if the path is not already quoted
// This is only needed on Windows where the runtime builds the command line for createdump
string pathToUse = dumpPath;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
    pathToUse = dumpPath.StartsWith("\"") && dumpPath.EndsWith("\"") ? dumpPath : $"\"{dumpPath}\"";
}
```

## Testing

Manually verified the quoting logic handles:

**On Windows:**
- ✅ Paths without spaces: `C:\dumps\dump.dmp` → `"C:\dumps\dump.dmp"`
- ✅ Paths with spaces: `C:\my dumps\dump.dmp` → `"C:\my dumps\dump.dmp"`
(fixes the issue)
- ✅ Already quoted paths: `"C:\my dumps\dump.dmp"` → `"C:\my
dumps\dump.dmp"` (no double-quoting)

**On Linux/macOS:**
- ✅ All paths are passed through unchanged (no quoting applied)

The fix is minimal, backward compatible, platform-specific, and
addresses the reported issue while maintaining compatibility with the
workaround mentioned in the issue.

> [!WARNING]
>
> 






Fixes #5020

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>Diagnostics client fails to create dump when path has
spaces</issue_title>
> <issue_description>### Description
> 
> .NET diagnostics client fails when path to dump file has spaces in it.
This reproduces both on net8 and net9, and fails with error:
> 
> [createdump] The pid argument is no longer supported [coming from
here](https://grep.app/search?q=The%20pid%20argument%20is%20no%20longer%20supported)
> 
> It can be fixed by wrapping the path to extra `""`.
> 
> I am using the latest version of the client package.
> 
> ### Reproduction Steps
> 
> ```csproj
> <!-- file DumpError.csproj -->
> <Project Sdk="Microsoft.NET.Sdk">
> 
>   <PropertyGroup>
>     <OutputType>Exe</OutputType>
>     <TargetFramework>net8.0</TargetFramework>
>     <ImplicitUsings>enable</ImplicitUsings>
>     <Nullable>enable</Nullable>
>   </PropertyGroup>
> 
>   <ItemGroup>
> <PackageReference Include="Microsoft.Diagnostics.NETCore.Client"
Version="0.2.547301" />
>   </ItemGroup>
> 
> </Project>
> 
> ```
> 
> ```cs
> // file Program.cs
> using Microsoft.Diagnostics.NETCore.Client;
> using System.Diagnostics;
> 
> public static class Program
> {
>     public static int Main(string[] args)
>     {
>         if (args.Length > 0 && args[0] == "--hang")
>         {
>             Thread.Sleep(int.MaxValue);
>         }
> 
> var path = Path.Combine(AppContext.BaseDirectory, "DumpError.exe");
>         Process? process = null;
>         try
>         {
>             process = Process.Start(path, "--hang");
>             var diagnosticClient = new DiagnosticsClient(process.Id);
>             Thread.Sleep(1_000);
> diagnosticClient.WriteDump(DumpType.Full,
$"{Path.Combine(AppContext.BaseDirectory, $"my dump with spaces
{Stopwatch.GetTimestamp()}.dmp")}", logDumpGeneration: true);
>         }
>         finally
>         {
>             process?.Kill();
>         }
> 
>         return 0;
>     }
> }
> 
> ```
> 
> 
> 
> ### Expected behavior
> 
> Spaces are correctly escaped and my dump is written to disk.
> 
> ### Actual behavior
> 
> [createdump] The pid argument is no longer supported error is shown
and dump is not written.
> 
> ### Regression?
> 
> No.
> 
> ### Known Workarounds
> 
> Add quotes around the path, or move to folder without spaces.
> 
> ### Configuration
> 
> Win 11, 8.0.110, but reproduces on latest net9 as well.
> 
> ### Other information
> 
> _No response_</issue_description>
> 
> <agent_instructions>See what you can do to support spaces in the path
when generating a dump file calling WriteDump.</agent_instructions>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>
Fixes #5020

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: steveisok <471438+steveisok@users.noreply.github.com>
…5787)

The CollectCommandFunctionalTests substitute a MemoryStream for the
output file stream, so the trace file is never created on disk. The
interactive status-printing path is non-deterministic in these tests
because the MemoryStream-backed session completes near-instantly, and
whether the status line fires before the loop exits depends on thread
scheduling. When it does fire, FileInfo.Length throws because the file
does not exist.

Set IsOutputRedirected=true on MockConsole to disable the status path.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hoyosjs hoyosjs merged commit 68aa60a into release/stable Apr 2, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.