[Profiler] Raise the crashing-app flag even when crashtracking is disabled - #9096
Draft
dkattan wants to merge 1 commit into
Draft
[Profiler] Raise the crashing-app flag even when crashtracking is disabled#9096dkattan wants to merge 1 commit into
dkattan wants to merge 1 commit into
Conversation
…abled The mmap-shared "application is crashing" flag introduced in DataDog#7657 was only ever set on the execve path that substitutes dd-dotnet for createdump. With DD_CRASHTRACKING_ENABLED=false (or when dd-dotnet is not found), crashHandler stays NULL, ShouldCallCustomCreatedump never matches, and the profiler's signal-based stack collector keeps sampling straight through the runtime's crash handling - re-exposing the crash-on-a-crash behavior DataDog#7657 was written to fix. Capture the user-configured minidump name at library load and use it in the execve interception to recognize a crash-triggered createdump invocation (the runtime forwards DOTNET_DbgMiniDumpName through --name, or passes no --name when unset, while on-demand dump requests such as dotnet-dump always carry a client-chosen name). When recognized, raise the shared flag and pass the original createdump invocation through unchanged. Behavior with crashtracking enabled is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of changes
Raise the mmap-shared "application is crashing" flag (introduced in #7657) on the
execveinterception path even when the call is not redirected to the Datadog crash handler — i.e. whenDD_CRASHTRACKING_ENABLED=false, or whendd-dotnetis not found next to the wrapper. The original createdump invocation is then passed through unchanged.Reason for change
#7657 exists because the profiler's signal-based stack collector can interrupt a process that is in the middle of its crash handling ("crash on a crash"). But the flag is only ever set inside the
ShouldCallCustomCreatedump != 0branch of theexecvewrapper, and that branch is unreachable when crashtracking is disabled:DD_CRASHTRACKING_ENABLED=false,initLibrary()takes the early return, socrashHandlerstaysNULLandShouldCallCustomCreatedumpreturns 0 unconditionally.--name datadog_crashtrackingmarker is only planted intoDOTNET_DbgMiniDumpNamewhen crashtracking is enabled, so the marker check can never match either.Result: disabling crashtracking (a legitimate configuration for users who want the stock runtime
createdumpminidumps) silently re-exposes the exact pre-#7657 behavior — the profiler keeps sending sampling signals throughout the entire crash sequence.Production evidence (dd-trace-dotnet 3.51.1, .NET 10.0.11, Ubuntu 24.04 AKS via k8s single-step injection, all profiler samplers enabled,
DD_CRASHTRACKING_ENABLED=false,DOTNET_DbgEnableMiniDump=1): a process with a 12–17 GB heap crashing on its GC hard limit terminated with exit code 139 (second SIGSEGV during crash handling) and no dump produced, on 4 consecutive crashes. The final log line each time was the runtime'swhich is the fork-handshake in
PROCCreateCrashDump:read()returning 0 with errno 0 means the crashing parent died betweenfork()and writing the go-byte, so createdump was never even exec'd. Disabling the profiler makes dumps reliably appear again.Implementation details
profiler/src/ProfilerEngine/Datadog.Linux.ApiWrapper/functions_to_wrap.c:initLibrary()now captures the user-configuredDOTNET_DbgMiniDumpName/COMPlus_DbgMiniDumpNameintouserMiniDumpNamebefore the crashtracking-disabled early return (thereal_getenv/real_setenvlookup moved above the check to make that possible; the check itself is unchanged).IsCreatedump()extracts the existing/createdumppath-suffix test (now shared withShouldCallCustomCreatedump, which is otherwise unchanged).IsCrashTriggeredCreatedump()distinguishes a crash-triggered createdump from an on-demand dump request when thedatadog_crashtrackingmarker mechanism is not in place: following a crash the runtime forwards the raw value ofDbgMiniDumpNamethrough--name(and passes no--nameat all when the variable is not set), whereas dump-generation requests (ex:dotnet-dump collect) carry a client-chosen file name. This mirrors what the marker check does for the crashtracking-enabled path, and avoids permanently muting the profiler after a livedotnet-dump collect(the flag is sticky).execve(): whenShouldCallCustomCreatedumpsays no, butIsCrashTriggeredCreatedumpsays yes, set*is_app_crashing = 1and fall through to the originalexecve(pathname, argv, envp)unchanged — no dd-dotnet substitution, no argv/envp rewriting, so user-visible createdump behavior in this configuration is exactly as before, minus the sampling during the crash window.No reader-side changes were needed: the shared-memory flag is already mmap'd unconditionally in
init(), and the profiler consumes it through the weakdd_inside_wrapped_functions()symbol (LinuxStackFramesCollector,TimerCreateCpuProfiler,SystemCallsShield) with no crashtracking gating.Behavior with crashtracking enabled and functioning is unchanged: the crash path still goes through the marker match and the custom-createdump branch. The one intentional extension is the "crashtracking enabled but dd-dotnet binary not found" case, where the flag is now also raised (the guard was equally dead there).
Test coverage
profiler/test/Datadog.Linux.ApiWrapper.Tests/CrashFlag.cpp(runs under the existingRunNativeWrapperNativeTeststarget with the wrapper preloaded): forks, execve's a (nonexistent).../createdumpthe way the runtime's crash path does, and assertsdd_inside_wrapped_functions()becomes non-zero in the parent. This fails against master and passes with this change. Note the flag is sticky for the remainder of the test process (documented in the test).CreatedumpTests.DoNothingIfNotEnabled(enableCrashDumps: true)integration test covers this exact configuration and asserts stock createdump still runs — the pass-through here keeps it intact.gleocadie/centos7-clang16CI image (wrapper built andLD_PRELOADed into a fork/execve harness mimickingPROCCreateCrashDump):--namematchingDOTNET_DbgMiniDumpName→ flag set (was: not set on master);--name→ flag not set;DbgMiniDumpNameunset + no--name→ flag set;COMPlus_variants → flag set.functions_to_wrap.ccompiles clean (clang 16, -std=c11 -Wall, glibc and-DDD_ALPINE) in the CI image and the full.solinks;nm -Dconfirms no new exported symbols (the wrapper-symbols snapshot test should be unaffected). I could not run the full Nuke/CMake pipeline locally (macOS host), so relying on CI for the complete build and test matrix.Other details
execve()interception time, but the vulnerable window opens atfork()— thePROCCreateCrashDumppipe handshake happens before exec, and in our production crashes the parent died inside that pre-exec window. So even with crashtracking enabled, the current mechanism cannot protect the fork→exec gap; closing it (e.g. raising the flag from the runtime's SIGSEGV handler side, or intercepting the crash-pathfork) is follow-up material.GenerateCoreDumprequest whose target file name happens to equalDbgMiniDumpNamewould be treated as a crash.Refs #7657
🤖 Generated with Claude Code