An open API service indexing awesome lists of open source software.

https://github.com/mvenditto/managedcorprofiler

Prototype NET (CLR) Profiler written in C#
https://github.com/mvenditto/managedcorprofiler

clr coreclr csharp dotnet icorprofiler icorprofilercallback nativeaot net6 net7 profiler

Last synced: 9 months ago
JSON representation

Prototype NET (CLR) Profiler written in C#

Awesome Lists containing this project

README

          

# ManagedCorProfiler

A prototype CLR profiler written in C# for learning and fun.

> [!WARNING]
> 🚧 WIP WIP WIP 🚧

## Building blocks
- [NativeAOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) native compilation to build and link the native profiler library.
- [ComWrappers API](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.comwrappers?view=net-7.0) to expose the `ICorProfilerCallbackX` to the CLR.
- [CsWin32](https://github.com/microsoft/CsWin32) to generate C# bindings for the native [Profiling API](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/).

## Exposing a profiler callback
```csharp
///
/// A sample profiler callback that prints the loaded modules
///
[ProfilerCallback("1E040027-162F-489B-B12F-F113E6AF40CF")]
internal unsafe class MyProfiler : CorProfilerCallback2
{
private ICorProfilerInfo2* _corProfilerInfo;

public override HRESULT Initialize(IUnknown* pICorProfilerInfoUnk)
{
// Query for a pointer to the ICorProfilerCallback2 interface
var hr = pICorProfilerInfoUnk->QueryInterface(ICorProfilerInfo2.IID_Guid, out var pinfo);

if (hr.Failed)
{
Console.WriteLine($"FAIL QueryInterface hr={hr}");
return HRESULT.E_FAIL;
}

// Track our reference
_corProfilerInfo = (ICorProfilerInfo2*)pinfo;
_corProfilerInfo->AddRef();

// Specify our profiler is interested in module load events (Module* callbacks)
hr = _corProfilerInfo->SetEventMask((uint)COR_PRF_MONITOR.COR_PRF_MONITOR_MODULE_LOADS);

if (hr.Failed)
{
Console.WriteLine($"FAIL SetEventMask hr={hr}");
return HRESULT.E_FAIL;
}

return HRESULT.S_OK;
}

public override HRESULT ModuleLoadFinished(nuint moduleId, HRESULT hrStatus)
{
if (hrStatus.Failed)
{
return HRESULT.S_OK;
}

const int NameBufferLength = 256; // char

// Allocate a buffer to hold the module name.
using var szNameBuffer = NativeBuffer.Alloc(NameBufferLength);

// A pointer to a string of wide-characters to pass in input to GetModuleInfo.
var szName = new PWSTR(szNameBuffer.Pointer);

uint pcchName = 0;

// Retrieve the file name of the module
var hr = _corProfilerInfo->GetModuleInfo(
moduleId, // the target moduleId
null,
NameBufferLength, // The length, in characters, of the szName return buffer
&pcchName, // A pointer to the total character length of the module's file name that is returned
szName, // A caller-provided wide character buffer
null);

if (hr.Failed)
{
Console.WriteLine($"FAIL GetModuleInfo hr={hr}");
return hr;
}

var moduleName = szName.CopyToString(length: (int)pcchName);

Console.WriteLine($"loaded module 0x{moduleId:x8} <{moduleName}>");

return HRESULT.S_OK;
}

public override HRESULT Shutdown()
{
_corProfilerInfo->Release();
return HRESULT.S_OK;
}
}
```

### Sample output

C:\ManagedCorProfiler\Samples\ModuleLoadsProfiler> .\run.cmd

[... OMITTED ...]
Loaded Module -> 0x7ffeac1b4000 C:\Users\dev\Source\Repos\runtime\artifacts\bin\coreclr\windows.x64.Debug\System.Private.CoreLib.dll
Loaded Module -> 0x7ffeac702148 C:\ManagedCorProfiler\Samples\SampleApp\bin\Debug\net8.0\SampleApp.dll
Loaded Module -> 0x7ffeac703e40 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\system.runtime.dll
Loaded Module -> 0x7ffeac8f9798 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\system.console.dll
Loaded Module -> 0x7ffeac8fc1f0 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\system.threading.dll
Loaded Module -> 0x7ffeac9317d0 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\system.text.encoding.extensions.dll
Loaded Module -> 0x7ffeac9389a0 C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.0\system.runtime.interopservices.dll
Hello World!
C:\ManagedCorProfiler\Samples\ModuleLoadsProfiler> â–ˆ

## Compilation

## Dumpbin of the profiler DLL

C:\ManagedCorProfiler\Samples\ModuleLoadsProfiler> dumpbin.exe /EXPORTS bin\Release\net8.0\publish\win-x64\ModuleLoadsProfiler.dll

[...OMITTED FOR BREVITY...]
ordinal hint RVA name
1 0 00232660 DllCanUnloadNow = DllCanUnloadNow
2 1 002323A0 DllGetClassObject = DllGetClassObject
3 2 002326A0 DllMain = DllMain
[...OMITTED FOR BREVITY...]
C:\ManagedCorProfiler\Samples\ModuleLoadsProfiler> â–ˆ

## ELT Hooks
.

## How to Build
.
### Requirements
.

## Contributing
Any contribution is welcome.
I'm actually working on porting the tests at [dotnet/runtime/tree/main/src/tests/profiler](https://github.com/dotnet/runtime/tree/main/src/tests/profiler),
this is a good place to start contributing.

## Resources
### Misc
- [Writing a .NET Profiler in C# by Kevin Gosse](https://minidump.net/writing-a-net-profiler-in-c-part-1-d3978aae9b12)
- [https://github.com/dotnet/runtime](https://github.com/dotnet/runtime)
### COM / COM Interop
- [DllGetClassObject](https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-dllgetclassobject)
- [ComWrappers Tutorial](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/tutorial-comwrappers)
- [ComWrappers interop sample](https://github.com/dotnet/samples/blob/main/core/interop/comwrappers/Tutorial/Program.cs)
### Profiling
- [Profiling Overview](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-overview)
- [Unmanaged profiling API](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/)
- [CoreCLR Profilin BOTR](https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/profiling.md)
- [clr-sample ProfilingAPI](https://github.com/mvenditto/clr-samples/tree/master/ProfilingAPI)
- [Profiling Interfaces](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-interfaces)
- [Profiling global static function](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-global-static-functions)
### Native AOT
- [NativeAOT Interop](https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/interop.md)
- [Deploying NativeAOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/)