Skip to content

Commit f540c7d

Browse files
committed
Add profiling examples and README snippets
1 parent ddf89b6 commit f540c7d

File tree

14 files changed

+352
-16
lines changed

14 files changed

+352
-16
lines changed

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,104 @@ Outputs are written to `profile-output/` in the current working directory.
166166
- `--input <path>` render existing `nettrace`, `speedscope.json`, `etlx`, or `gcdump` files
167167
- `--tfm <tfm>` target framework when profiling a `.csproj` or `.sln`
168168

169+
## Profiler flags
170+
171+
### --cpu
172+
Sampled CPU profiling with a call tree and top‑function table.
173+
Use this to find the hottest methods by time.
174+
175+
Example (examples/cpu):
176+
```bash
177+
dotnet build -c Release examples/cpu/CpuDemo.csproj
178+
asynkron-profiler --cpu -- ./examples/cpu/bin/Release/net10.0/CpuDemo
179+
```
180+
181+
Sample output (top of call tree):
182+
```
183+
Call Tree (Total Time)
184+
327.41 ms 100.0% 1x Total
185+
└─ 299.66 ms 91.5% 1x Program.Main
186+
└─ 298.22 ms 91.1% 1x Program.RunWork
187+
└─ 281.02 ms 85.8% 1x Program.CpuWork
188+
└─ 276.10 ms 84.3% 3x Program.Fib
189+
```
190+
191+
### --memory
192+
Allocation profiling using GC allocation tick events, plus a per‑type call tree.
193+
Use this to find the biggest allocation sources.
194+
195+
Example (examples/memory):
196+
```bash
197+
dotnet build -c Release examples/memory/MemoryDemo.csproj
198+
asynkron-profiler --memory -- ./examples/memory/bin/Release/net10.0/MemoryDemo
199+
```
200+
201+
Sample output (top of call tree):
202+
```
203+
Allocation Call Tree (Sampled)
204+
Byte[] (20.06 MB, 69.4%, 198x)
205+
20.06 MB 100.0% 198x Byte[]
206+
└─ 19.95 MB 99.5% 197x Program.Main lambda
207+
```
208+
209+
### --exception
210+
Exception profiling with thrown counts, throw‑site call tree, and optional catch‑site table/tree.
211+
Use `--exception-type` to focus on a specific exception.
212+
213+
Example (examples/exception):
214+
```bash
215+
dotnet build -c Release examples/exception/ExceptionDemo.csproj
216+
asynkron-profiler --exception --exception-type "InvalidOperation" -- ./examples/exception/bin/Release/net10.0/ExceptionDemo
217+
```
218+
219+
Sample output (top of call tree):
220+
```
221+
Call Tree (Thrown Exceptions)
222+
6,667x 100.0% InvalidOperationException
223+
└─ 6,667x 100.0% Program.Main lambda
224+
└─ 6,667x 100.0% EH.DispatchEx
225+
```
226+
227+
### --contention
228+
Lock contention profiling with wait‑time call tree and top contended methods.
229+
Use this to find where threads are blocking on locks.
230+
231+
Example (examples/contention):
232+
```bash
233+
dotnet build -c Release examples/contention/ContentionDemo.csproj
234+
asynkron-profiler --contention -- ./examples/contention/bin/Release/net10.0/ContentionDemo
235+
```
236+
237+
Sample output (top of call tree):
238+
```
239+
Call Tree (Wait Time)
240+
49563.89 ms 100.0% 96x Total
241+
└─ 49563.89 ms 100.0% 96x Program.RunWorkers lambda
242+
└─ 49563.89 ms 100.0% 96x Program.WorkWithLock
243+
```
244+
245+
### --heap
246+
Heap snapshot using `dotnet-gcdump`, with a summary of top types.
247+
Use this to inspect retained memory after the run.
248+
249+
Example (examples/heap):
250+
```bash
251+
dotnet build -c Release examples/heap/HeapDemo.csproj
252+
asynkron-profiler --heap -- ./examples/heap/bin/Release/net8.0/HeapDemo
253+
```
254+
255+
Sample output (top types):
256+
```
257+
HEAP SNAPSHOT: HeapDemo
258+
14,595,494 GC Heap bytes
259+
50,375 GC Heap objects
260+
261+
Object Bytes Count Type
262+
524,312 1 System.Byte[][] (Bytes > 100K)
263+
30,168 1 System.String (Bytes > 10K)
264+
24 50,003 System.Byte[]
265+
```
266+
169267
## Troubleshooting
170268

171269
- `dotnet-trace` not found: install with `dotnet tool install -g dotnet-trace` and ensure your global tool path is on `PATH`.

examples/contention/Program.cs

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,54 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.Runtime.CompilerServices;
45
using System.Threading;
56
using System.Threading.Tasks;
67

7-
const int Seconds = 3;
8-
var workers = Math.Max(4, Environment.ProcessorCount * 2);
9-
var gate = new object();
10-
var start = new ManualResetEventSlim(false);
11-
var tasks = new List<Task>(workers);
12-
13-
for (var i = 0; i < workers; i++)
8+
internal static class Program
149
{
15-
tasks.Add(Task.Run(() =>
10+
private const int Seconds = 3;
11+
12+
[MethodImpl(MethodImplOptions.NoInlining)]
13+
private static void WorkWithLock(object gate, TimeSpan duration)
1614
{
17-
start.Wait();
1815
var sw = Stopwatch.StartNew();
19-
while (sw.Elapsed < TimeSpan.FromSeconds(Seconds))
16+
while (sw.Elapsed < duration)
2017
{
2118
lock (gate)
2219
{
2320
Thread.Sleep(2);
2421
}
2522
}
26-
}));
27-
}
23+
}
24+
25+
[MethodImpl(MethodImplOptions.NoInlining)]
26+
private static void RunWorkers(int workers, int seconds)
27+
{
28+
var gate = new object();
29+
var start = new ManualResetEventSlim(false);
30+
var tasks = new List<Task>(workers);
31+
var duration = TimeSpan.FromSeconds(seconds);
32+
33+
for (var i = 0; i < workers; i++)
34+
{
35+
tasks.Add(Task.Run(() =>
36+
{
37+
start.Wait();
38+
WorkWithLock(gate, duration);
39+
}));
40+
}
2841

29-
start.Set();
30-
Task.WaitAll(tasks.ToArray());
42+
start.Set();
43+
Task.WaitAll(tasks.ToArray());
44+
}
3145

32-
Console.WriteLine($"Completed {workers} workers with contention.");
33-
Console.WriteLine($"Lock contention count: {Monitor.LockContentionCount}");
46+
public static void Main()
47+
{
48+
var workerCount = Math.Max(4, Environment.ProcessorCount * 2);
49+
RunWorkers(workerCount, Seconds);
50+
51+
Console.WriteLine($"Completed {workerCount} workers with contention.");
52+
Console.WriteLine($"Lock contention count: {Monitor.LockContentionCount}");
53+
}
54+
}

examples/cpu/CpuDemo.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
</Project>

examples/cpu/Program.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
4+
internal static class Program
5+
{
6+
[MethodImpl(MethodImplOptions.NoInlining)]
7+
private static int Fib(int n)
8+
{
9+
var a = 0;
10+
var b = 1;
11+
for (var i = 0; i < n; i++)
12+
{
13+
var tmp = a + b;
14+
a = b;
15+
b = tmp;
16+
}
17+
18+
return a;
19+
}
20+
21+
[MethodImpl(MethodImplOptions.NoInlining)]
22+
private static long CpuWork(int iterations)
23+
{
24+
long sum = 0;
25+
for (var i = 0; i < iterations; i++)
26+
{
27+
var n = (i % 30) + 1;
28+
sum += Fib(n);
29+
}
30+
31+
return sum;
32+
}
33+
34+
[MethodImpl(MethodImplOptions.NoInlining)]
35+
private static void RunWork()
36+
{
37+
var total = CpuWork(1_000_000);
38+
Console.WriteLine(total.ToString());
39+
}
40+
41+
public static void Main()
42+
{
43+
RunWork();
44+
}
45+
}

examples/cpu/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# CPU demo
2+
3+
Build:
4+
5+
```bash
6+
dotnet build -c Release
7+
```
8+
9+
Profile:
10+
11+
```bash
12+
asynkron-profiler --cpu -- ./bin/Release/net10.0/CpuDemo
13+
```
14+
15+
Or (framework-dependent):
16+
17+
```bash
18+
asynkron-profiler --cpu -- dotnet ./bin/Release/net10.0/CpuDemo.dll
19+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
</Project>

examples/exception/Program.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
3+
var handled = 0;
4+
5+
for (var i = 0; i < 20_000; i++)
6+
{
7+
try
8+
{
9+
if (i % 3 == 0)
10+
{
11+
throw new InvalidOperationException("boom");
12+
}
13+
14+
if (i % 7 == 0)
15+
{
16+
throw new ArgumentException("bad");
17+
}
18+
}
19+
catch (InvalidOperationException)
20+
{
21+
handled += 1;
22+
}
23+
catch (ArgumentException)
24+
{
25+
handled += 2;
26+
}
27+
}
28+
29+
Console.WriteLine(handled.ToString());

examples/exception/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Exception demo
2+
3+
Build:
4+
5+
```bash
6+
dotnet build -c Release
7+
```
8+
9+
Profile:
10+
11+
```bash
12+
asynkron-profiler --exception -- ./bin/Release/net10.0/ExceptionDemo
13+
```
14+
15+
Filter to a specific exception type:
16+
17+
```bash
18+
asynkron-profiler --exception --exception-type "InvalidOperation" -- ./bin/Release/net10.0/ExceptionDemo
19+
```

examples/heap/HeapDemo.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
</Project>

examples/heap/Program.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
5+
var payloads = new List<byte[]>();
6+
7+
for (var i = 0; i < 50_000; i++)
8+
{
9+
payloads.Add(new byte[256]);
10+
}
11+
12+
Console.WriteLine(payloads.Count.ToString());
13+
Thread.Sleep(15000);
14+
GC.KeepAlive(payloads);

0 commit comments

Comments
 (0)