-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b7a6738
commit db21204
Showing
4 changed files
with
103 additions
and
5 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,88 @@ | ||
Silhouette - A library to build .NET profilers in .NET | ||
======================= | ||
|
||
# Quick start | ||
|
||
Create a new C# NativeAOT project. Reference the Silhouette nuget package and add a class inheriting from `Silhouette.CorProfilerCallback11Base` (you can use a different version of `CorProfilerCallbackBase` depending on the version of .NET you're targeting). Override the `Initialize` method. It will be called with the highest version number of `ICorProfilerInfo` supported by the target runtime. | ||
|
||
```csharp | ||
|
||
using Silhouette; | ||
|
||
internal partial class CorProfiler : CorProfilerCallback11Base | ||
{ | ||
protected override HResult Initialize(int iCorProfilerInfoVersion) | ||
{ | ||
if (iCorProfilerInfoVersion < 11) | ||
{ | ||
return HResult.E_FAIL; | ||
} | ||
|
||
var result = ICorProfilerInfo11.SetEventMask(COR_PRF_MONITOR.COR_PRF_ENABLE_STACK_SNAPSHOT | COR_PRF_MONITOR.COR_PRF_MONITOR_THREADS); | ||
|
||
return result; | ||
} | ||
} | ||
``` | ||
|
||
You also need to expose a `DllGetClassObject` method that will be called by the .NET runtime when initializing the profiler. Use the built-in `ClassFactory` implementation and give it an instance of your `CorProfiler` class. | ||
|
||
```csharp | ||
using Silhouette; | ||
using System.Runtime.InteropServices; | ||
|
||
internal class DllMain | ||
{ | ||
private static ClassFactory Instance; | ||
|
||
[UnmanagedCallersOnly(EntryPoint = "DllGetClassObject")] | ||
public static unsafe HResult DllGetClassObject(Guid* rclsid, Guid* riid, nint* ppv) | ||
{ | ||
// Use your own profiler GUID here | ||
if (*rclsid != new Guid("0A96F866-D763-4099-8E4E-ED1801BE9FBC")) | ||
{ | ||
return HResult.E_NOINTERFACE; | ||
} | ||
|
||
Instance = new ClassFactory(new CorProfiler()); | ||
*ppv = Instance.IClassFactory; | ||
|
||
return 0; | ||
} | ||
} | ||
``` | ||
|
||
`CorProfilerXxBase` offers base virtual methods for all `ICorProfilerCallback` methods, so override the ones you're interested in: | ||
|
||
```csharp | ||
protected override HResult ThreadCreated(ThreadId threadId) | ||
{ | ||
Console.WriteLine($"Thread created: {threadId.Value}"); | ||
return HResult.S_OK; | ||
} | ||
``` | ||
|
||
Use the `ICorProfilerInfoXx` fields to access the `ICorProfilerInfo` APIs: | ||
|
||
```csharp | ||
private unsafe string ResolveMethodName(nint ip) | ||
{ | ||
try | ||
{ | ||
var functionId = ICorProfilerInfo11.GetFunctionFromIP(ip).ThrowIfFailed(); | ||
var functionInfo = ICorProfilerInfo2.GetFunctionInfo(functionId).ThrowIfFailed(); | ||
using var metaDataImport = ICorProfilerInfo2.GetModuleMetaData(functionInfo.ModuleId, CorOpenFlags.ofRead, KnownGuids.IMetaDataImport).ThrowIfFailed().Wrap(); | ||
var methodProperties = metaDataImport.Value.GetMethodProps(new MdMethodDef(functionInfo.Token)).ThrowIfFailed(); | ||
var typeDefProps = metaDataImport.Value.GetTypeDefProps(methodProperties.Class).ThrowIfFailed(); | ||
|
||
return $"{typeDefProps.TypeName}.{methodProperties.Name}"; | ||
} | ||
catch (Win32Exception) | ||
{ | ||
return "<unknown>"; | ||
} | ||
} | ||
``` | ||
|
||
Most methods return an instance of `HResult<T>`. You can deconstruct it into a `(HResult error, T result)` and manually check the error code. You can also use the `ThrowIfFailed()` method that will return only the result and throw a `Win32Exception` if the error code is not `S_OK`. | ||
|
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
@set CORECLR_ENABLE_PROFILING=1 | ||
@set CORECLR_PROFILER={846F5F1C-F9AE-4B07-969E-05C26BC060D8} | ||
@set CORECLR_PROFILER_PATH=E:\git\ManagedDotnetProfiler\ManagedDotnetProfiler\bin\Release\net9.0\win-x64\publish\ManagedDotnetProfiler.dll | ||
@set CORECLR_PROFILER={0A96F866-D763-4099-8E4E-ED1801BE9FBC} | ||
@set CORECLR_PROFILER_PATH=..\..\..\..\ManagedDotnetProfiler\bin\Release\net9.0\win-x64\publish\ManagedDotnetProfiler.dll | ||
@TestApp.exe |