// profiler.cpp : implementation for a minimum profiler, so it can be used for decrypting protected .NET assemblies
//
#include <windows.h>

#include <stdio.h>
#include <limits.h>
#include <malloc.h>
#include <winreg.h>

#include "cor.h"
#include "corhdr.h"
#include "corhlpr.h"
#include "corerror.h"
#include "corprof.h"

#include "profiler.h"
#include "dump.h"

#define MAX_LENGTH 1024
#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))

Profiler *g_pCallbackObject;        // global reference to callback object

// the result xml file to store dumped assembly info
FILE *resultXmlFile = NULL;

// the simple exe file name, not full path
char simpleExeFileName[MAX_PATH+1]; 

// the default dump directory
char dumpDir[MAX_PATH+1] = "dumped";;

Profiler::Profiler() :
m_refCount(0),
m_dwShutdown(0)
{
	g_pCallbackObject = this;

	// result file is saved into exe.xml in the same path as the executable
	char filename[MAX_PATH+1];
	GetModuleFileNameA(GetModuleHandle(NULL), filename, MAX_PATH);	
	
	char *p = strrchr(filename, '\\');
	if (p) 
		p++;
	else
		p = filename;

	strcpy(simpleExeFileName, p);

	CreateDirectoryA(dumpDir, NULL);

	sprintf(filename, "%s\\%s.xml", dumpDir, simpleExeFileName);
	resultXmlFile = fopen(filename, "wb");
	
	fprintf(resultXmlFile, "<?xml version='1.0'?>\r\n");
	fprintf(resultXmlFile, "<modules>\r\n");
}

Profiler::~Profiler()
{
	fprintf(resultXmlFile, "</modules>\r\n");
	fclose(resultXmlFile);
}

HRESULT Profiler::CreateObject( REFIID riid, void **ppInterface )
{
    HRESULT hr = E_NOINTERFACE;    
     	
    *ppInterface = NULL;
    if ( (riid == IID_IUnknown)
        || 
		(riid == IID_ICorProfilerCallback) 
#ifdef __ICorProfilerCallback2_INTERFACE_DEFINED__
        || (riid == IID_ICorProfilerCallback2) 
#endif
		)
    {                                  
        Profiler * pProfilerCallback = new Profiler();
        if ( pProfilerCallback != NULL )
        {
            hr = S_OK;
            
            pProfilerCallback->AddRef();
            *ppInterface = static_cast<ICorProfilerCallback *>( pProfilerCallback );
        }
        else
            hr = E_OUTOFMEMORY;
    }        

	printf("Profiler::CreateObject(), hr=0x%x\n", hr);

    return hr;
}

/*********** IUnknown methods *************/

ULONG Profiler::AddRef() 
{
    return InterlockedIncrement( &m_refCount );
}

ULONG Profiler::Release() 
{
    long refCount;

    refCount = InterlockedDecrement( &m_refCount );
    if ( refCount == 0 )
        delete this;
     
    return refCount;
}

HRESULT Profiler::QueryInterface( REFIID riid, void **ppInterface )
{
    if ( riid == IID_IUnknown )
        *ppInterface = static_cast<IUnknown *>( this ); 

    else if ( riid == IID_ICorProfilerCallback )
        *ppInterface = static_cast<ICorProfilerCallback *>( this );
#ifdef __ICorProfilerCallback2_INTERFACE_DEFINED__
    else if ( riid == IID_ICorProfilerCallback2 )
        *ppInterface = static_cast<ICorProfilerCallback2 *>( this );
#endif // __ICorProfilerCallback2_INTERFACE_DEFINED__
    else
    {
        *ppInterface = NULL;
        return E_NOINTERFACE;
    }
    
    reinterpret_cast<IUnknown *>( *ppInterface )->AddRef();

    return S_OK;
}
/*********** End of IUnknown methods *************/

/*********** ICorProfilerCallback methods *************/

//
// STARTUP/SHUTDOWN EVENTS
//
HRESULT Profiler::Initialize( IUnknown *pICorProfilerInfoUnk )
{	
    // mask for assembly/module information    
    DWORD dwEventMask = (DWORD) COR_PRF_MONITOR_MODULE_LOADS 
						| (DWORD) COR_PRF_MONITOR_ASSEMBLY_LOADS;

	HRESULT hr = pICorProfilerInfoUnk->QueryInterface( IID_ICorProfilerInfo,
                                               (void **)&m_pProfilerInfo );

	if ( SUCCEEDED( hr ) )
    {
        hr = m_pProfilerInfo->SetEventMask( dwEventMask );
	}

#ifdef __ICorProfilerInfo2_INTERFACE_DEFINED__   
    hr = pICorProfilerInfoUnk->QueryInterface( IID_ICorProfilerInfo2,
                                                   (void **)&m_pProfilerInfo2 );    
#endif // __ICorProfilerInfo2_INTERFACE_DEFINED__

  
	return S_OK;
}
 
