[fix] Fix missing dumps for .NET Framework child processes in NetClientHangDumper - #16098
Conversation
…ramework child processes When a .NET Core test host spawns .NET Framework (net462/net48) child processes, DiagnosticsClient.WriteDump() fails because those processes don't have a diagnostics IPC socket. The exception was silently swallowed, resulting in missing dumps for those child processes. On Windows, fall back to WindowsHangDumper.CollectDump() (which uses MiniDumpWriteDump via P/Invoke) when DiagnosticsClient fails. This covers both .NET Framework testhosts and other non-.NET child processes that Windows can still dump via dbghelp.dll. The outputFile path is computed before the try/catch so it's available in the fallback. Fixes #15580 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves hang-dump collection reliability in the Blame Data Collector when dumping process trees on Windows: if Microsoft.Diagnostics.NETCore.Client.DiagnosticsClient.WriteDump() fails (notably for .NET Framework or native child processes that lack a diagnostics IPC endpoint), the code now falls back to the Windows minidump-based dumper.
Changes:
- Compute the dump output path before entering the
tryso it can be reused on failures. - On Windows, add a fallback path that calls
WindowsHangDumper.CollectDump()whenDiagnosticsClient.WriteDump()throws. - Improve error logging to distinguish the primary (DiagnosticsClient) failure from the fallback failure.
| if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
| { | ||
| EqtTrace.Verbose($"NetClientHangDumper.Dump: Falling back to MiniDumpWriteDump for process {p.Id} - {p.ProcessName}."); | ||
| try | ||
| { | ||
| WindowsHangDumper.CollectDump(new ProcessHelper(), p, outputFile, type); | ||
| } | ||
| catch (Exception fallbackEx) | ||
| { | ||
| EqtTrace.Error($"NetClientHangDumper.Dump: Fallback dump also failed for process {p.Id} - {p.ProcessName}: {fallbackEx}."); | ||
| } |
Jakub Jareš (nohwnd)
left a comment
There was a problem hiding this comment.
Code Review — [Crash & Hang Dump Reliability]
Activated dimensions (from routing): Crash & Hang Dump Reliability · Error Reporting & Diagnostic Clarity · Environment Variable & Feature Flag Contracts
Summary
The fix is sound. Moving outputFile before the try block and adding the WindowsHangDumper.CollectDump fallback in the catch correctly addresses the scenario where a .NET Framework child process has no diagnostics socket and DiagnosticsClient.WriteDump times out after 30 s.
What was checked:
- Fallback safety:
MiniDumpWriteDump.CollectDumpUsingMiniDumpWriteDumpopens the file withFileMode.Create, so any partial output left by the primary attempt is cleanly overwritten. Since thetryblock contains no code afterWriteDump(), a successful primary dump is never at risk of being overwritten. - Architecture mismatches:
WindowsHangDumper.CollectDumpcorrectly handles x86/x64/ARM64 mismatches viaDumpMinitool— this is important for 32-bit.NET Frameworkchild processes running under a 64-bit host. - Process suspension: The fallback calls
CollectDump(the static helper) rather thanDump, so processes are not suspended before dumping. This is consistent withNetClientHangDumper's explicit no-suspension design, and an unsuspended MiniDump is far better than no dump for a hang scenario. - Platform guard: The
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)check is correct —.NET Frameworkprocesses can only exist on Windows, anddbghelp.dllis Windows-only. - Timeout interaction: The
CancellationTokenis checked before the task starts, not mid-execution, so the fallback may run slightly past the 5-minute deadline. This is benign:CollectDumpchecksprocess.HasExited, and the kill loop runs afterTask.WhenAllthrows, so if the timeout fires and processes are killed before the fallback runs, it exits gracefully. - PR description alignment: Matches the diff accurately.
One comment posted inline about comment scope vs. actual catch breadth — no blocking issues found.
🧠 Reviewed by Expert Code Reviewer
🧠 Reviewed by Expert Code Reviewer 🧠
| // DiagnosticsClient can only connect to .NET Core/5+ processes. For .NET Framework or native | ||
| // child processes there is no diagnostics socket, so WriteDump throws. On Windows, fall back | ||
| // to MiniDumpWriteDump which works for any Windows process. | ||
| if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
There was a problem hiding this comment.
[Crash & Hang Dump Reliability] The inline comment on lines 82–84 frames the fallback as specific to the "no diagnostics socket" case (.NET Framework / native processes), but the catch block is catch (Exception ex) — it activates for any DiagnosticsClient.WriteDump failure on Windows.
That broader scope is actually correct and beneficial: MiniDumpWriteDump.CollectDumpUsingMiniDumpWriteDump uses FileMode.Create, so it safely overwrites any partial/corrupt output the primary attempt may have left behind. And since the try block ends immediately after WriteDump() returns, a successful primary dump can never be accidentally clobbered by the fallback.
The risk is that a future reader may try to narrow this to ServerNotAvailableException based on the comment, inadvertently regressing the broader-failure handling. Consider updating the comment to make the intentional scope explicit, e.g.:
// DiagnosticsClient can only connect to .NET Core/5+ processes. On Windows, fall back
// to MiniDumpWriteDump for any DiagnosticsClient failure — this primarily covers .NET Framework
// and native child processes with no diagnostics socket, but also acts as a last resort for
// other transient failures. FileMode.Create in CollectDump ensures any partial output is replaced.There was a problem hiding this comment.
Done — updated the comment to explicitly call out that the catch (Exception ex) intentionally covers all DiagnosticsClient failures on Windows (not just missing-socket), and noted that FileMode.Create in CollectDump safely replaces any partial primary output.
🔧 Iterated by PR Iteration Agent 🔧
Update the comment to make it explicit that the catch block intentionally covers all DiagnosticsClient failures on Windows, not just the missing-socket case. Also notes that FileMode.Create in CollectDump ensures any partial primary output is safely replaced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
Jakub Jareš (nohwnd)
left a comment
There was a problem hiding this comment.
Code Review — [Crash & Hang Dump Reliability] · Follow-up
Activated dimensions: Crash & Hang Dump Reliability · Error Reporting & Diagnostic Clarity · Resource & IDisposable Management · Algorithmic Correctness
Summary
This follow-up review confirms the previous finding has been fully addressed by the PR Iteration Agent (run 27049397912). No new issues found.
What was re-checked
Finding from prior review — resolved ✅
The comment in the catch block (lines 82–85) now explicitly documents that catch (Exception ex) intentionally covers all DiagnosticsClient.WriteDump failures on Windows — not just the missing-socket case. The FileMode.Create safety note is also present. The comment in the current head matches the suggestion exactly.
Additional checks completed:
outputFilemoved beforetry: Safe in practice —p.ProcessNamewas already accessed and implicitly cached during theprocessTreeLINQ filter (line 28,.ToList()). The theoretical exception path fromPath.Combinebypassing the inner catch is benign; no real scenario where this would regress.new ProcessHelper():ProcessHelperdoes not implementIDisposable— no resource leak from the inline allocation.- Fallback isolation: The fallback
try/catch (Exception fallbackEx)correctly prevents a failingCollectDumpfrom masking the primary error already logged. process.HasExitedguard:WindowsHangDumper.CollectDumpchecksHasExitedbefore dumping — graceful behavior if the process exits before the fallback executes.- Thread safety:
ProcessHelper.GetCurrentProcessArchitecture()andGetProcessArchitecture()are read-only operations; concurrentTask.Runinvocations are safe. - PR description alignment: Description accurately reflects all code changes.
🧠 Reviewed by Expert Code Reviewer 🧠
🧠 Reviewed by Expert Code Reviewer 🧠
Summary
🤖 This is an automated fix created by the Issue Repro Triage & Auto-Fix agent.
Fixes #15580
Root Cause
NetClientHangDumperis used on Windows for.NETCoreApptargets and on all platforms for Linux/macOS. It usesDiagnosticsClient.WriteDump()(fromMicrosoft.Diagnostics.NETCore.Client) to collect hang dumps from the process tree.However,
DiagnosticsClientcan only connect to managed .NET Core/5+ processes via the diagnostics IPC socket. When a .NET Core test host has child processes that target .NET Framework (net462, net48, etc.) or native processes, there is no diagnostics socket —WriteDump()throws after a 30-second timeout. The exception was silently swallowed with only an error log, resulting in missing dump files for those child processes.Fix
On Windows, when
DiagnosticsClient.WriteDump()fails, fall back toWindowsHangDumper.CollectDump()which usesMiniDumpWriteDumpvia P/Invoke (dbghelp.dll). This API works for any Windows process, regardless of runtime (.NET Framework, native, etc.).Changes:
outputFilepath computation before thetryblock so it's accessible in the catchWindowsHangDumper.CollectDump()in the catch blockTesting
Existing unit tests pass. The existing
HangDumpChildProcessesacceptance test inBlameDataCollectorTests.cscovers the child-process dump collection path (for .NET Core child processes). A new test specifically for .NET Framework child processes would require a Windows-only test asset and is not added here — the fallback path is exercised wheneverDiagnosticsClientfails for any reason on Windows.