Skip to content

mvenditto/ManagedCorProfiler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

d7161ba Â· Jan 24, 2024

History

93 Commits
Jan 10, 2024
Jan 10, 2024
Jan 21, 2024
Jan 10, 2024
Jan 20, 2024
Jan 21, 2024
Jan 20, 2024
Jan 21, 2024
Jan 1, 2023
Jan 13, 2024
Feb 1, 2023
Jan 21, 2024
Jan 24, 2024

Repository files navigation

ManagedCorProfiler

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

Warning

🚧 WIP WIP WIP 🚧

Building blocks

Exposing a profiler callback

///
/// 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<char>.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, this is a good place to start contributing.

Resources

Misc

COM / COM Interop

Profiling

Native AOT