HRESULT Profiler::Shutdown()
{
	m_dwShutdown++;
	return S_OK;
}

HRESULT Profiler::DllDetachShutdown()
{
    //
    // If no shutdown occurs during DLL_DETACH, release the callback
    // interface pointer. This scenario will more than likely occur
    // with any interop related program (e.g., a program that is 
    // comprised of both managed and unmanaged components).
    //
    m_dwShutdown++;
    if ( (m_dwShutdown == 1) && (g_pCallbackObject != NULL) )
    {
        g_pCallbackObject->Release();   
        g_pCallbackObject = NULL;
    }
    
    return S_OK;
}

//
// APPLICATION DOMAIN EVENTS
//
HRESULT Profiler::AppDomainCreationStarted( AppDomainID appDomainID )
{
	return S_OK;
}

HRESULT Profiler::AppDomainCreationFinished( AppDomainID appDomainID,
                                                 HRESULT hrStatus )
{
	return S_OK;
}

HRESULT Profiler::AppDomainShutdownStarted( AppDomainID appDomainID )
{
	return S_OK;
}

HRESULT Profiler::AppDomainShutdownFinished( AppDomainID appDomainID, 
                                                 HRESULT hrStatus )
{
	return S_OK;
}

//
// ASSEMBLY EVENTS
//
HRESULT Profiler::AssemblyLoadStarted( AssemblyID assemblyID )
{
	printf("AssemblyLoadStarted, assemblyID=0x%x\n", assemblyID);
	return S_OK;
}

HRESULT Profiler::AssemblyLoadFinished( AssemblyID assemblyID,
                                            HRESULT hrStatus )
{
	printf("AssemblyLoadFinished, assemblyID=0x%x\n", assemblyID);
	return S_OK;
}

HRESULT Profiler::AssemblyUnloadStarted( AssemblyID assemblyID )
{
	printf("AssemblyUnloadStarted, assemblyID=0x%x\n", assemblyID);
	return S_OK;
}

HRESULT Profiler::AssemblyUnloadFinished( AssemblyID assemblyID, 
                                              HRESULT hrStatus )
{
	printf("AssemblyUnloadFinished, assemblyID=0x%x\n", assemblyID);
	return S_OK;
}


//
// MODULE EVENTS
//
HRESULT Profiler::ModuleLoadStarted( ModuleID moduleID )
{	
	printf("ModuleLoadStarted, moduleID=0x%x\n", moduleID);
	return S_OK;
}

HRESULT Profiler::ModuleLoadFinished( ModuleID moduleID,
                                          HRESULT hrStatus )
{	
	PBYTE pBaseLoadAddress;
	ULONG size;
    WCHAR name[2048] = L"";
	AssemblyID assemblyId;
	WCHAR moduleName[2048] = L"";
	IMetaDataImport *pImport = NULL;

	HRESULT hr = m_pProfilerInfo->GetModuleInfo(
						moduleID, (LPCBYTE *)&pBaseLoadAddress,
						2048, &size, name,       
						&assemblyId
					);

	__try {
		// let's determine the module name from metadata
		hr = m_pProfilerInfo->GetModuleMetaData(moduleID, 0, IID_IMetaDataImport, (IUnknown**) &pImport);
		if (SUCCEEDED(hr)) {			
			GUID		mvid;		
			ULONG		nameLen = 0;
			hr = pImport->GetScopeProps(moduleName, 2048, &nameLen, &mvid);
		}
	} __except(EXCEPTION_EXECUTE_HANDLER) {
	}

	printf("ModuleLoadFinished, moduleID=0x%x, name=%ls, pBaseLoadAddress=0x%x, pImport=0x%x, moduleName=%ls\n", moduleID, name, pBaseLoadAddress, pImport, moduleName);
	if (name[0] == 0)
		SaveFile(pBaseLoadAddress, moduleName);
	else
		SaveFile(pBaseLoadAddress, name);
	return S_OK;
}

HRESULT Profiler::ModuleUnloadStarted( ModuleID moduleID )
{
	return S_OK;
}

HRESULT Profiler::ModuleUnloadFinished( ModuleID moduleID, 
                                            HRESULT hrStatus )
{
	return S_OK;
}

HRESULT Profiler::ModuleAttachedToAssembly( ModuleID moduleID,
                                                AssemblyID assemblyID )
{
	return S_OK;
}
        

//
// CLASS EVENTS
//
HRESULT Profiler::ClassLoadStarted( ClassID classID )
{
	return S_OK;
}

HRESULT Profiler::ClassLoadFinished( ClassID classID,
                                         HRESULT hrStatus )
{
	return S_OK;
}

HRESULT Profiler::ClassUnloadStarted( ClassID classID )
{
	return S_OK;
}

HRESULT Profiler::ClassUnloadFinished( ClassID classID, 
                                           HRESULT hrStatus )
{
	return S_OK;
}

HRESULT Profiler::FunctionUnloadStarted( FunctionID functionID )
{
	return S_OK;
}

//
// JIT EVENTS
//              
HRESULT Profiler::JITCompilationStarted( FunctionID functionID,
                                             BOOL fIsSafeToBlock )
{
	return S_OK;
}
                                
HRESULT Profiler::JITCompilationFinished( FunctionID functionID,
                                              HRESULT hrStatus,
                                              BOOL fIsSafeToBlock )
{
	return S_OK;
}

HRESULT Profiler::JITCachedFunctionSearchStarted( FunctionID functionID,
                                                      BOOL *pbUseCachedFunction )
{
	return S_OK;
}

HRESULT Profiler::JITCachedFunctionSearchFinished( FunctionID functionID,
                                                       COR_PRF_JIT_CACHE result )
{
	return S_OK;
}
                                                             
HRESULT Profiler::JITFunctionPitched( FunctionID functionID )
{
	return S_OK;
}

HRESULT Profiler::JITInlining( FunctionID callerID,
                                   FunctionID calleeID,
                                   BOOL *pfShouldInline )
{
	return S_OK;
}

//
// THREAD EVENTS
//
HRESULT Profiler::ThreadCreated( ThreadID threadID )
{
	return S_OK;
}

HRESULT Profiler::ThreadDestroyed( ThreadID threadID )
{
	return S_OK;
}

HRESULT Profiler::ThreadAssignedToOSThread( ThreadID managedThreadID,
                                                DWORD osThreadID )
{
	return S_OK;
}

//
// REMOTING EVENTS
//                                                      

//
// Client-side events
//
HRESULT Profiler::RemotingClientInvocationStarted()
{
	return S_OK;
}

HRESULT Profiler::RemotingClientSendingMessage( GUID *pCookie,
                                                    BOOL fIsAsync )
{
	return S_OK;
}

HRESULT Profiler::RemotingClientReceivingReply( GUID *pCookie,
                                                    BOOL fIsAsync )
{
	return S_OK;
}

HRESULT Profiler::RemotingClientInvocationFinished()
{
	return S_OK;
}

//
// Server-side events
//
HRESULT Profiler::RemotingServerReceivingMessage( GUID *pCookie,
                                                      BOOL fIsAsync )
{
	return S_OK;
}

HRESULT Profiler::RemotingServerInvocationStarted()
{
	return S_OK;
}

HRESULT Profiler::RemotingServerInvocationReturned()
{
	return S_OK;
}

HRESULT Profiler::RemotingServerSendingReply( GUID *pCookie,
                                                  BOOL fIsAsync )
{
	return S_OK;
}

//
// CONTEXT EVENTS
//                                                      
HRESULT Profiler::UnmanagedToManagedTransition( FunctionID functionID,
                                                    COR_PRF_TRANSITION_REASON reason )
{
	return S_OK;
}

HRESULT Profiler::ManagedToUnmanagedTransition( FunctionID functionID,
                                                    COR_PRF_TRANSITION_REASON reason )
{
	return S_OK;
}
                                                      
                                                             
//
// SUSPENSION EVENTS
//    
HRESULT Profiler::RuntimeSuspendStarted( COR_PRF_SUSPEND_REASON suspendReason )
{
	return S_OK;
}

HRESULT Profiler::RuntimeSuspendFinished()
{
	return S_OK;
}

HRESULT Profiler::RuntimeSuspendAborted()
{
	return S_OK;
}

HRESULT Profiler::RuntimeResumeStarted()
{
	return S_OK;
}

HRESULT Profiler::RuntimeResumeFinished()
{
	return S_OK;
}

HRESULT Profiler::RuntimeThreadSuspended( ThreadID threadid )
{
	return S_OK;
}

HRESULT Profiler::RuntimeThreadResumed( ThreadID threadid )
{
	return S_OK;
}


//
// GC EVENTS
//    
HRESULT Profiler::MovedReferences( ULONG cmovedObjectIDRanges,
                                       ObjectID oldObjectIDRangeStart[],
                                       ObjectID newObjectIDRangeStart[],
                                       ULONG cObjectIDRangeLength[] )
{
	return S_OK;
}

HRESULT Profiler::SurvivingReferences( ULONG cmovedObjectIDRanges,
                                           ObjectID objectIDRangeStart[],
                                           ULONG cObjectIDRangeLength[] )
{
	return S_OK;
}

