Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions core/diagnostics/PerformanceScenarios/CpuScenarios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

internal static class CpuScenarios
{
public static Task HotspotAsync(CancellationToken token)
{
long result = 0;
while (!token.IsCancellationRequested)
{
result += Fibonacci(36);
}

GC.KeepAlive(result);
return Task.CompletedTask;
}

private static long Fibonacci(int value)
{
return value <= 1 ? value : Fibonacci(value - 1) + Fibonacci(value - 2);
}
}
21 changes: 21 additions & 0 deletions core/diagnostics/PerformanceScenarios/MemoryScenarios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

internal static class MemoryScenarios
{
public static Task LohGcAsync(CancellationToken token)
{
List<byte[]> retained = [];
while (!token.IsCancellationRequested)
{
retained.Add(new byte[200_000]);
if (retained.Count > 200)
{
retained.RemoveRange(0, 100);
}
}

GC.KeepAlive(retained);
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
51 changes: 51 additions & 0 deletions core/diagnostics/PerformanceScenarios/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

Dictionary<string, Scenario> scenarios = new(StringComparer.OrdinalIgnoreCase)
{
["cpu-hotspot"] = new("One CPU core remains saturated.", CpuScenarios.HotspotAsync),
["loh-gc"] = new("Full collections occur despite a modest object count.", MemoryScenarios.LohGcAsync),
};

if (args.Length == 0 || args[0] is "--list" or "-l")
{
Console.WriteLine("Usage: dotnet run -- <scenario> [duration-seconds]");
Console.WriteLine();
foreach ((string name, Scenario scenario) in scenarios)
{
Console.WriteLine($" {name,-24} {scenario.Symptom}");
}

return;
}

if (!scenarios.TryGetValue(args[0], out Scenario? selected))
{
Console.Error.WriteLine($"Unknown scenario '{args[0]}'. Run with --list to see the available scenarios.");
Environment.ExitCode = 1;
return;
}

int durationSeconds = 30;
if (args.Length > 1 && (!int.TryParse(args[1], out durationSeconds) || durationSeconds <= 0))
{
Console.Error.WriteLine($"Invalid duration '{args[1]}'. Specify a positive whole number of seconds.");
Environment.ExitCode = 1;
return;
}

using CancellationTokenSource cancellation = new(TimeSpan.FromSeconds(durationSeconds));

Console.WriteLine($"Process ID: {Environment.ProcessId}");
Console.WriteLine($"Symptom: {selected.Symptom}");
Console.WriteLine($"Duration: {durationSeconds} seconds");

try
{
await selected.RunAsync(cancellation.Token);
}
catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
{
}

internal sealed record Scenario(string Symptom, Func<CancellationToken, Task> RunAsync);
45 changes: 45 additions & 0 deletions core/diagnostics/PerformanceScenarios/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
languages:
- csharp
products:
- dotnet
page_type: sample
name: "dotnet-trace collect-linux performance scenarios"
urlFragment: "dotnet-trace-collect-linux-performance-scenarios"
description: "Two .NET performance scenarios for the dotnet-trace collect-linux tutorial: a CPU hotspot and large object heap pressure."
ai-usage: ai-assisted
---
# `dotnet-trace collect-linux` performance scenarios

This console application creates two scenarios: a managed CPU hotspot and large object heap (LOH) allocation pressure. Use it with the [`dotnet-trace collect-linux` performance investigation tutorial](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace-collect-linux-performance) to collect trace data and reach a conclusion from the evidence.

## Download the source

Select **Browse code** at the top of this page to open the repository, or clone the [dotnet/samples](https://github.com/dotnet/samples) repository and navigate to `core/diagnostics/PerformanceScenarios`.

## Build and list the scenarios

The sample requires the .NET 10 SDK:

```dotnetcli
dotnet build -c Release
dotnet run -c Release --no-build -- --list
```

## Run a scenario

Pass the scenario name and an optional duration in seconds. Run the scenarios one at a time:

```dotnetcli
dotnet run -c Release --no-build -- cpu-hotspot 45
dotnet run -c Release --no-build -- loh-gc 45
```

The application prints its process ID, the symptom to investigate, and the configured duration. The tutorial shows how to collect machine-wide traces and analyze them in Visual Studio, with PerfView as an alternative.

## Scenario matrix

| Scenario | Distinct investigation question | Differentiating evidence or next step |
| --- | --- | --- |
| `cpu-hotspot` | Which managed method directly consumes the CPU? | Exclusive CPU samples identify one expensive application method and its callers. |
| `loh-gc` | Are large objects causing full collections despite a modest object count? | Large `System.Byte[]` allocations, large object heap growth, and generation 2 collections distinguish large-object pressure from small-object churn. |
Loading