I have received inquires from several people on how to use
Get/SetILFunctionBody in the profiling interfaces properly.

To address these and future inquiries, I have created a working example
of what can be done with ICorProfilerInfo::SetILFunctionBody in Rotor. 

This example replaces the Main method of managed application with a
simple "Hello world" program. The tokens in the "Hello world" program
are dynamically generated using IMetaDataEmit interface.

To see the example in action:
1. go to sscli\samples\utilities\dnprofiler\profilercallback.cpp in the
Rotor sources and replace the CProfilerCallback::JITCompilationStarted
method with the following code fragment
2. build
3. activate profiling by running "profiling_on" script 
4. run managed application. I have tried running "clix
%TARGETCOMPLUS%\caspol.exe". It printed following for me:

C:\sscli\samples\utilities\dnprofiler>clix %TARGETCOMPLUS%\caspol.exe
Original: 1b 30 03 00 35 00 00 00 03 00 00 11 28 11 00 00 06 28 10 00 00
06 02 8e 69 2d 18 14 7e 1b 00 00 04 72 99 00 00 70 6f 0f 00 00 0a 15 28
2f 00 00 06 2b08 02 0a 06 28 16 00 00 06 de 03 26 de 00 2a 00 00 00 01
10 00 00 00 00 0a 00 27 31 00 03 0b 00 00 02 00
Replaced
Hello world!

-Jan

----------------------------------------------------
void Check(HRESULT hr) {
    if (FAILED(hr)) {
        fprintf(stderr, "FAILED\n");
        exit(1);
    }
}

#if BIGENDIAN
inline UINT32 VAL32(UINT32 x)
{
    return  (x >> 24) |
            ((x >> 8) & 0x0000FF00L) |
            ((x & 0x0000FF00L) << 8) |
            (x << 24);
}
#else
#define VAL32(x) x
#endif

HRESULT CProfilerCallback::JITCompilationStarted(UINT functionId, BOOL
fIsSafeToBlock)
{
    wchar_t wszClass[512];
    wchar_t wszMethod[512];

    if (GetMethodNameFromFunctionId(functionId, wszClass, wszMethod))
    {
        if (wcscmp(L"Main", wszMethod) == 0) {
            ClassID classId = 0;
            ModuleID moduleId = 0;
            mdToken tkMethod = 0;
            IMetaDataEmit* pMetaDataEmit = NULL;
            IMetaDataAssemblyEmit* pMetaDataAssemblyEmit = NULL;
            mdSignature msig = 0;
            IMethodMalloc* pMalloc = NULL;
            PVOID pReplacedMethod;
            LPCBYTE pMethodBytes;
            ULONG cbMethodSize;
            mdModuleRef tkMSCORLIB;
            mdTypeRef tkConsole;
            mdMemberRef tkWriteLine;
            mdString tkString;            
            ULONG i;

            Check(m_pICorProfilerInfo->GetFunctionInfo(
                functionId, &classId, &moduleId, &tkMethod));

            // get the code of the original function
            Check(m_pICorProfilerInfo->GetILFunctionBody(
                moduleId, tkMethod, &pMethodBytes, &cbMethodSize));

            // print the original code of the function
            fprintf(stderr, "Original:");
            for (i = 0; i < cbMethodSize; i++) {
                fprintf(stderr, " %02x", pMethodBytes[i]);
            }
            fprintf(stderr, "\n");

            // replace the method with a simple "Hello world!"
            Check(m_pICorProfilerInfo->GetModuleMetaData(
                moduleId, ofRead | ofWrite, IID_IMetaDataEmit,
(IUnknown** )&pMetaDataEmit ));

 
Check(pMetaDataEmit->QueryInterface(IID_IMetaDataAssemblyEmit,
(void**)&pMetaDataAssemblyEmit));

            // get token of the Console.WriteLine(string)
            ASSEMBLYMETADATA amd;
            ZeroMemory(&amd, sizeof(amd));
            amd.usMajorVersion = 1;
            amd.usMinorVersion = 0;
            amd.usBuildNumber = 3300;
            amd.usRevisionNumber = 0;
            BYTE keyECMA[] = { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0,
0x89 };
            Check(pMetaDataAssemblyEmit->DefineAssemblyRef(keyECMA,
sizeof(keyECMA),
                L"mscorlib", &amd, NULL, 0, 0, &tkMSCORLIB));
            Check(pMetaDataEmit->DefineTypeRefByName(tkMSCORLIB,
L"System.Console", &tkConsole));
            BYTE Sig_void_String[] = { 
                0, // IMAGE_CEE_CS_CALLCONV_DEFAULT
                0x1, // argument count
                0x1, // ret = ELEMENT_TYPE_VOID
                0xe, // arg1 = ELEMENT_TYPE_STRING
            };                              
            Check(pMetaDataEmit->DefineMemberRef(tkConsole,
L"WriteLine",
                Sig_void_String, sizeof(Sig_void_String),
&tkWriteLine));

            // get token of "Hello world!" string
            LPCWSTR szString = L"Hello world!";
            Check(pMetaDataEmit->DefineUserString(szString,
wcslen(szString), &tkString));

#include <pshpack1.h>
            struct {
                BYTE Flags_CodeSize;
                BYTE ldstr; DWORD str_token;
                BYTE call; DWORD method_token;
                BYTE ret;
            } ILCode;
#include <poppack.h>

            ILCode.Flags_CodeSize = 0x42;
            ILCode.ldstr = 0x72;
            ILCode.str_token = VAL32(tkString);
            ILCode.call = 0x28;
            ILCode.method_token = VAL32(tkWriteLine);
            ILCode.ret = 0x2A;

            Check(m_pICorProfilerInfo->GetILFunctionBodyAllocator(
moduleId, &pMalloc ));
            pReplacedMethod = pMalloc->Alloc(sizeof(ILCode));

            memcpy(pReplacedMethod, &ILCode, sizeof(ILCode));

            Check(m_pICorProfilerInfo->SetILFunctionBody(
                moduleId, tkMethod, (LPCBYTE)pReplacedMethod));

            pMalloc->Release();
            pMetaDataEmit->Release();
            pMetaDataAssemblyEmit->Release();

            fprintf(stderr, "Replaced\n");
        }

        ProfilerPrintf("JITCompilationStarted:
%ls::%ls\n",wszClass,wszMethod);
    }
    else
    {
        ProfilerPrintf( "JITCompilationStarted\n" );
    }

    ChangeNestingLevel( 1 );
    return S_OK;
}
----------------------------------------------------

This posting is provided "AS IS" with no warranties, and confers no
rights.

Reply via email to