Repository navigation
Add collect-linux mode to dotnet-trace integration - #3
Draft
LoopedBard3 wants to merge 13 commits into
Draft
LoopedBard3 wants to merge 13 commits into
LoopedBard3 wants to merge 13 commits into
Conversation
LoopedBard3
changed the base branch from
loopedbard3/modernize-dotnet-trace
to
main
June 3, 2026 20:29
There was a problem hiding this comment.
Pull request overview
Adds an opt-in collect-linux (and collect) mode to crank’s dotnet-trace integration so agents can shell out to the dotnet-trace CLI (including perf_event-based whole-machine tracing on modern Linux kernels) while preserving existing default behavior for current users.
Changes:
- Introduces new
Jobknobs for dotnet-trace mode selection and CLI stop timeout, with defaults intended to preserve legacy behavior. - Implements agent-side CLI installation + lifecycle management, plus Linux/root/kernel preflight for
collect-linux. - Expands unit test coverage around provider/profile normalization, kernel version parsing, and mixed-version payload compatibility.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/Microsoft.Crank.UnitTests/TraceExtensionsTests.cs | Validates CLR keyword/profile tables and EventPipe session behavior after the diagnostics client bump. |
| test/Microsoft.Crank.UnitTests/JobMixedVersionCompatTests.cs | Ensures new Job properties deserialize/round-trip with legacy-default semantics. |
| test/Microsoft.Crank.UnitTests/DotNetTraceCollectLinuxPreflightTests.cs | Tests kernel version parsing and non-Linux prereq failure message contract. |
| test/Microsoft.Crank.UnitTests/DotNetTraceCliNormalizationTests.cs | Tests CLI argv building, provider normalization, mode-specific defaults, and buffersize gating. |
| src/Microsoft.Crank.Models/Job.cs | Adds DotNetTraceBufferSizeMB, DotNetTraceRequestRundown, DotNetTraceCollectMode, DotNetTraceStopTimeoutSec. |
| src/Microsoft.Crank.Controller/README.md | Documents new dotnet-trace flags and a 3-row decision matrix. |
| src/Microsoft.Crank.Controller/Documentation.cs | Updates CLI help text for new dotnet-trace flags and decision matrix. |
| src/Microsoft.Crank.Controller/default.config.yml | Updates a diagnostics link reference (master → main). |
| src/Microsoft.Crank.Agent/TraceExtensions.cs | Updates CLR keyword aliases and adds modern dotnet-trace profiles while keeping legacy ones. |
| src/Microsoft.Crank.Agent/Startup.cs | Implements mode switching, CLI install, prereqs, argv building, and stop/kill lifecycle for CLI modes. |
| Directory.Packages.props | Bumps Microsoft.Diagnostics.NETCore.Client to 0.2.661903. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
LoopedBard3
added a commit
that referenced
this pull request
Jun 3, 2026
…, hard-fail on version mismatch Three Copilot review comments on PR #3 surfaced real backcompat / soundness issues in BuildDotnetTraceCliArgs and EnsureDotnetTraceCliInstalledAsync. All three are fixed here with regression tests. 1. Tokenizer split on ',' only; legacy Collect() splits on ',' and ' '. Users with `gc-collect gc+jit`-style configs would tokenize differently under the CLI modes. Fixed by adding ' ' to the split delimiters. 2. Token classifier routed any non-':', non-profile token to --clrevents. A bare provider name like Microsoft-DotNETCore-SampleProfiler (the example shown in the README) is neither, so it would hit --clrevents and be rejected by the CLI. Fixed by adding TraceExtensions.IsRecognizedClrKeywordExpression: classify as --clrevents only when every '+'-part is a known CLR keyword; otherwise fall through to --providers, which accepts bare provider names. 3. dotnet-trace --version mismatch with the pinned DotnetTraceVersion was warn-and-proceed. Defeats the assert's purpose: a previous run's wrong-version binary in toolPath silently shadows the pin. Promoted to hard fail (with _dotnetTracePath reset so a manual cleanup recovers), matching the 'no silent fallback' theme established for the collect-linux preflight. Tests: +3 regression tests (BuildArgs_BareProviderName_GoesToProvidersFlag, BuildArgs_PlusJoinedWithUnknownPart_RoutesToProviders, BuildArgs_SpaceSeparatedTokens_AreTokenizedSeparately). Suite: 103 / 103 passing (was 100 / 100). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Images like mysql and postgres declare VOLUME for their data directories.
When the crank agent runs a container from such an image without an
explicit named/bind mount, Docker creates a fresh anonymous volume per
container. The agent removed the container with 'docker rm' (and
'docker rm --force') without the -v flag, leaving those anonymous
volumes behind. Across daily benchmark runs on agent machines this
accumulated tens of GB of dangling volumes between the nightly
'docker system prune --volumes' cron.
Add -v to both 'docker rm' invocations so anonymous volumes attached
to the removed container are cleaned up. Named volumes and bind
mounts are unaffected by 'docker rm -v', so scenarios that opt into
persistent storage via job.Arguments continue to work.
Also fix a tightly-coupled typo on the pre-start leftover cleanup,
which was passing imageName to 'docker rm' instead of the actual
containerName (= sanitized imageName + '-{jobId}') used at 'docker
create' time, so the leftover-container path almost never matched.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
LoopedBard3
force-pushed
the
loopedbard3/dotnet-trace-collect-linux
branch
from
June 8, 2026 20:08
4fc9593 to
ec71a54
Compare
…otnet#886) * Modernize crank's dotnet-trace plumbing (no default-behavior change) PR A of the dotnet-trace modernization series. Strictly additive: every existing `--application.dotNetTrace true` invocation produces the same trace contents it did before, byte-for-byte. What changed ------------ * Bump `Microsoft.Diagnostics.NETCore.Client` to 0.2.661903 to pick up the 3-arg `StartEventPipeSession(providers, requestRundown, circularBufferMB)` overload. `Microsoft.Diagnostics.Tracing.TraceEvent` stays at 3.1.23 -- bumping it transitively forces a 4-package STJ-pack bump across the whole solution, which is out of scope here and is now tracked as a follow-up. * Refresh `TraceExtensions.cs` against `dotnet/diagnostics` `main`: * Add modern CLR keywords (`monitoring`, `codesymbols`, `compilation` family, `waithandle`, `allocationsampling`, etc.) * Add modern aliases `assemblyloader` (=`fusion`) and `managedheapcollect` (=`gcheapcollect`); preserve the legacy keys for back-compat. * Add corrected `gcsampledobjectallocation{high,low}` aliases; preserve the legacy typo'd `gcsampledobjectallcation{high,low}` keys for back-compat. * Add modern profiles `dotnet-common`, `dotnet-sampled-thread-time`, `sample-profiler`, `database` from `ListProfilesCommandHandler`. The legacy `cpu-sampling` profile is left untouched so default invocations keep producing identical trace contents. * New `Job` knobs (defaults reproduce today's exact behavior): * `DotNetTraceBufferSizeMB` (default 256, today's hardcoded value). * `DotNetTraceRequestRundown` (default true, today's implicit overload behavior). Surface as `--[JOB].dotnetTraceBufferSizeMB` and `--[JOB].dotnetTraceRequestRundown`. * `Startup.Collect(...)` and `Startup.StartDotNetTrace(...)` wire the new knobs through to `StartEventPipeSession`. * Documentation fixes: * `dotnet/diagnostics/blob/master/...` -> `.../main/...` in `Documentation.cs`, `README.md`, and `default.config.yml`. * Document the two new flags. * Add a two-row 'which trace collection mode?' matrix (`DotNetTrace=true` vs `Collect=true`). The `collect-linux` row lands in PR B. * Tests: * `TraceExtensionsTests` cover legacy + modern keyword resolution, typo<->corrected alias equivalence, profile lookup, provider-string parsing, and a smoke test that exercises the new 3-arg `StartEventPipeSession` overload end-to-end (gracefully skips in sandboxes where diagnostic IPC is unavailable). * `JobMixedVersionCompatTests` validate that a pre-PR-A job JSON payload deserializes into the legacy default values, so new-agent + old-controller and new-controller + old-agent both keep behaving like today. Tested ------ * `dotnet build Microsoft.Crank.sln -c Release` clean. * `dotnet test test/Microsoft.Crank.UnitTests` -- 67 passed, 0 failed, 0 skipped (28 new TraceExtensions + 3 new mixed-version compat + 36 pre-existing). * Manual cobalt aarch64 Ubuntu 24.04 validation pending. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove "Which trace collection mode should I use?" decision matrix The matrix was added to crank's controller help text in the dotnet-trace modernization PR. On review, embedding decision guidance directly in the flag listing crowds the help output, and the matrix will go stale as the trace collection story keeps evolving (collect-linux is still a preview verb, perfcollect's behavior on modern distros varies, etc.). Remove it here so the help text stays a flat flag reference; the same guidance will be rehomed to a dedicated docs page once the trace collection surface stabilizes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Layers a CLI-driven dotnet-trace collection path on top of PR A's
in-process EventPipe path, with collect-linux as a Linux-only opt-in
that captures kernel + native frames via perf_event_open.
What:
- New `Job.DotNetTraceCollectMode` (`default` | `collect` |
`collect-linux`). Default preserves today's in-process behavior.
- New `Job.DotNetTraceStopTimeoutSec` (0 = per-mode default: 60s for
`collect`, 180s for `collect-linux`).
- Agent lazily `dotnet tool install`s pinned `dotnet-trace 9.0.661903`
on first CLI-mode use, asserts the reported version, caches the path.
- `StartDotNetTrace` dispatches on the mode. `collect` / `collect-linux`
shell out via a new `CollectViaDotNetTraceCliAsync` that:
* normalizes `DotNetTraceProviders` through crank's keyword table
(rewrites `cpu-sampling`\u2192`dotnet-sampled-thread-time` for
`collect` mode -- required, since modern `cpu-sampling` is
collect-linux-exclusive; cosmetic alias rewrites for `fusion`,
`gcheapcollect`, the `...allcation...` typos);
* groups all rewrites into one `[INF]` line per job;
* spawns the CLI directly (not via `ProcessUtil.RunAsync`) so we keep
lifecycle control, signals SIGINT on stop, falls back to
`Kill(entireProcessTree:true)` on grace expiry;
* logs `[WRN]` on grace expiry but keeps the partial trace.
- `collect-linux` preflight is a HARD FAIL (no silent fallback):
Linux + `geteuid()==0` + kernel >= 6.4. Uses the verbatim error
text from the plan so it's greppable.
Why:
- LTTng-based `Collect=true` is structurally fragile on modern Ubuntu
(empty managed event sequences on 24.04). `collect-linux` is the
perf_event-based replacement that doesn't depend on LTTng.
- Existing `DotNetTrace=true` users keep their exact current behavior;
`collect-linux` is purely opt-in.
Notes:
- Mixed-version compat: all new Job properties default to today's
behavior. New controller + old agent silently uses `default` mode.
New agent + old controller is byte-for-byte identical to today.
- `collect-linux` rejects `--buffersize` (perf_event ring buffers, not
EventPipe); the builder strips it in that mode regardless of the knob.
- Stop timeout is per-mode by design: `collect-linux`'s rundown +
native symbolication is genuinely heavier than `collect`'s.
Tested:
- `dotnet build Microsoft.Crank.sln -c Release` clean.
- `dotnet test test/Microsoft.Crank.UnitTests` 100/100 passing
(33 new across BuildDotnetTraceCliArgs / NormalizeClrEventExpression
/ ParseKernelVersion / mixed-version-compat).
- Manual collect-linux validation on cobalt aarch64 Ubuntu 24.04 pending
reviewer sign-off.
Stacked on loopedbard3/modernize-dotnet-trace (PR A).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, hard-fail on version mismatch Three Copilot review comments on PR #3 surfaced real backcompat / soundness issues in BuildDotnetTraceCliArgs and EnsureDotnetTraceCliInstalledAsync. All three are fixed here with regression tests. 1. Tokenizer split on ',' only; legacy Collect() splits on ',' and ' '. Users with `gc-collect gc+jit`-style configs would tokenize differently under the CLI modes. Fixed by adding ' ' to the split delimiters. 2. Token classifier routed any non-':', non-profile token to --clrevents. A bare provider name like Microsoft-DotNETCore-SampleProfiler (the example shown in the README) is neither, so it would hit --clrevents and be rejected by the CLI. Fixed by adding TraceExtensions.IsRecognizedClrKeywordExpression: classify as --clrevents only when every '+'-part is a known CLR keyword; otherwise fall through to --providers, which accepts bare provider names. 3. dotnet-trace --version mismatch with the pinned DotnetTraceVersion was warn-and-proceed. Defeats the assert's purpose: a previous run's wrong-version binary in toolPath silently shadows the pin. Promoted to hard fail (with _dotnetTracePath reset so a manual cleanup recovers), matching the 'no silent fallback' theme established for the collect-linux preflight. Tests: +3 regression tests (BuildArgs_BareProviderName_GoesToProvidersFlag, BuildArgs_PlusJoinedWithUnknownPart_RoutesToProviders, BuildArgs_SpaceSeparatedTokens_AreTokenizedSeparately). Suite: 103 / 103 passing (was 100 / 100). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Position crash When dotnet-trace collect-linux is spawned by crank as a child process with stdout/stderr redirected to .NET pipes, its progress LineRewriter calls Console.SetCursorPosition(0, -1) and crashes with ArgumentOutOfRangeException. Console.CursorTop returns -1 in a non-TTY context; the 9.0.661903 build we previously pinned doesn't guard against that. Two upstream fixes addressing this are present in 10.0.721401 (tag v10.0.721401 on dotnet/diagnostics release/stable, commit 3f576a7): - dotnet/diagnostics#5771 'Fix no-tty crash with console capability aware ProgressWriter' (commit 767b7c0e) - dotnet/diagnostics#5745 'Handle redirected input/output in collect-linux' (commit 1f02e6f6) 10.x is not on nuget.org -- nuget.org currently tops out at 9.0.661903. Resolve the tool from the dnceng public dotnet-tools feed by writing a hermetic NuGet.config alongside the tool path and passing --configfile to dotnet tool install. A bare --add-source conflicts with package-source-mapping when the host has it configured, so --configfile is the portable form. The 10.x package layout is tools/net8.0/any/ (managed-IL only), so linux-arm64 on the cobalt-hosted aarch64 box is satisfied by the 'any' RID -- no native bits needed. Also redirect stdin in the child ProcessStartInfo as defense in depth: with IsInputRedirected true, the rewriter skips its ANSI-capability probe entirely (the path that produces the -1 from Console.CursorTop). Stop is signaled with SIGINT on Linux so we never write to stdin. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ect-linux dotnet-trace collect-linux reads tracefs to translate perf_event_open ring-buffer records into symbolicated frames. In a container the /sys/kernel/tracing directory exists but is empty unless the host's tracefs is bind-mounted, surfacing as 'Tracefs is not accessible: It appears tracefs is not mounted.' at runtime. Add -v /sys/kernel/tracing:/sys/kernel/tracing to the agent's docker run line. EventPipe-based collection (collect mode, or Collect=true perfcollect) does not need this mount; only collect-linux does. Host must have tracefs mounted -- Ubuntu 24.04 + systemd does this automatically at boot. Validated on the cobalt-hosted aarch64 Ubuntu 24.04 (kernel 6.8) host. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removes the 3-row matrix that PR A's 2-row version was expanded into when collect-linux mode was added. Same reasoning as the matching removal on PR A's branch: keep the controller help text a flat flag reference and rehome the decision guidance to a dedicated docs page later, once the trace collection surface (collect-linux preview status, perfcollect behavior on modern distros) stabilizes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
LoopedBard3
force-pushed
the
loopedbard3/dotnet-trace-collect-linux
branch
from
June 9, 2026 20:15
401fcbd to
0b06a0b
Compare
The pinned dotnet-trace version (10.0.721401) only lives on the dnceng public dotnet-tools feed; nuget.org tops out at 9.0.661903 and cannot supply it. Listing nuget.org here is dead weight at best and a footgun at worst (an org-scoped 9.x mirror could shadow the intended 10.x). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Startup.cs: align EnsureDotnetTraceCliInstalledAsync comment with code (throws, not warns, on version mismatch). - DotNetTraceCollectLinuxPreflightTests: drop dead Build==-1 branch; ParseKernelVersion always returns a 3-part Version. - DotNetTraceCliNormalizationTests: rename LeavesUnknownKeywordsAlone -> LeavesNonAliasedKeywordExpressionAlone for clarity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses PR dotnet#887 review feedback: move DotNetTraceCollectMode validation (and the collect-linux host prerequisite check) out of StartDotNetTrace -- which runs only after the app has launched -- and into the JobState.New acceptance path, so a bad mode or unsupported host fails before any asset restore, clone/build, or app launch. - New ValidateDotNetTraceOptions(job) centralizes the mode allow-list and delegates collect-linux to ValidateCollectLinuxPrerequisites. - StartDotNetTrace's collect-linux arm no longer re-runs the preflight; the default arm is now a defensive-only guard. - 12 new unit tests cover the gate, mode allow-list, unknown-mode rejection, profile-type gating, and collect-linux delegation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses PR dotnet#887 review: the SemaphoreSlim only serialized concurrent installs within a single agent process and could not coordinate across separate crank agent processes. The SDK install path (EnsureDotnetInstallExistsAsync) uses no such lock, so this was inconsistent. Rely on the File.Exists idempotency short-circuit instead, matching the established pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses PR dotnet#887 review: replace the IndexOfAny delimiter list in ParseKernelVersion with an anchored regex (^(\d+)\.(\d+)(?:\.(\d+))?). The regex captures the leading major.minor[.patch] and naturally stops at any suffix delimiter, so enumerating '-', '+', ' ', '~' is no longer needed. Added test cases for ~ and space suffixes plus bare major.minor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
LoopedBard3
added a commit
that referenced
this pull request
Aug 17, 2026
…, RID unify Fixes three Copilot review comments on the buildcache channel: - #1 (build reuse): a framework-dependent reused build previously returned early before any BCS resolve, so it ran the feed runtime instead of the resolved BCS bits. Introduce SHA-keyed persistent dotnet homes under a shared LRU root and RefreshBuildCacheForReuseAsync on the reuse path: re-resolve current shas (latest advances, pinned is idempotent), attach the persistent home on hit or re-materialize on drift/eviction (FDD), re-overlay only drifted side(s) for self-contained, re-stamp the two +buildcache.{sha} version surfaces, and fail loud on refresh failure. - #2 (overlay validation): OverlayRuntimeIntoHome now reports per-category counts (managed assemblies, native libraries, host binaries) and throws unless both managed and native files were copied. - #3 (RID unification): GetPlatformMoniker collapses Windows X86 to win-x64. Adds unit tests for the persistent-home key/cache-hit/drift behavior. Full solution builds clean; unit suite 99/99. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f12e0666-adff-4aaa-b059-821e8384151d
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.
What
Adds a
collect-linuxmode to crank'sdotnet-traceintegration so Linux agents on modern kernels can capture whole-machine, perf_event-based traces with kernel + native frames, without LTTng.New
Jobproperties (defaults reproduce today's exact behavior):DotNetTraceCollectMode(default|collect|collect-linux) —defaultkeeps today's in-processDiagnosticsClient.StartEventPipeSessionpath;collectandcollect-linuxshell out to thedotnet-traceCLI.DotNetTraceStopTimeoutSec— grace period after SIGINT before the CLI is killed.0selects the per-mode default (60s forcollect, 180s forcollect-linux).Agent additions in
Startup.cs:dotnet-trace 9.0.661903(latest on nuget.org withcollect-linux; ships alinux-arm64build).dotnet tool install --tool-pathon first CLI-mode use, with a post-install--versionassert and a single-flight semaphore.CollectViaDotNetTraceCliAsyncspawns the CLI directly (not viaProcessUtil.RunAsync) so we keep lifecycle control: wait for the benchmark to signal stop → send SIGINT → wait per-mode grace → fall back toKill(entireProcessTree:true)on expiry.[WRN] dotnet-trace did not finalize within {N}s; trace may be incompleteand keeps the partial trace — does not fail the job.Provider-string compatibility:
BuildDotnetTraceCliArgsclassifies each token into profile / provider-spec / CLR-keyword-expression using crank's existingTraceExtensionstable.cpu-sampling→dotnet-sampled-thread-timeincollectmode only (the moderncpu-samplingprofile hasVerbExclusivity="collect-linux"and is rejected by thecollectverb).fusion→assemblyloader,gcheapcollect→managedheapcollect, plus the twogcsampledobjectallcation{high,low}typos.[INF] dotnet-trace providers normalized: …line per job.cpu-samplingforcollect-linuxanddotnet-sampled-thread-time,dotnet-commonforcollect. Empty-providers default fordefaultmode is unchanged (stillcpu-samplingvia PR A's preserved behavior).collect-linuxpreflight (hard fail — no silent fallback tocollect, per design):/proc/sys/kernel/osrelease), effective UID == 0 (viaMono.Unix.Native.Syscall.geteuid()).DotNetTraceCollectMode=collect-linux requires Linux, root (effective UID 0), and kernel >= 6.4. Detected: OS={os}, EUID={euid}, kernel={ver}. Set DotNetTraceCollectMode=collect to use EventPipe collection instead.Docs:
Documentation.cs+README.md.DotNetTrace=true(any OS),DotNetTrace=true+DotNetTraceCollectMode=collect-linux(modern Linux + root),Collect=true(legacy PerfView pipelines).Why
LTTng-based
Collect=trueis structurally fragile on modern Ubuntu — cobalt-hosted Ubuntu 24.04 aarch64 returns empty managed-event sequences even withlttng-tools+liblttng-ust-devinstalled correctly (likely runtime↔liblttng-ust ABI mismatch).dotnet-trace collect-linuxis the perf_event-based replacement: same kernel-sampling capability as perfcollect provided, but without the LTTng dependency. Cobalt hosts (kernel 6.8, agent container runs as root) meet every prerequisite.Existing
--application.dotNetTrace trueusers keep their exact current behavior.collect-linuxis purely opt-in via the new mode flag.Notes
collect-linuxrejects--buffersize(TreatUnmatchedTokensAsErrors=truein upstream'sCollectLinuxCommand; perf_event uses kernel ring buffers, not EventPipe circular buffers). The argv builder strips it in that mode regardless ofDotNetTraceBufferSizeMB.collect-linuxrequires the traced process to be .NET 10+. If a user points it at a .NET 8/9 process, the CLI's stderr ("EventPipe IPC command not understood") is captured verbatim intojob.Error.Mono.Unix.UnixUserInfo.RealUserIdwould return the pre-sudo UID under elevation;geteuid()is the signal that matters forperf_event_opencapability checks. (Rubber-duck blind-spot fix from the planning session.)collectmode is best-effort.GenerateConsoleCtrlEventdoesn't reliably deliver Ctrl+C to non-console children spawned withUseShellExecute=false; the helper falls back toCloseMainWindow()+ the grace-period kill. Windows users almost always wantdefaultmode anyway.Mixed-version compatibility
DotNetTraceCollectMode/DotNetTraceStopTimeoutSec. If the controller asks forcollect-linux, the old agent silently runsdefault. Documented expectation.defaultmode,0-as-per-mode-default timeout). Byte-for-byte identical to today.Validated by
JobMixedVersionCompatTestscovering legacy and PR-A-era payloads.Tested
dotnet build Microsoft.Crank.sln -c Release→ clean (0 errors, 0 warnings).dotnet test test/Microsoft.Crank.UnitTests→ 100 / 100 passing, including 33 new tests across:DotNetTraceCliNormalizationTests(BuildDotnetTraceCliArgs / NormalizeClrEventExpression — defaults per mode, cpu-sampling rewrite, classification, alias rewrites, buffer-size gating)DotNetTraceCollectLinuxPreflightTests(ParseKernelVersion across the 6.4 boundary, error-message contract on non-Linux)JobMixedVersionCompatTests(PR B knob defaults + round-trip)collect-linuxvalidation on cobalt aarch64 Ubuntu 24.04 host — pending reviewer. Manual-validation checklist before un-drafting:dotnet tool install dotnet-trace --version 9.0.661903 --tool-path /tmp/xresolves an arm64 tool on the cobalt host (no x64-only fallback).--application.dotNetTrace true --application.dotNetTraceCollectMode collect-linuxproduces a non-empty.nettracewith both managed and native frames.collect).Operator notes
dotnet-trace collect-linux reads tracefs to translate
perf_event_openrecords. In a container the/sys/kernel/tracingdirectory exists but is empty unless the host's tracefs is bind-mounted, surfacing asError: Tracefs is not accessible: It appears tracefs is not mounted.at runtime. This PR adds-v /sys/kernel/tracing:/sys/kernel/tracingtodocker/agent/run.shso the wrapper script handles this for new agent deployments. EventPipe-based collection (DotNetTraceCollectMode=collectorCollect=trueperfcollect) does not need this mount; onlycollect-linuxdoes. The host must also have tracefs mounted (Ubuntu 24.04 + systemd does this at boot; check withmount | grep tracefs). Operators upgrading an existing agent need to./docker/agent/stop.sh && ./docker/agent/run.shto pick up the new mount -- volume mounts cannot be added to a running container. Validated on the cobalt-hosted aarch64 Ubuntu 24.04 (kernel 6.8) host.The pinned
dotnet-traceCLI version is 10.0.721401 (tagv10.0.721401ondotnet/diagnostics:release/stable, commit3f576a7). This is the latest stable release that contains both upstream fixes for the no-ttySetCursorPosition(0, -1)crash that occurs whencollect-linuxis spawned with redirected stdout:dotnet/diagnostics#5771("Fix no-tty crash with console capability aware ProgressWriter") anddotnet/diagnostics#5745("Handle redirected input/output in collect-linux"). 10.x is not on nuget.org; the agent writes a hermeticNuGet.confignext to the tool path and resolves from the dnceng publicdotnet-toolsfeed via--configfile.Stacking
This PR is stacked on
loopedbard3/modernize-dotnet-trace(PR A, #2). Targeting PR A's branch as the merge base so the diff shows only PR B's content.