(Full disclosure: I'm on the Visual Studio Profiler team, but the below information is public)
You can do this by writing a CLR profiler that runs inside the process you're targeting. CLR profilers are C++ COM objects that get instantiated by the runtime when the COR_PROFILER
and COR_PROFILING_ENABLED
environment variables are set (see here). There are two main CLR profiling interfaces, specifically, ICorProfilerCallback
and ICorProfilerInfo
. ICorProfilerCallback
is what the CLR uses to notify you about specific events that you subscribe to (module loads, function JIT compliation, thread creation, GC events), while ICorProfilerInfo
can be used by your profiler to obtain additional information about threads, modules, types, methods, and metadata for the loaded assemblies. This interface is what you could use to obtain symbol information about the types allocated.
With your profiler in-process, you can force a GC through ICorProfilerInfo::ForceGC
. After the GC completes, your profiler will get notified via ICorProfilerCallback2::GarbageCollectionFinished
, and you will get the root references via ICorProfilerCallback2::RootReferences2
. When you combine the root reference information with ICorProfilerCallback::ObjectReferences
, you can get the complete object reference graph for your .NET application.
You can get more realtime information by using the ICorProfilerCallback::ObjectAllocated
callback to determine when individual CLR objects get created. This can be expensive, though, since you're incurring at least an additional function call for each allocated object. You can track individual objects by mapping the CLR-assigned ObjectID
to your own internal ID. An ObjectID
for a given object is an ephemeral pointer since it can change as garbage collections happen, which can cause objects to move during compaction. This process is illustrated here. You can use the information from ICorProfilerCallback::MovedReferences
to track moving objects.
In order to activate the callbacks mentioned above, you need to tell the CLR profiling API that you're interested in them. You can do this by specifying COR_PRF_MONITOR_GC
and COR_PRF_MONITOR_OBJECT_ALLOCATED
as part of your event flags when calling ICorProfilingInfo::SetEventMask
.
David Broman is the developer on the CLR profiler, and his blog has tons of great information on profiling in general, including all the crazy pitfalls and issues you might run into.