From 5737c0fa09552647165a4eecaa104145fad41fe9 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 18 Nov 2024 14:27:15 +0100 Subject: [PATCH] Add basic documentation --- doc/readme.md | 129 ++++++++++++++++++++++- readme.md | 20 +++- src/Ultra.Core/Ultra.Core.csproj | 6 +- src/Ultra.Core/readme.md | 20 ++++ src/Ultra.Example/Program.cs | 84 +++++++-------- src/Ultra.Tests/FirefoxProfilerTests.cs | 5 +- src/Ultra/Properties/launchSettings.json | 9 ++ src/Ultra/Ultra.csproj | 4 +- src/ultra.sln | 5 + src/ultra.sln.DotSettings | 2 + 10 files changed, 229 insertions(+), 55 deletions(-) create mode 100644 src/Ultra.Core/readme.md create mode 100644 src/Ultra/Properties/launchSettings.json create mode 100644 src/ultra.sln.DotSettings diff --git a/doc/readme.md b/doc/readme.md index 733a4f4..4ffb33a 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -1,3 +1,130 @@ # Ultra User Guide -This is a default project description. +## Quick Start + +> ____ +> 🚨 The profiler **requires to run from an elevated prompt with administrative rights** 🚨 +> +> _This is required to allow to collect full stack traces, including kernel and native functions._ +> ____ + +Example: open a **terminal with administrative rights**, to profile an executable called `my_commands.exe`: + +```console +$ ultra.exe profile -- my_command.exe arg0 arg1 arg2... +``` + +This will create a `ultra_my_command_..._.json.gz` trace file in the current directory. + +## Ultra Profiler UI + +In order to visualize a trace produced by `ultra.exe` you need to go to https://profiler.firefox.com/ and open the generated `json.gz` trace file. + +### Timeline + +This screenshot shows a profile captured with ultra and is available online [here](https://share.firefox.dev/3Cya7YW) + +You have access to several capabilities: +- Zoom in a timeline +- When in the Flame Graph / Stack Chart, you can hold `SHIFT + MouseWheel` to zoom-in / zoom-out + +![Timeline](profile_example.png) + +### Flame Graph / Stack Chart + +The Flame Graph / Stack Chart is a visualization of the time spent in functions. The width of the boxes is proportional to the time spent in the function. The boxes are stacked to show the call hierarchy. + +![Flame Graph](profile_flame_graph.png) + +### Categories + +When selecting functions, you can visualize the split between the time in the different modules: + +- `.NET`: Managed code (user or BCL) +- `.NET JIT`: Functions participating in the JIT to compile methods +- `.NET GC`: Functions participating in the GC +- `.NET CLR`: Functions used by the CoreCLR runtime +- `Native`: Native functions +- `Kernel`: Kernel functions - displayed with hexadecimal address + +The colors are reflected in the Flame Graph / Stack Chart to easily spot the different usage. + +![Categories](profile_categories.png) + +While hovering the mouse over a function, you can see the time spent in the function and the categories: + +Here is an example of a function in the `JIT` category: + +![Category JIT)](profile_category_jit.png) + + +Here is an example of a function in the `GC` category: + +![Category GC](profile_category_gc.png) + +### Memory Track + +The memory track shows the managed memory usage of the process. You can see the memory usage over time. + +![Profile Memory](profile_memory.png) + +## GC Allocation Track + +The GC Allocation Track shows the allocation rate of the process. You can see the allocation rate over time. + +![Profile GC Allocation](profile_gc_alloc.png) + +### JIT and GC Markers + +The JIT and GC markers are displayed in the timeline. You can see the JIT and GC events in the timeline. + +You can also see them in the Marker Chart and Marker Table view. + +![Profile JIT and GC Markers](profile_markers.png) + +## Ultra Command Line + +ultra.exe command line options: + +```console +Usage: ultra [Options] command + + -h, -?, --help Show this message and exit + -v, --version Show the version of this command + --verbose Display verbose progress + +Available commands: + profile Profile a new process or attach to an existing process + convert Convert an existing ETL file to a Firefox Profiler json file +``` + +## Profile + +This is the main command to profile an application - Only working within an elevated prompt: + +```console +Usage: ultra profile [Options] + + -h, -?, --help Show this message and exit + --pid=PID The PID of the process to attach the profiler to. + --sampling-interval=VALUE The VALUE of the sample interval in ms. Default is 8190Hz = 0.122ms. + --symbol-path=VALUE The VALUE of symbol path. The default value is `;SRV*C:\Users\xoofx\AppData\Local\Temp\SymbolCache*https://msdl.microsoft.com/download/symbols;SRV*C:\Users\xoofx\AppData\Local\Temp\SymbolCache*https:// + symbols.nuget.org/download/symbols`. + --keep-merged-etl-file Keep the merged ETL file. + --keep-intermediate-etl-files Keep the intermediate ETL files before merging. +``` + +## Convert + +Convert an existing ETL file to a Firefox Profiler json file: + +It requires a list of PID in order to only produce results for these processes. + +```console +Usage: ultra convert --pid xxx + + -h, -?, --help Show this message and exit + --pid=PID The PID of the process + --symbol-path=VALUE The VALUE of symbol path. The default value is `;SRV*C:\Users\xoofx\AppData\Local\Temp\SymbolCache*https://msdl.microsoft.com/download/symbols;SRV*C:\Users\xoofx\AppData\Local\Temp\SymbolCache*https:// + symbols.nuget.org/download/symbols`. +``` diff --git a/readme.md b/readme.md index 0997ec5..05aaba3 100644 --- a/readme.md +++ b/readme.md @@ -9,6 +9,10 @@ Ultra is a an advanced profiler for .NET Applications available on Windows. - ETW based **sampling profiler** - up to 8190 samples/second - UI based on https://profiler.firefox.com/ - Traces shareable online: Check this example: ✨ https://share.firefox.dev/3Cya7YW ✨ + - **Timeline** visualization + - **Flamegraph / Stack Chart** visualization + - **Call Tree** visualization + - **Marker Chart** and **Marker Table** visualization - Precise **kernel**, **native** and **managed** **function call stacks** - Categorization of functions: - `.NET`, `.NET JIT`, `.NET GC`, `.NET CLR`, `Native`, `Kernel` @@ -34,12 +38,26 @@ You need to have installed a [.NET 8.0+ SDK](https://dotnet.microsoft.com/en-us/ $ dotnet tool install -g Ultra # The command ultra.exe will be available from your PATH ``` -Open a **terminal in administrative rights**, to profile the executable `my_commands.exe`: +> ____ +> 🚨 The profiler **requires to run from an elevated prompt with administrative rights** 🚨 +> +> _This is required to allow to collect full stack traces, including kernel and native functions._ +> ____ + +Example: open a **terminal with administrative rights**, to profile an executable called `my_commands.exe`: ```console $ ultra.exe profile -- my_command.exe arg0 arg1 arg2... ``` +> ⚠️ Notice the `--` separator to separate the arguments to `ultra.exe` from the arguments to the profiled application. + +Profiling a running application just requires the PID of the process to profile: + +```console +$ ultra.exe profile 15243 # PID of the process to profile +``` + ## 📖 User Guide For more details on how to use Ultra, please visit the [user guide](https://github.com/xoofx/Ultra/blob/main/doc/readme.md). diff --git a/src/Ultra.Core/Ultra.Core.csproj b/src/Ultra.Core/Ultra.Core.csproj index 005e41f..427dcbd 100644 --- a/src/Ultra.Core/Ultra.Core.csproj +++ b/src/Ultra.Core/Ultra.Core.csproj @@ -10,11 +10,11 @@ - This is a default project description + Ultra is a an advanced profiler for .NET Applications available on Windows. Alexandre Mutel en-US Alexandre Mutel - tag1;tag2;tag3 + profiler;etw readme.md ultra.png https://github.com/xoofx/ultra @@ -27,7 +27,7 @@ - + diff --git a/src/Ultra.Core/readme.md b/src/Ultra.Core/readme.md new file mode 100644 index 0000000..a07ba19 --- /dev/null +++ b/src/Ultra.Core/readme.md @@ -0,0 +1,20 @@ +# Ultra.Core + +Ultra is a an advanced profiler for .NET Applications available on Windows. + +Ultra.Core is the core library that contains the core functionalities of the Ultra profiler: + +- ETW sampling profiler via the class `EtwUltraProfiler` +- Converter to Firefox Profiler format via the class `EtwConverterToFirefox` + +## 📖 User Guide + +For more details on how to use Ultra, please visit the [user guide](https://github.com/xoofx/Ultra/blob/main/doc/readme.md). + +## 🪪 License + +This software is released under the [BSD-2-Clause license](https://opensource.org/licenses/BSD-2-Clause). + +## 🤗 Author + +Alexandre Mutel aka [xoofx](https://xoofx.github.io). diff --git a/src/Ultra.Example/Program.cs b/src/Ultra.Example/Program.cs index 7870119..0129a0c 100644 --- a/src/Ultra.Example/Program.cs +++ b/src/Ultra.Example/Program.cs @@ -1,58 +1,48 @@ -namespace Ultra.Example; +// Sample program using Markdig and Scriban to create a workload example for profiling with ultra -/// -/// Sample program using Markdig and Scriban to create a workload example for profiling with ultra -/// -internal class Program +const int countBenchMarkdig = 500; +const int countBenchScriban = 5000; +var md = await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "CommonMark.md")); + +var benchMarkdig = () => { - static async Task Main(string[] args) + for (int i = 0; i < countBenchMarkdig; i++) { - const int CountBenchMarkdig = 500; - const int CountBenchScriban = 5000; - - var md = await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "CommonMark.md")); - - var benchMarkdig = () => - { - for (int i = 0; i < CountBenchMarkdig; i++) - { - var html = Markdig.Markdown.ToHtml(md); - } - }; - - var benchScriban = () => - { - var template = Scriban.Template.Parse(""" - {{ for $i in values }} - [{{i}}] This is an example of a template with a loop - {{ end }} - """); + var html = Markdig.Markdown.ToHtml(md); + } +}; - var values = new List { "one", "two", "three" }; - for (int i = 0; i < CountBenchScriban; i++) - { - var text = template.Render(new { values = values }); - } - }; +var benchScriban = () => +{ + var template = Scriban.Template.Parse(""" + {{ for $i in values }} + [{{i}}] This is an example of a template with a loop + {{ end }} + """); + + var values = new List { "one", "two", "three" }; + for (int i = 0; i < countBenchScriban; i++) + { + var text = template.Render(new { values = values }); + } +}; - benchMarkdig(); +benchMarkdig(); - var tasks = new List(); +var tasks = new List(); - for (int i = 0; i < Math.Max(2, Environment.ProcessorCount / 4); i++) - { - var markdigTask = new Task(benchMarkdig); - var scribanTask = new Task(benchScriban); +for (int i = 0; i < Math.Max(2, Environment.ProcessorCount / 4); i++) +{ + var markdigTask = new Task(benchMarkdig); + var scribanTask = new Task(benchScriban); - markdigTask.Start(); - scribanTask.Start(); + markdigTask.Start(); + scribanTask.Start(); - tasks.Add(markdigTask); - tasks.Add(scribanTask); - } + tasks.Add(markdigTask); + tasks.Add(scribanTask); +} - benchScriban(); +benchScriban(); - await Task.WhenAll(tasks); - } -} \ No newline at end of file +await Task.WhenAll(tasks); \ No newline at end of file diff --git a/src/Ultra.Tests/FirefoxProfilerTests.cs b/src/Ultra.Tests/FirefoxProfilerTests.cs index ea94667..d7d6420 100644 --- a/src/Ultra.Tests/FirefoxProfilerTests.cs +++ b/src/Ultra.Tests/FirefoxProfilerTests.cs @@ -10,8 +10,11 @@ namespace Ultra.Tests; +/// +/// No real tests here yet, just to check the serialization of Firefox profiler format +/// [TestClass] -public class Class1Test +public class FirefoxProfilerTests { [TestMethod] public void TestMarker() diff --git a/src/Ultra/Properties/launchSettings.json b/src/Ultra/Properties/launchSettings.json new file mode 100644 index 0000000..30110ae --- /dev/null +++ b/src/Ultra/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Ultra": { + "commandName": "Project", + "commandLineArgs": "convert --pid 472972 .\\ultra_Ultra.Example_2024-11-18_12_04_12.etl", + "workingDirectory": "C:\\code\\Captures" + } + } +} \ No newline at end of file diff --git a/src/Ultra/Ultra.csproj b/src/Ultra/Ultra.csproj index 3ddeb14..cec2d89 100644 --- a/src/Ultra/Ultra.csproj +++ b/src/Ultra/Ultra.csproj @@ -14,11 +14,11 @@ - This is a default project description + Ultra is a an advanced profiler for .NET Applications available on Windows. Alexandre Mutel en-US Alexandre Mutel - tag1;tag2;tag3 + profiler;etw readme.md ultra.png https://github.com/xoofx/ultra diff --git a/src/ultra.sln b/src/ultra.sln index 7c93c26..453df49 100644 --- a/src/ultra.sln +++ b/src/ultra.sln @@ -25,6 +25,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ultra.Core", "Ultra.Core\Ul EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ultra.Example", "Ultra.Example\Ultra.Example.csproj", "{E0F444AD-490E-464F-BA51-A82886C7FF6A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{2D4C3E26-09DE-45BB-A581-6EEF97C6CFA1}" + ProjectSection(SolutionItems) = preProject + ..\doc\readme.md = ..\doc\readme.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/ultra.sln.DotSettings b/src/ultra.sln.DotSettings new file mode 100644 index 0000000..3a93f7c --- /dev/null +++ b/src/ultra.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file