Skip to content

Create a profiler API that appends to the TPA list #10382

Closed
@noahfalk

Description

@noahfalk

This is a more distilled design proposal that solves issues raised in #5429. The idea is to add a new profiler API such as:

ICorProfilerInfo10::AddAssemblyPath(AppDomainID appDomainId, const WCHAR* pAssemblyPath)

which will add a path to list of assemblies that the runtime probes to resolve assembly references. This allows profilers to instrument managed code and include assembly references to a new assembly that was deployed by the profiler rather than being deployed as part of the application.

Background info to assist implementors

Probably the hardest part of the problem is creating a testable repro scenario (assuming you don't already have your own IL instrumenting profiler handy). There is an example instrumenting profiler here: https://github.com/Microsoft/clr-samples/tree/master/ProfilingAPI/ReJITEnterLeaveHooks that has most of what is necessary. However this example instruments using a CALLI instruction:
https://github.com/Microsoft/clr-samples/blob/master/ProfilingAPI/ReJITEnterLeaveHooks/ILRewriter.cpp#L746

To reproduce the problem here the profiler instead needs to:

  1. Include with the profiler a new managed assembly that has the implementation of some simple callback within it. Lets call this ManagedInstrumentation.dll.
     public static class ManagedCallbackFunctions
     {
         public static void OnMethodEnter(int functionId)
         {
              Console.WriteLine("Function " + functionId + " was called");
         }
     }
  1. Use IMetaDataAssemblyEmit::DefineAssemblyRef to create an AssemblyRef token that refers to the ManagedInstrumentation.dll assembly.
    https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataassemblyemit-defineassemblyref-method
  2. Use IMetaDataEmit::DefineTypeRefByName and DefineMemberRef
    https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataemit-definetyperefbyname-method
    https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataemit-definememberref-method
    to create a MemberRef token that refers to ManagedCallbackFunctions.OnMethodEnter
  3. Change the instrumentation to use an IL CALL opcode with the ManagedCallbackFunctions.OnMethodEnter token

Running an application with this profiler should fail because ManagedInstrumentation.dll can't be located.

  1. To test the new API you will need to handle the AppDomainCreationFinished callback. In the handler QI for ICorProfilerInfo10 and then invoke AddTrustedAssemblyPath(domainId, path_to_ManagedInstrumentation.dll). Initially the QI will fail, but once the feature is implemented this call should succeed at modifying the assembly list. Later the compilation of instrumented methods should also succeed because the runtime will find ManagedInstrumentation.dll and load it.

To implement the feature:

  1. Add the new API to https://github.com/dotnet/coreclr/blob/master/src/inc/corprof.idl
  2. Add an implementation to
    https://github.com/dotnet/coreclr/blob/master/src/vm/proftoeeinterfaceimpl.h
    https://github.com/dotnet/coreclr/blob/master/src/vm/proftoeeinterfaceimpl.cpp

The implementation of the method needs to change the TPA list which is stored here:
https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/binder/inc/applicationcontext.inl#L74

Navigating to that datastructure should be something like:

BaseDomain* pDomain = (BaseDomain *) appDomainId;
pDomain->GetTPABinderContext()->GetAppContext()->GetTpaList();

More background docs about CLR and profiling:
https://github.com/dotnet/coreclr/tree/master/Documentation/botr
https://github.com/dotnet/coreclr/blob/master/Documentation/botr/profiling.md
https://github.com/dotnet/coreclr/blob/master/Documentation/botr/profilability.md

Where the TPA list normally gets set from without using a profiler:
https://github.com/dotnet/coreclr/blob/ef88a92215a8f90fe0bd8b0327c16bb889902105/src/vm/corhost.cpp#L820
Inside the pProfilerNames/Values there is a property named TRUSTED_PLATFORM_ASSEMBLIES with a list of assembly paths. The runtime host constructs this list however they want (dotnet.exe assembles it from some .json configuration files like app.deps.json) and then the runtime stores it.

Metadata

Metadata

Assignees

Labels

area-Diagnostics-coreclrenhancementProduct code improvement that does NOT require public API changes/additions

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions