Click here to Skip to main content
15,893,904 members
Articles / Programming Languages / C#

Really Easy Logging using IL Rewriting and the .NET Profiling API

Rate me:
Please Sign up or sign in to vote.
4.88/5 (18 votes)
22 Jan 2007CPOL9 min read 78.5K   1.4K   50  
Explains how to insert logging into code at runtime using IL rewriting and the .Net profiling API
#include "StdAfx.h"
#include "ILWriterBase.h"
#include "SmallILWriter.h"
#include "FatILWriter.h"
#include "ConvertedFatILWriter.h"

ILWriterBase *ILWriterBase::CreateILWriter(ICorProfilerInfo *profilerInfo, FunctionInfo *functionInfo)
{	
    LPCBYTE oldMethodBytes;
    ULONG oldMethodSize;
	
    Check(profilerInfo->GetILFunctionBody(functionInfo->GetModuleID(), functionInfo->GetToken(), &oldMethodBytes, &oldMethodSize));

	Diagnostics::GetInstance()->PrintMessage(functionInfo->GetSignatureText());

	if(IsTiny(oldMethodBytes))
	{	
		Diagnostics::GetInstance()->PrintMessage(" -> Tiny Header");

		if(oldMethodSize > (MAX_TINY_FORMAT_SIZE - sizeof(ILCode)))
		{	
			Diagnostics::GetInstance()->PrintMessage(" -> Convertted to fat header");
			return new ConvertedFatILWriter(profilerInfo, functionInfo, oldMethodBytes, oldMethodSize);
		}

		return new SmallILWriter(profilerInfo, functionInfo, oldMethodBytes, oldMethodSize);
	}
	else
	{
		Diagnostics::GetInstance()->PrintMessage(" -> Fat Header");
		return new FatILWriter(profilerInfo, functionInfo, oldMethodBytes, oldMethodSize);
	}
}

ILWriterBase::ILWriterBase(ICorProfilerInfo *profilerInfo, FunctionInfo *functionInfo, LPCBYTE oldMethodBytes, ULONG oldMethodSize)
{
	ILWriterBase::profilerInfo = profilerInfo;
	ILWriterBase::functionInfo = functionInfo;
	ILWriterBase::oldMethodBytes = oldMethodBytes;
	ILWriterBase::oldMethodSize = oldMethodSize;
}

ILWriterBase::~ILWriterBase(void)
{
}

BOOL ILWriterBase::IsTiny(LPCBYTE methodBytes)
{
	return ((COR_ILMETHOD_TINY*) methodBytes)->IsTiny();
}

LPCBYTE ILWriterBase::GetOldMethodBytes()
{
	return oldMethodBytes;
}

ULONG ILWriterBase::GetOldMethodSize()
{
	return oldMethodSize;
}

ULONG ILWriterBase::GetOldHeaderSize()
{
	return GetHeaderSize();
}

ULONG ILWriterBase::GetNewMethodSize()
{
	return GetOldMethodSize() + sizeof(ILCode);
}

ULONG ILWriterBase::GetNewMethodBodySize()
{
	return GetOldMethodBodySize() + sizeof(ILCode);
}

BOOL ILWriterBase::CanRewrite()
{
	return TRUE;
}

void *ILWriterBase::GetNewILBytes()
{
	void* newMethodBytes = AllocateNewMethodBody(functionInfo);
	WriteHeader(newMethodBytes);
	WriteNewIL(newMethodBytes);
	WriteOldIL(newMethodBytes);
	WriteExtra(newMethodBytes);

	Diagnostics::GetInstance()->PrintIL((BYTE*)GetOldMethodBytes(), GetOldMethodSize());
	Diagnostics::GetInstance()->PrintIL((BYTE*)newMethodBytes, GetNewMethodSize());

	return newMethodBytes;
}

void *ILWriterBase::AllocateNewMethodBody(FunctionInfo *functionInfo)
{
	IMethodMalloc* methodMalloc = NULL;
	Check(profilerInfo->GetILFunctionBodyAllocator(functionInfo->GetModuleID(), &methodMalloc));
	void *result = methodMalloc->Alloc(GetNewMethodSize());
	methodMalloc->Release();
	return result;
}

void ILWriterBase::WriteNewIL(void *newMethodBytes)
{
	memcpy((BYTE*)newMethodBytes + GetHeaderSize(), CreateNewIL(), sizeof(ILCode));
}

void ILWriterBase::WriteOldIL(void *newMethodBytes)
{
	memcpy((BYTE*)newMethodBytes + GetHeaderSize() + sizeof(ILCode), (BYTE*)GetOldMethodBytes() + GetOldHeaderSize(), GetOldMethodBodySize());
}

void ILWriterBase::WriteExtra(void* newMethodBytes)
{
}

ILCode *ILWriterBase::CreateNewIL()
{
	IMetaDataEmit* metaDataEmit = NULL;
	Check(profilerInfo->GetModuleMetaData(functionInfo->GetModuleID(), ofRead | ofWrite, IID_IMetaDataEmit, (IUnknown** )&metaDataEmit));
	
	mdTypeRef loggerClassToken;
	Check(metaDataEmit->DefineTypeRefByName(GetAssemblyToken(metaDataEmit), L"Logger.Core.Logger", &loggerClassToken));

	mdMemberRef logMethodToken;	
	const BYTE logSignature[] = {IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, ELEMENT_TYPE_VOID, ELEMENT_TYPE_STRING, }; //calling convention, argument count, return type, arg1 type
	Check(metaDataEmit->DefineMemberRef(loggerClassToken, L"Log", logSignature, sizeof(logSignature), &logMethodToken));

	mdString methodSignatureTextToken;
    Check(metaDataEmit->DefineUserString(functionInfo->GetSignatureText(), wcslen(functionInfo->GetSignatureText()), &methodSignatureTextToken));

	metaDataEmit->Release();

	ILCode *ilCode = new ILCode();
	ilCode->nop = 0x00;
	ilCode->ldstr = 0x72;
	memcpy(ilCode->stringToken, (void*)&methodSignatureTextToken, sizeof(methodSignatureTextToken));
	ilCode->call = 0x28;
	memcpy(ilCode->callToken, (void*)&logMethodToken, sizeof(logMethodToken));

	return ilCode;
}

mdModuleRef ILWriterBase::GetAssemblyToken(IMetaDataEmit* metaDataEmit)
{
	ASSEMBLYMETADATA assemblyMetaData;
	ZeroMemory(&assemblyMetaData, sizeof(assemblyMetaData));
	assemblyMetaData.usMajorVersion = 1;
	assemblyMetaData.usMinorVersion = 0;
	assemblyMetaData.usBuildNumber = 0;
	assemblyMetaData.usRevisionNumber = 0;

	const BYTE keyEMCA[] = { 0xf5, 0xc2, 0x2b, 0x8f, 0xbb, 0x0c, 0x47, 0x97 };
	IMetaDataAssemblyEmit* metaDataAssemblyEmit = NULL;
	mdModuleRef assemblyToken;
	Check(metaDataEmit->QueryInterface(IID_IMetaDataAssemblyEmit, (void**)&metaDataAssemblyEmit));    
	Check(metaDataAssemblyEmit->DefineAssemblyRef(keyEMCA, sizeof(keyEMCA), L"Logger.Core", &assemblyMetaData, NULL, 0, 0, &assemblyToken));
	metaDataAssemblyEmit->Release();

	return assemblyToken;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Scratch Audio
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions