Skip to content

Add collect-linux mode to dotnet-trace integration - #3

Draft
LoopedBard3 wants to merge 13 commits into
mainfrom
loopedbard3/dotnet-trace-collect-linux
Draft

LoopedBard3 wants to merge 13 commits into
mainfrom
loopedbard3/dotnet-trace-collect-linux

Conversation

@LoopedBard3

@LoopedBard3 LoopedBard3 commented Jun 3, 2026 •

Copy link
Copy Markdown
Owner

This PR targets LoopedBard3/crank:main (the fork) for pre-review. It will be re-opened against dotnet/crank after fork-side review sign-off.

Stacking note: PR B is conceptually stacked on PR A (#2). Both PRs target the fork's main, so this PR's diff includes PR A's commit (95d8e81) in addition to PR B's own commit (f0f1604). Review focus: the second commit -- the first commit is identical to PR A.

What

Adds a collect-linux mode to crank's dotnet-trace integration so Linux agents on modern kernels can capture whole-machine, perf_event-based traces with kernel + native frames, without LTTng.

New Job properties (defaults reproduce today's exact behavior):

  • DotNetTraceCollectMode (default | collect | collect-linux) — default keeps today's in-process DiagnosticsClient.StartEventPipeSession path; collect and collect-linux shell out to the dotnet-trace CLI.
  • DotNetTraceStopTimeoutSec — grace period after SIGINT before the CLI is killed. 0 selects the per-mode default (60s for collect, 180s for collect-linux).

Agent additions in Startup.cs:

  • Pinned dotnet-trace 9.0.661903 (latest on nuget.org with collect-linux; ships a linux-arm64 build).
  • Lazy dotnet tool install --tool-path on first CLI-mode use, with a post-install --version assert and a single-flight semaphore.
  • CollectViaDotNetTraceCliAsync spawns the CLI directly (not via ProcessUtil.RunAsync) so we keep lifecycle control: wait for the benchmark to signal stop → send SIGINT → wait per-mode grace → fall back to Kill(entireProcessTree:true) on expiry.
  • Grace-period expiry logs [WRN] dotnet-trace did not finalize within {N}s; trace may be incomplete and keeps the partial trace — does not fail the job.

Provider-string compatibility:

  • BuildDotnetTraceCliArgs classifies each token into profile / provider-spec / CLR-keyword-expression using crank's existing TraceExtensions table.
  • Required rewrite: cpu-sampling → dotnet-sampled-thread-time in collect mode only (the modern cpu-sampling profile has VerbExclusivity="collect-linux" and is rejected by the collect verb).
  • Cosmetic alias rewrites: fusion → assemblyloader, gcheapcollect → managedheapcollect, plus the two gcsampledobjectallcation{high,low} typos.
  • All rewrites are grouped into one [INF] dotnet-trace providers normalized: … line per job.
  • Empty-providers default is cpu-sampling for collect-linux and dotnet-sampled-thread-time,dotnet-common for collect. Empty-providers default for default mode is unchanged (still cpu-sampling via PR A's preserved behavior).

collect-linux preflight (hard fail — no silent fallback to collect, per design):

  • OS == Linux, kernel ≥ 6.4 (parsed from /proc/sys/kernel/osrelease), effective UID == 0 (via Mono.Unix.Native.Syscall.geteuid()).
  • Verbatim error text:
    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:

  • New flags documented in Documentation.cs + README.md.
  • Decision matrix expanded from 2 rows (PR A) → 3 rows: DotNetTrace=true (any OS), DotNetTrace=true + DotNetTraceCollectMode=collect-linux (modern Linux + root), Collect=true (legacy PerfView pipelines).

Why

LTTng-based Collect=true is structurally fragile on modern Ubuntu — cobalt-hosted Ubuntu 24.04 aarch64 returns empty managed-event sequences even with lttng-tools + liblttng-ust-dev installed correctly (likely runtime↔liblttng-ust ABI mismatch). dotnet-trace collect-linux is 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 true users keep their exact current behavior. collect-linux is purely opt-in via the new mode flag.

Notes

  • collect-linux rejects --buffersize (TreatUnmatchedTokensAsErrors=true in upstream's CollectLinuxCommand; perf_event uses kernel ring buffers, not EventPipe circular buffers). The argv builder strips it in that mode regardless of DotNetTraceBufferSizeMB.
  • Pinned tool version targets net8.0 (the tool's own TFM). collect-linux requires 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 into job.Error.
  • Effective UID, not real UID. Mono.Unix.UnixUserInfo.RealUserId would return the pre-sudo UID under elevation; geteuid() is the signal that matters for perf_event_open capability checks. (Rubber-duck blind-spot fix from the planning session.)
  • Windows collect mode is best-effort. GenerateConsoleCtrlEvent doesn't reliably deliver Ctrl+C to non-console children spawned with UseShellExecute=false; the helper falls back to CloseMainWindow() + the grace-period kill. Windows users almost always want default mode anyway.

Mixed-version compatibility

Direction Behavior
New controller + old agent Old agent's deserializer ignores DotNetTraceCollectMode / DotNetTraceStopTimeoutSec. If the controller asks for collect-linux, the old agent silently runs default. Documented expectation.
New agent + old controller New properties default to today's exact behavior (default mode, 0-as-per-mode-default timeout). Byte-for-byte identical to today.

Validated by JobMixedVersionCompatTests covering 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)
    • Extended JobMixedVersionCompatTests (PR B knob defaults + round-trip)
  • ❌ Manual collect-linux validation 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/x resolves an arm64 tool on the cobalt host (no x64-only fallback).
    • A simple .NET 10 benchmark with --application.dotNetTrace true --application.dotNetTraceCollectMode collect-linux produces a non-empty .nettrace with both managed and native frames.
    • Same invocation on a non-Linux/non-root environment fails fast with the documented preflight error string (no silent fallback to collect).

Operator notes

dotnet-trace collect-linux reads tracefs to translate perf_event_open records. In a container the /sys/kernel/tracing directory exists but is empty unless the host's tracefs is bind-mounted, surfacing as Error: Tracefs is not accessible: It appears tracefs is not mounted. at runtime. This PR adds -v /sys/kernel/tracing:/sys/kernel/tracing to docker/agent/run.sh so the wrapper script handles this for new agent deployments. EventPipe-based collection (DotNetTraceCollectMode=collect or Collect=true perfcollect) does not need this mount; only collect-linux does. The host must also have tracefs mounted (Ubuntu 24.04 + systemd does this at boot; check with mount | grep tracefs). Operators upgrading an existing agent need to ./docker/agent/stop.sh && ./docker/agent/run.sh to 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-trace CLI version is 10.0.721401 (tag v10.0.721401 on dotnet/diagnostics:release/stable, commit 3f576a7). This is the latest stable release that contains both upstream fixes for the no-tty SetCursorPosition(0, -1) crash that occurs when collect-linux is spawned with redirected stdout: dotnet/diagnostics#5771 ("Fix no-tty crash with console capability aware ProgressWriter") and dotnet/diagnostics#5745 ("Handle redirected input/output in collect-linux"). 10.x is not on nuget.org; the agent writes a hermetic NuGet.config next to the tool path and resolves from the dnceng public dotnet-tools feed 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.

@LoopedBard3
LoopedBard3 changed the base branch from loopedbard3/modernize-dotnet-trace to main June 3, 2026 20:29
@LoopedBard3
LoopedBard3 requested a review from Copilot June 3, 2026 20:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Job knobs 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.

Comment thread src/Microsoft.Crank.Agent/Startup.cs
Comment thread src/Microsoft.Crank.Agent/Startup.cs
Comment thread src/Microsoft.Crank.Agent/Startup.cs Outdated
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
LoopedBard3 force-pushed the loopedbard3/dotnet-trace-collect-linux branch from 4fc9593 to ec71a54 Compare June 8, 2026 20:08
DrewScoggins and others added 7 commits June 8, 2026 15:07
…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
LoopedBard3 force-pushed the loopedbard3/dotnet-trace-collect-linux branch from 401fcbd to 0b06a0b Compare June 9, 2026 20:15
Copilot AI added 5 commits June 9, 2026 13:27
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
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.

4 participants