HRESULT Profiler::ObjectAllocated( ObjectID objectID,
                                       ClassID classID )
{
	return S_OK;
}

HRESULT Profiler::ObjectsAllocatedByClass( ULONG classCount,
                                               ClassID classIDs[],
                                               ULONG objects[] )
{
	return S_OK;
}

HRESULT Profiler::ObjectReferences( ObjectID objectID,
                                        ClassID classID,
                                        ULONG cObjectRefs,
                                        ObjectID objectRefIDs[] )
{
	return S_OK;
}

HRESULT Profiler::RootReferences( ULONG cRootRefs,
                                      ObjectID rootRefIDs[] )
{
	return S_OK;
}

//
// EXCEPTION EVENTS
//                                                         

// Exception creation
HRESULT Profiler::ExceptionThrown( ObjectID thrownObjectID )
{
	return S_OK;
}

// Search phase
HRESULT Profiler::ExceptionSearchFunctionEnter( FunctionID functionID )
{
	return S_OK;
}

HRESULT Profiler::ExceptionSearchFunctionLeave()
{
	return S_OK;
}

HRESULT Profiler::ExceptionSearchFilterEnter( FunctionID functionID )
{
	return S_OK;
}

HRESULT Profiler::ExceptionSearchFilterLeave()
{
	return S_OK;
}

HRESULT Profiler::ExceptionSearchCatcherFound( FunctionID functionID )
{
	return S_OK;
}

HRESULT Profiler::ExceptionCLRCatcherFound()
{
	return S_OK;
}

HRESULT Profiler::ExceptionCLRCatcherExecute()
{
	return S_OK;
}

HRESULT Profiler::ExceptionOSHandlerEnter( FunctionID functionID )
{
	return S_OK;
}
    
HRESULT Profiler::ExceptionOSHandlerLeave( FunctionID functionID )
{
	return S_OK;
}

// Unwind phase
HRESULT Profiler::ExceptionUnwindFunctionEnter( FunctionID functionID )
{
	return S_OK;
}

HRESULT Profiler::ExceptionUnwindFunctionLeave()
{
	return S_OK;
}

HRESULT Profiler::ExceptionUnwindFinallyEnter( FunctionID functionID )
{
	return S_OK;
}

HRESULT Profiler::ExceptionUnwindFinallyLeave(){
	return S_OK;
}

HRESULT Profiler::ExceptionCatcherEnter( FunctionID functionID,
                                             ObjectID objectID )
{
	return S_OK;
}

HRESULT Profiler::ExceptionCatcherLeave()
{
	return S_OK;
}


//
// COM CLASSIC WRAPPER
//
HRESULT Profiler:: COMClassicVTableCreated( ClassID wrappedClassID,
                                                REFGUID implementedIID,
                                                void *pVTable,
                                                ULONG cSlots )
{
	return S_OK;
}

HRESULT Profiler:: COMClassicVTableDestroyed( ClassID wrappedClassID,
                                                  REFGUID implementedIID,
                                                  void *pVTable )
{
	return S_OK;
}

#ifdef __ICorProfilerCallback2_INTERFACE_DEFINED__

HRESULT Profiler::ThreadNameChanged( 
            /* [in] */ ThreadID threadId,
            /* [in] */ ULONG cchName,
            /* [in] */ WCHAR name[  ])
{
    return S_OK;
} 

HRESULT Profiler::FinalizeableObjectQueued( 
            /* [in] */ DWORD finalizerFlags,
            /* [in] */ ObjectID objectID)
{    
    return S_OK;
}

HRESULT Profiler::RootReferences2( 
            /* [in] */ ULONG cRootRefs,
            /* [size_is][in] */ ObjectID rootRefIds[  ],
            /* [size_is][in] */ COR_PRF_GC_ROOT_KIND rootKinds[  ],
            /* [size_is][in] */ COR_PRF_GC_ROOT_FLAGS rootFlags[  ],
            /* [size_is][in] */ UINT_PTR rootIds[  ])
{   
    return S_OK;
}

HRESULT Profiler::HandleCreated(
            UINT_PTR handleId,
            ObjectID initialObjectId)
{    
    return S_OK;
}

HRESULT Profiler::HandleDestroyed(
            UINT_PTR handleId)
{   
    return S_OK;
}

HRESULT Profiler::GarbageCollectionStarted(
    int cGenerations,
    BOOL generationCollected[],
    COR_PRF_GC_REASON reason)
{    
    return S_OK;
}

HRESULT Profiler::GarbageCollectionFinished()
{   
    return S_OK;
}
#endif // __ICorProfilerCallback2_INTERFACE_DEFINED__

/*********** End of ICorProfilerCallback methods *************/

/* DllMain/ClassFactory */ 
#include "dllmain.hpp"
