Create your own CLR Profiler
A CLR profiler is a very powerful way to examine your managed code. Let’s create your own CLR Profiler.
This simple example will
1. Start the specified managed application that we will profile
2. Intercept the creation of all managed objects
3. Calculate the class name for the object (Like “System.String” or “WindowsFormsApplication1.Paddle”)
4. Accumulate per class the number of instances and the total size.
5. Upon exit of the profilee, the data is written to a file on your Desktop called “LogOutputClassMap.csv”
6. An attempt to start the file will be made. If you have Excel on the machine, it will be opened with Excel.
7. In Excel, hit AltN->T (Insert Table), then Enter to accept the default table size.
8. You can sort, filter and query the data in Excel.
I see output like so:
We’ll create a solution with 2 C++ projects. The first is ClrLauncher, which will produce ClrLauncher.exe.
It’s almost the same as Use reflection from native C++ code to run managed code
Start Visual Studio
File->New Project->C++->Win32->Win32 Project.
Name it "ClrLauncher"
Application Type: Windows Application
Choose Additional options: Empty Project
Project->Add New Item->C++ File "ClrLauncher.cpp"
Paste in the code below marked “ClrLauncher” (Note : there are 2 different sections of code below. Use the one marked “ClrLauncher”)
In the command line arguments, paste in 3 parameters
Full Path to exe (in quotes if there's a space embedded)
Name of type to instantiate (Like "WindowsFormsApplication1.Form1")
Name of method on type to invoke (like "ShowDialog")
I used: "C:\Users\calvinh\Documents\Visual Studio 2013\Projects\Paddle\Paddle\bin\Debug\Paddle.exe" WindowsFormsApplication1.Form1 ShowDialog
Now before we add the 2nd project, we can just hit F5 to run the program, which will start the CLR and launch the target Breakout game.
See Create and play your own Breakout game
Now we’ll add the 2nd project to the solution:
File->Add New project->C++ Win32 Project “ClrProfiler”
Application Type: Dll
Choose Additional options: Empty Project
Now right click on the Source Files node of the ClrProfiler project in Solution Explorer, and add a new C++ Item: ClrProfiler.cpp
Paste in the “ClrProfiler” code below.
Build the project.
Make sure the code in ClrLauncher: EnableMyProfiler points to the ClrProfiler.dll (the full path is shown in the Output window on a full build)
Run the project again. This time, because ClrProfiler.dll exists, the CLR will instantiate it and calls to Object Allocations are intercepted.
The ClrProfiler code can be run separately: when the CLR starts, it looks for environment variables specifying a profiler.
See Setting Up a Profiling Environment
Try setting the environment variables, then start Visual Studio! I get thousands of classes.
<Code for ClrLauncher>
//
/*
ClrLauncher: will launch Managed code
Pass in 3 command line parameters:
Full Path to exe (in quotes if there's a space embedded)
Name of type to instantiate (Like "WindowsFormsApplication1.Form1")
Name of method on type to invoke (like "ShowDialog")
This sample also sets 3 environment variables specifying a Clr Profiler,
which are ignored if the profiler is not found.
Start Visual Studio
File->New Project->C++->Win32->Win32 Project.
Name it "ClrLauncher"
Application Type: Windows Application
Choose Additional options: Empty Project
Project->Add New Item->C++ File "ClrLauncher.cpp"
paste in the code below:
Pass the asmFileName and typeNameToInstantiate in the cmd line args
Project->Properties->Configuration Properties->Debugging->Command Arguments
For mine, I used: ("Create and play your own Breakout game": https://blogs.msdn.com/b/calvin_hsia/archive/2013/10/30/10461927.aspx )
"C:\Users\calvinh\Documents\Visual Studio 2013\Projects\Paddle\Paddle\bin\Debug\Paddle.exe" WindowsFormsApplication1.Form1 ShowDialog
You can just hit F5 to run have this code start your application.
Note: the default debugger is Native for this project, but because we start the CLR
managed debugging is useful too.
Project->Properties->Configuration Properties->Debugging->Debugger Type: Mixed
*/
#include "atlsafe.h"
#include "atlcom.h"
#import <mscorlib.tlb> raw_interfaces_only rename("ReportEvent","ReportEventManaged")
#include <metahost.h>
#pragma comment(lib,"mscoree.lib")
#include <mscoree.h>
#include "shellapi.h"
using namespace mscorlib;
void EnableMyProfiler()
{
#if _DEBUG
WCHAR filename[] = L"c:\\users\\calvinh\\documents\\visual studio 2013\\Projects\\ClrLauncher\\Debug\\ClrProfiler.dll";
#else
WCHAR filename[] = L"c:\\users\\calvinh\\documents\\visual studio 2013\\Projects\\ClrLauncher\\Release\\ClrProfiler.dll";
#endif
/*
// set these 3 env variables to enable profiling
// indicate that the profiler object to be created lives in this module
set COR_ENABLE_PROFILING=1
set COR_PROFILER={EA6ED0D7-6196-457D-9A2D-B00F1CC3EA8E}
set COR_PROFILER_PATH=c:\users\calvinh\documents\visual studio 2013\Projects\ClrLauncher\Debug\ClrProfiler.dll
(COR_PROFILER_PATH should have no quotes)
Any program that uses the CLR will loook for these environment variables
Try setting them, then start VS
*/
SetEnvironmentVariable(L"COR_ENABLE_PROFILING", L"1");
SetEnvironmentVariable(L"COR_PROFILER", L"{EA6ED0D7-6196-457D-9A2D-B00F1CC3EA8E}");
SetEnvironmentVariable(L"COR_PROFILER_PATH", filename);
}
// find a specified assembly from an AppDomain by enumerating assemblies
HRESULT GetAssemblyFromAppDomain(
_AppDomain* pAppDomain,
LPCWSTR wszAssemblyName,
_Deref_out_opt_ _Assembly **ppAssembly)
{
*ppAssembly = NULL;
// get the assemblies into a safearray
SAFEARRAY *pAssemblyArray = NULL;
HRESULT hr = pAppDomain->GetAssemblies(&pAssemblyArray);
if (FAILED(hr))
{
return hr;
}
// put the safearray into a smart ptr, so it gets released
CComSafeArray<IUnknown*> csaAssemblies;
csaAssemblies.Attach(pAssemblyArray);
size_t cchAssemblyName = wcslen(wszAssemblyName);
long cAssemblies = csaAssemblies.GetCount();
for (long i = 0; i<cAssemblies; i++)
{
CComPtr<_Assembly> spAssembly;
spAssembly = csaAssemblies[i];
if (spAssembly == NULL)
continue;
CComBSTR cbstrAssemblyFullName;
hr = spAssembly->get_FullName(&cbstrAssemblyFullName);
if (FAILED(hr))
continue;
// is it the one we want?
if (cbstrAssemblyFullName != NULL &&
_wcsnicmp(cbstrAssemblyFullName,
wszAssemblyName,
cchAssemblyName) == 0)
{
*ppAssembly = spAssembly.Detach();
hr = S_OK;
break;
}
}
if (*ppAssembly == 0)
{
hr = E_FAIL;
}
return hr;
}
void StartClrCode(CComBSTR asmFileName, CComBSTR typeNameToInstantiate, CComBSTR typeMemberToCall)
{
CComPtr<ICLRMetaHost> spClrMetaHost;
// get a MetaHost
HRESULT hr = CLRCreateInstance(
CLSID_CLRMetaHost,
IID_PPV_ARGS(&spClrMetaHost)
);
_ASSERT(hr == S_OK);
// get a particular runtime version
CComPtr<ICLRRuntimeInfo> spCLRRuntimeInfo;
hr = spClrMetaHost->GetRuntime(L"v4.0.30319",
IID_PPV_ARGS(&spCLRRuntimeInfo)
);
_ASSERT(hr == S_OK);
// get the CorRuntimeHost
CComPtr<ICorRuntimeHost> spCorRuntimeHost;
hr = spCLRRuntimeInfo->GetInterface(
CLSID_CorRuntimeHost,
IID_PPV_ARGS(&spCorRuntimeHost)
);
_ASSERT(hr == S_OK);
// Start the CLR
hr = spCorRuntimeHost->Start();
_ASSERT(hr == S_OK);
// get the Default app domain as an IUnknown
CComPtr<IUnknown> spAppDomainThunk;
hr = spCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
_ASSERT(hr == S_OK);
// convert the Appdomain IUnknown to a _AppDomain
CComPtr<_AppDomain> spAppDomain;
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spAppDomain));
_ASSERT(hr == S_OK);
// Get the mscorlib assembly
CComPtr<_Assembly> sp_mscorlib;
hr = GetAssemblyFromAppDomain(spAppDomain, L"mscorlib", &sp_mscorlib);
_ASSERT(hr == S_OK);
// get the Type of "System.Reflection.Assembly"
CComPtr<_Type> _typeReflectionAssembly;
hr = sp_mscorlib->GetType_2(
CComBSTR(L"System.Reflection.Assembly"),
&_typeReflectionAssembly);
_ASSERT(hr == S_OK);
// create the array of args. only need 1 argument, array
auto psaLoadFromArgs = SafeArrayCreateVector(
VT_VARIANT,
0, //start array at 0
1); //# elems = 1
long index = 0;
// set the array element
CComVariant arg1(asmFileName); // the argument: the asm to load
SafeArrayPutElement(psaLoadFromArgs, &index, &arg1);
//invoke the "Assembly.LoadFrom" public static member to load the paddle.exe
CComVariant cvtEmptyTarget;
CComVariant cvtLoadFromReturnValue;
hr = _typeReflectionAssembly->InvokeMember_3(
CComBSTR(L"LoadFrom"),
static_cast<BindingFlags>(BindingFlags_InvokeMethod |
BindingFlags_Public |
BindingFlags_Static),
nullptr, //Binder
cvtEmptyTarget, // target. Since the method is static, an empty variant
psaLoadFromArgs, //args
&cvtLoadFromReturnValue);
_ASSERT(hr == S_OK);
SafeArrayDestroy(psaLoadFromArgs); // don't need args any more
_ASSERT(cvtLoadFromReturnValue.vt == VT_DISPATCH);
// get the assembly from the return value
CComPtr<_Assembly> srpAssemblyTarget;
srpAssemblyTarget.Attach(
static_cast<_Assembly *>(cvtLoadFromReturnValue.pdispVal
));
// get the desired type from the assembly
CComPtr<_Type> _typeForm;
hr = srpAssemblyTarget->GetType_2(
typeNameToInstantiate,
&_typeForm);
_ASSERT(hr == S_OK && _typeForm != nullptr);
// create an instance of the target type
CComVariant resTargetInstance;
hr = srpAssemblyTarget->CreateInstance(
typeNameToInstantiate,
&resTargetInstance);
_ASSERT(hr == S_OK);
// create an array var for return value of Type->GetMember
SAFEARRAY *psaMember = nullptr;
hr = _typeForm->GetMember(typeMemberToCall,
MemberTypes_Method,
(BindingFlags)(BindingFlags_Instance + BindingFlags_Public),
&psaMember);
_ASSERT(hr == S_OK);
// put into SafeArray so it gets released
CComSafeArray<IUnknown *> psaMem;
psaMem.Attach(psaMember);
// Get the Method Info for "ShowDialog" from the 1st type in the array
CComPtr<_MethodInfo> methodInfo;
hr = psaMem[0]->QueryInterface(
IID_PPV_ARGS(&methodInfo)
);
_ASSERT(hr == S_OK);
// invoke the ShowDialog method on the Form(WinForm) or Window (wpf)
hr = methodInfo->Invoke_3(
resTargetInstance,
nullptr, //parameters
nullptr // return value
);
_ASSERT(hr == S_OK);
// stop the runtime
hr = spCorRuntimeHost->Stop();
_ASSERT(hr == S_OK);
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
EnableMyProfiler();
// convert the single cmd line string to multiple args (handles quotes too)
// "C:\Users\calvinh\Documents\Visual Studio 2012\Projects\Cartoon\Cartoon\bin\Debug\Cartoon.exe" Cartoon.MainWindow ShowDialog
// "C:\Users\calvinh\Documents\Visual Studio 2012\Projects\Paddle\Paddle\bin\Debug\Paddle.exe" WindowsFormsApplication1.Form1 ShowDialog
int nArgs = 0;
LPWSTR *pCmdLineArgs = CommandLineToArgvW(lpCmdLine, &nArgs);
if (nArgs == 3)
{
// the full file name on disk for the assembly. Can be WPF or Winform (both have ShowDialog methods)
CComBSTR asmFileName = pCmdLineArgs[0];
// the name of the type to instantiate
CComBSTR typeNameToInstantiate = pCmdLineArgs[1];
// the static public member of the type to call
CComBSTR typeMemberToCall = pCmdLineArgs[2];
StartClrCode(asmFileName, typeNameToInstantiate, typeMemberToCall);
}
else
{
MessageBox(0, L"Enter 3 parameters: full path to assembly to load (perhaps quoted), name of Type to create (like \"WindowsFormsApplication1.Form1\", Member Name to call", L"", 0);
}
return 0;
}
</Code for ClrLauncher>
<Code for ClrProfiler>
// ClrProfiler.cpp : Defines the exported functions for the DLL application.
// File->New Project->VC++->Win32 Project "ClrProfiler"
// in the Win32 Application Wizard, choose application type: Dll, Empty Project.
// Right click on Solution Explroer->Source Files->Add New Item->C++ File. "ClrProfiler.cpp"
//
#include "atlsafe.h"
#include "atlcom.h"
#include <cor.h>
#include <corprof.h>
#include "string"
#include "unordered_map"
#include "map"
#include "iostream"
#include "fstream"
#include "shlobj.h"
#include "shellapi.h"
using namespace std;
//using namespace mscorlib;
#define MAXCLASSNAMELEN 900
#define PASTE2(x,y) x##y
#define PASTE(x,y) PASTE2(x,y)
#define __WFUNCDNAME__ PASTE(L, __FUNCDNAME__)
#define PROF_NOT_IMP(methodName, ...) \
STDMETHOD(methodName) (__VA_ARGS__) \
{ \
if (m_fLoggingOn) LogOutput(__WFUNCDNAME__); \
return S_OK; \
} \
// let's define a Guid that represents our profiler
// VS->Tools->Create GUID
#include "initguid.h"
// {EA6ED0D7-6196-457D-9A2D-B00F1CC3EA8E}
DEFINE_GUID(CLSID_MyProfiler,
0xea6ed0d7, 0x6196, 0x457d, 0x9a, 0x2d, 0xb0, 0xf, 0x1c, 0xc3, 0xea, 0x8e);
// a class that holds information about CLR objects
class ClrClassStats {
public:
wstring className; // name of class
int nInstances; // # of instances ever created
long nSize; // total size (some instances vary in size, like arrays and string)
};
CComQIPtr<ICorProfilerInfo2 > g_pCorProfilerInfo; // can be null
// declare a COM profiler that implements an interface ICorProfilerCallback3
class MyProfiler :
public ICorProfilerCallback3,
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<MyProfiler, &CLSID_MyProfiler>
{
public:
BEGIN_COM_MAP(MyProfiler)
COM_INTERFACE_ENTRY_IID(CLSID_MyProfiler, MyProfiler)
COM_INTERFACE_ENTRY(ICorProfilerCallback2)
COM_INTERFACE_ENTRY(ICorProfilerCallback3)
END_COM_MAP()
DECLARE_NOT_AGGREGATABLE(MyProfiler)
DECLARE_NO_REGISTRY()
MyProfiler()
{
LogOutput(L"MyProfiler Constructor");
}
bool m_fLoggingOn;
void LogOutput(LPCWSTR wszFormat, ...)
{
if (IsDebuggerPresent())
{
SYSTEMTIME st;
GetLocalTime(&st);
WCHAR buf[1000];
swprintf_s(buf, L"%2d/%02d/%02d %2d:%02d:%02d:%03d thrd=%d ", st.wMonth, st.wDay, st.wYear - 2000, st.wHour,
st.wMinute, st.wSecond, st.wMilliseconds, GetCurrentThreadId());
OutputDebugStringW(buf);
va_list insertionArgs;
va_start(insertionArgs, wszFormat);
_vsnwprintf_s(buf, _countof(buf), wszFormat, insertionArgs);
va_end(insertionArgs);
OutputDebugStringW(buf);
OutputDebugStringW(L"\r\n");
}
}
// ICorProfilerCallback2 >>
// STARTUP/SHUTDOWN EVENTS
STDMETHOD(Initialize)(IUnknown *pICorProfilerInfoUnk)
{
m_fLoggingOn = true;
// tell clr which events we want to be called for
DWORD dwEventMask = COR_PRF_ENABLE_STACK_SNAPSHOT
| COR_PRF_ENABLE_OBJECT_ALLOCATED
| COR_PRF_MONITOR_GC //GarbageCollectionStarted, GarbageCollectionFinished, MovedReferences, SurvivingReferences, ObjectReferences, ObjectsAllocatedByClass, RootReferences, HandleCreated, HandleDestroyed, and FinalizeableObjectQueued callbacks.
| COR_PRF_MONITOR_OBJECT_ALLOCATED // Object
// | COR_PRF_MONITOR_ENTERLEAVE // method enter/leave
| COR_PRF_MONITOR_CLASS_LOADS // ClassLoad and ClassUnload
| COR_PRF_MONITOR_MODULE_LOADS // ModuleLoad, ModuleUnload, and ModuleAttachedToAssembly callbacks.
| COR_PRF_MONITOR_ASSEMBLY_LOADS // AssemblyLoad and AssemblyUnload callbacks
| COR_PRF_MONITOR_APPDOMAIN_LOADS // ModuleLoad, ModuleUnload, and ModuleAttachedToAssembly callbacks.
| COR_PRF_MONITOR_SUSPENDS //Controls the RuntimeSuspend, RuntimeResume, RuntimeThreadSuspended, and RuntimeThreadResumed callbacks.
| COR_PRF_MONITOR_THREADS // Controls the ThreadCreated, ThreadDestroyed, ThreadAssignedToOSThread, and ThreadNameChanged callbacks
;
g_pCorProfilerInfo = pICorProfilerInfoUnk;
g_pCorProfilerInfo->SetEventMask(dwEventMask);
return S_OK;
}
STDMETHOD(ObjectAllocated)(ObjectID objectID, ClassID classID)
{ // gets called whenever a managed obj is created
HRESULT hr = S_OK;
_csectClassMap.Lock();
ClrClassStats *pClrObjStats = nullptr;
// beware of threading!
auto res = _ClassDictionary.find(classID);
if (res != _ClassDictionary.end())
{// already exists
pClrObjStats = &res->second;
}
else
{ // not found. figure out the name of the obj
bool fIsArrayClass = false;
CorElementType elemType;
ClassID arrayClassId;
ULONG arrayRank;
hr = g_pCorProfilerInfo->IsArrayClass(
classID,
&elemType,
&arrayClassId,
&arrayRank);
if (hr == S_OK)
{
if (elemType == ELEMENT_TYPE_CLASS)
{
fIsArrayClass = true;
classID = arrayClassId;
}
else
{
hr = E_FAIL; // todo
}
}
if (hr != E_FAIL)// (S_FALSE if not array)
{
ModuleID moduleID = 0;
mdTypeDef tdToken;
ClassID classIdParent;
ClassID typeArgs[20]; //generics
ULONG32 cNumTypeArgs;
cNumTypeArgs = _countof(typeArgs);
hr = g_pCorProfilerInfo->GetClassIDInfo2(
classID,
&moduleID,
&tdToken,
&classIdParent,
cNumTypeArgs,
&cNumTypeArgs,
typeArgs);
if (hr == S_OK && moduleID != 0)
{
CComPtr<IMetaDataImport> pIMetaDataImport;
if (g_pCorProfilerInfo->GetModuleMetaData(
moduleID,
ofRead,
IID_IMetaDataImport,
(LPUNKNOWN *)&pIMetaDataImport) == S_OK)
{
WCHAR wszClassName[MAXCLASSNAMELEN] = { 0 };
mdToken tkExtends = NULL;
hr = pIMetaDataImport->GetTypeDefProps(
tdToken,
wszClassName,
_countof(wszClassName),
0,
0,
&tkExtends);
if (hr == S_OK)
{
ClrClassStats classStat =
{
wszClassName //assign WCHAR to wstring
};
if (fIsArrayClass)
{
classStat.className.append(L"[]");
}
if (m_fLoggingOn)
{
LogOutput(classStat.className.c_str());
}
auto res = _ClassDictionary.insert(
pair<ClassID, ClrClassStats>(
classID,
classStat)
);
pClrObjStats = &res.first->second;
}
}
}
}
}
if (pClrObjStats != nullptr)
{
ULONG ulObjSize = 0;
g_pCorProfilerInfo->GetObjectSize(objectID, &ulObjSize);
pClrObjStats->nSize += ulObjSize;
pClrObjStats->nInstances += 1;
if (pClrObjStats->className == L"WindowsFormsApplication1.Paddle")
{
auto x = "Put a breakpoint here";
}
}
_csectClassMap.Unlock();
return S_OK;
}
STDMETHOD(Shutdown)()
{
wchar_t desktopFolderName[MAX_PATH];
SHGetFolderPath(
NULL, //hWnd,
CSIDL_DESKTOP,
NULL, //token
0, //dwFlags
desktopFolderName
); // C:\Users\calvinh\Desktop
wstring outFileNameClassMap(desktopFolderName);
outFileNameClassMap.append(L"\\LogOutputClassMap.csv");
{
// sort by name
map<wstring, ClrClassStats> mapClassbyName;
for (const auto &obj : _ClassDictionary)
{
mapClassbyName.insert(pair<wstring, ClrClassStats>(obj.second.className, obj.second));
}
wofstream outFile(outFileNameClassMap); //dtor will close file
outFile << "Class Name, NumInstances, TotSize" << endl;
for (const auto &obj : mapClassbyName)
{
LogOutput(L"%s, %d, %d", obj.first.c_str(), obj.second.nInstances, obj.second.nSize);
outFile << obj.first.c_str()
<< L","
<< obj.second.nInstances
<< L","
<< obj.second.nSize
<< endl;
}
}
// now start file with Excel
ShellExecute(
0, // hwnd
0, // lpOperation
outFileNameClassMap.c_str(),
nullptr, //lpParameters
nullptr, // lpDirectory
0 // nShowCmd
);
return S_OK;
}
// APPLICATION DOMAIN EVENTS
PROF_NOT_IMP(AppDomainCreationStarted, AppDomainID appDomainId);
PROF_NOT_IMP(AppDomainCreationFinished, AppDomainID appDomainId, HRESULT hr);
PROF_NOT_IMP(AppDomainShutdownStarted, AppDomainID);
PROF_NOT_IMP(AppDomainShutdownFinished, AppDomainID appDomainId, HRESULT hr);
// ASSEMBLY EVENTS
PROF_NOT_IMP(AssemblyLoadStarted, AssemblyID);
PROF_NOT_IMP(AssemblyLoadFinished, AssemblyID, HRESULT);
PROF_NOT_IMP(AssemblyUnloadStarted, AssemblyID);
PROF_NOT_IMP(AssemblyUnloadFinished, AssemblyID assemblyID, HRESULT hr);
// MODULE EVENTS
PROF_NOT_IMP(ModuleLoadStarted, ModuleID);
PROF_NOT_IMP(ModuleLoadFinished, ModuleID moduleID, HRESULT hr);
PROF_NOT_IMP(ModuleUnloadStarted, ModuleID moduleId);
PROF_NOT_IMP(ModuleUnloadFinished, ModuleID, HRESULT);
PROF_NOT_IMP(ModuleAttachedToAssembly, ModuleID moduleID, AssemblyID assemblyID);
// CLASS EVENTS
PROF_NOT_IMP(ClassLoadStarted, ClassID classId);
PROF_NOT_IMP(ClassLoadFinished, ClassID classId, HRESULT hr);
PROF_NOT_IMP(ClassUnloadStarted, ClassID classId);
PROF_NOT_IMP(ClassUnloadFinished, ClassID, HRESULT);
PROF_NOT_IMP(FunctionUnloadStarted, FunctionID);
// JIT EVENTS
PROF_NOT_IMP(JITCompilationStarted, FunctionID functionID, BOOL fIsSafeToBlock);
PROF_NOT_IMP(JITCompilationFinished, FunctionID functionID, HRESULT hrStatus, BOOL fIsSafeToBlock);
PROF_NOT_IMP(JITCachedFunctionSearchStarted, FunctionID functionId, BOOL *pbUseCachedFunction);
PROF_NOT_IMP(JITCachedFunctionSearchFinished, FunctionID, COR_PRF_JIT_CACHE);
PROF_NOT_IMP(JITFunctionPitched, FunctionID);
PROF_NOT_IMP(JITInlining, FunctionID, FunctionID, BOOL*);
// THREAD EVENTS
PROF_NOT_IMP(ThreadCreated, ThreadID);
PROF_NOT_IMP(ThreadDestroyed, ThreadID);
PROF_NOT_IMP(ThreadAssignedToOSThread, ThreadID, DWORD);
// REMOTING EVENTS
// Client-side events
PROF_NOT_IMP(RemotingClientInvocationStarted);
PROF_NOT_IMP(RemotingClientSendingMessage, GUID*, BOOL);
PROF_NOT_IMP(RemotingClientReceivingReply, GUID*, BOOL);
PROF_NOT_IMP(RemotingClientInvocationFinished);
// Server-side events
PROF_NOT_IMP(RemotingServerReceivingMessage, GUID*, BOOL);
PROF_NOT_IMP(RemotingServerInvocationStarted);
PROF_NOT_IMP(RemotingServerInvocationReturned);
PROF_NOT_IMP(RemotingServerSendingReply, GUID*, BOOL);
// CONTEXT EVENTS
PROF_NOT_IMP(UnmanagedToManagedTransition, FunctionID, COR_PRF_TRANSITION_REASON);
PROF_NOT_IMP(ManagedToUnmanagedTransition, FunctionID, COR_PRF_TRANSITION_REASON);
// SUSPENSION EVENTS
PROF_NOT_IMP(RuntimeSuspendStarted, COR_PRF_SUSPEND_REASON);
PROF_NOT_IMP(RuntimeSuspendFinished);
PROF_NOT_IMP(RuntimeSuspendAborted);
PROF_NOT_IMP(RuntimeResumeStarted);
PROF_NOT_IMP(RuntimeResumeFinished);
PROF_NOT_IMP(RuntimeThreadSuspended, ThreadID);
PROF_NOT_IMP(RuntimeThreadResumed, ThreadID);
// GC EVENTS
PROF_NOT_IMP(MovedReferences, ULONG cmovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], ULONG cObjectIDRangeLength[]);
PROF_NOT_IMP(ObjectsAllocatedByClass, ULONG classCount, ClassID classIDs[], ULONG objects[]);
PROF_NOT_IMP(ObjectReferences, ObjectID objectID, ClassID classID, ULONG cObjectRefs, ObjectID objectRefIDs[]);
PROF_NOT_IMP(RootReferences, ULONG cRootRefs, ObjectID rootRefIDs[]);
// Exception creation
PROF_NOT_IMP(ExceptionThrown, ObjectID);
// Exception Caught
PROF_NOT_IMP(ExceptionCatcherEnter, FunctionID, ObjectID);
PROF_NOT_IMP(ExceptionCatcherLeave);
// Search phase
PROF_NOT_IMP(ExceptionSearchFunctionEnter, FunctionID);
PROF_NOT_IMP(ExceptionSearchFunctionLeave);
PROF_NOT_IMP(ExceptionSearchFilterEnter, FunctionID);
PROF_NOT_IMP(ExceptionSearchFilterLeave);
PROF_NOT_IMP(ExceptionSearchCatcherFound, FunctionID);
// Unwind phase
PROF_NOT_IMP(ExceptionUnwindFunctionEnter, FunctionID);
PROF_NOT_IMP(ExceptionUnwindFunctionLeave);
PROF_NOT_IMP(ExceptionUnwindFinallyEnter, FunctionID);
PROF_NOT_IMP(ExceptionUnwindFinallyLeave);
PROF_NOT_IMP(ExceptionCLRCatcherFound); // Deprecated in .Net 2.0
PROF_NOT_IMP(ExceptionCLRCatcherExecute); // Deprecated in .Net 2.0
PROF_NOT_IMP(ExceptionOSHandlerEnter, FunctionID); // Not implemented
PROF_NOT_IMP(ExceptionOSHandlerLeave, FunctionID); // Not implemented
// IID_ICorProfilerCallback2 EVENTS
PROF_NOT_IMP(ThreadNameChanged, ThreadID threadId, ULONG cchName, __in WCHAR name[]);
PROF_NOT_IMP(GarbageCollectionStarted, int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason);
PROF_NOT_IMP(SurvivingReferences, ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], ULONG cObjectIDRangeLength[]);
PROF_NOT_IMP(GarbageCollectionFinished);
PROF_NOT_IMP(FinalizeableObjectQueued, DWORD finalizerFlags, ObjectID objectID);
PROF_NOT_IMP(RootReferences2, ULONG cRootRefs, ObjectID rootRefIds[], COR_PRF_GC_ROOT_KIND rootKinds[], COR_PRF_GC_ROOT_FLAGS rootFlags[], UINT_PTR rootIds[]);
PROF_NOT_IMP(HandleCreated, GCHandleID handleId, ObjectID initialObjectId);
PROF_NOT_IMP(HandleDestroyed, GCHandleID handleId);
// COM CLASSIC VTable
PROF_NOT_IMP(COMClassicVTableCreated, ClassID wrappedClassID, REFGUID implementedIID, void *pVTable, ULONG cSlots);
PROF_NOT_IMP(COMClassicVTableDestroyed, ClassID wrappedClassID, REFGUID implementedIID, void *pVTable);
// ICorProfilerCallback2 <<
PROF_NOT_IMP(InitializeForAttach, IUnknown* punk, void* data, UINT datasize);
PROF_NOT_IMP(ProfilerAttachComplete);
PROF_NOT_IMP(ProfilerDetachSucceeded);
protected:
//key = ClassId: one entry per unique ClassId
unordered_map<ClassID, ClrClassStats> _ClassDictionary;
CComAutoCriticalSection _csectClassMap;
};
OBJECT_ENTRY_AUTO(CLSID_MyProfiler, MyProfiler)
// define a class that represents this module
class CClrProfModule : public ATL::CAtlDllModuleT< CClrProfModule >
{
#if _DEBUG
public:
CClrProfModule()
{
int x = 0; // set a bpt here
}
~CClrProfModule()
{
int x = 0; // set a bpt here
}
#endif _DEBUG
};
// instantiate a static instance of this class on module load
CClrProfModule _AtlModule;
// this gets called by CLR due to env var settings
STDAPI DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, __deref_out LPVOID FAR* ppv)
{
HRESULT hr = E_FAIL;
hr = AtlComModuleGetClassObject(&_AtlComModule, rclsid, riid, ppv);
// hr= CComModule::GetClassObject();
return hr;
}
//tell the linker to export the function
#pragma comment(linker, "/EXPORT:DllGetClassObject=_DllGetClassObject@12,PRIVATE")
</Code for ClrProfiler>
Comments
- Anonymous
January 23, 2017
The comment has been removed