--- /dev/null
+cmake_minimum_required (VERSION 3.14)
+
+project(CorProfiler)
+
+if(NOT WIN32)
+ set(BASE_SOURCES )
+ add_compile_options(-Wno-invalid-noreturn -Wno-pragma-pack -Wno-int-to-pointer-cast -fPIC -fms-extensions -DBIT64 -DPAL_STDCPP_COMPAT -DPLATFORM_UNIX -DHOST_64BIT -std=c++11)
+ add_link_options(--no-undefined -pthread)
+
+ include_directories($ENV{CORECLR_PATH}/src/pal/inc/rt $ENV{CORECLR_PATH}/src/pal/inc $ENV{CORECLR_PATH}/src/inc)
+endif(NOT WIN32)
+
+if (WIN32)
+ set(BASE_SOURCES src/CorProfiler.def)
+endif(WIN32)
+
+include_directories($CORECLR_BIN/inc $ENV{CORECLR_PATH}/src/pal/prebuilt/inc)
+
+set(SOURCES ${BASE_SOURCES} src/ClassFactory.cpp src/CorProfiler.cpp src/dllmain.cpp src/sampler.cpp $ENV{CORECLR_PATH}/src/pal/prebuilt/idl/corprof_i.cpp)
+
+add_library(CorProfiler SHARED ${SOURCES})
\ No newline at end of file
--- /dev/null
+@echo off
+setlocal
+
+if not defined BuildOS (
+ set BuildOS=Windows
+)
+
+if not defined BuildArch (
+ set BuildArch=x64
+)
+
+if not defined BuildType (
+ set BuildType=Debug
+)
+
+if not defined CORECLR_PATH (
+ set CORECLR_PATH=C:/git/runtime/src/coreclr
+)
+
+if not defined CORECLR_BIN (
+ set CORECLR_BIN=C:/git/runtime/artifacts/bin/coreclr/%BuildOS%.%BuildArch%.%BuildType%
+)
+
+set VS_COM_CMD_PATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat"
+
+if not defined VS_CMD_PATH (
+ if exist %VS_COM_CMD_PATH% (
+ set VS_CMD_PATH=%VS_COM_CMD_PATH%
+ ) else (
+ echo No VS developer command prompt detected!
+ goto :EOF
+ )
+)
+
+echo CORECLR_PATH : %CORECLR_PATH%
+echo BuildOS : %BuildOS%
+echo BuildArch : %BuildArch%
+echo BuildType : %BuildType%
+echo VS PATH : %VS_CMD_PATH%
+
+echo.
+echo Building
+
+if not exist bin\ (
+ mkdir bin
+)
+
+pushd bin
+
+cmake -G "Visual Studio 16 2019" ..\ -DCMAKE_BUILD_TYPE=Debug
+
+echo Calling VS Developer Command Prompt to build
+call %VS_CMD_PATH%
+
+msbuild -v:m CorProfiler.sln
+
+popd
+
+echo.
+echo.
+echo.
+echo Done building
+
+echo Copying binary to main directory
+copy /y bin\Debug\CorProfiler.dll .
+
--- /dev/null
+#!/bin/bash
+
+[ -z "${BuildOS:-}" ] && export BuildOS=Linux
+[ -z "${BuildArch:-}" ] && export BuildArch=x64
+[ -z "${BuildType:-}" ] && export BuildType=Debug
+
+[ -z "${CORECLR_PATH:-}" ] && export CORECLR_PATH=~/git/runtime/src/coreclr
+[ -z "${CORECLR_BIN:-}" ] && export CORECLR_BIN=~/git/runtime/artifacts/bin/coreclr/$BuildOS.$BuildArch.$BuildType
+
+printf ' CORECLR_PATH : %s\n' "$CORECLR_PATH"
+printf ' BuildOS : %s\n' "$BuildOS"
+printf ' BuildArch : %s\n' "$BuildArch"
+printf ' BuildType : %s\n' "$BuildType"
+
+printf ' Building ...'
+
+if [ ! -d "bin/" ]; then
+ mkdir bin/
+fi
+
+pushd bin
+
+export CC=/usr/bin/clang
+export CXX=/usr/bin/clang++
+cmake ../ -DCMAKE_BUILD_TYPE=Debug
+
+make -j8
+
+popd
+
+printf ' Copying libCorProfiler.so to main directory\n'
+cp bin/libCorProfiler.so .
+
+printf 'Done.\n'
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "ClassFactory.h"
+#include "CorProfiler.h"
+
+ClassFactory::ClassFactory() : refCount(0)
+{
+}
+
+ClassFactory::~ClassFactory()
+{
+}
+
+HRESULT STDMETHODCALLTYPE ClassFactory::QueryInterface(REFIID riid, void **ppvObject)
+{
+ if (riid == IID_IUnknown || riid == IID_IClassFactory)
+ {
+ *ppvObject = this;
+ this->AddRef();
+ return S_OK;
+ }
+
+ *ppvObject = nullptr;
+ return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE ClassFactory::AddRef()
+{
+ return std::atomic_fetch_add(&this->refCount, 1) + 1;
+}
+
+ULONG STDMETHODCALLTYPE ClassFactory::Release()
+{
+ int count = std::atomic_fetch_sub(&this->refCount, 1) - 1;
+ if (count <= 0)
+ {
+ delete this;
+ }
+
+ return count;
+}
+
+HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
+{
+ if (pUnkOuter != nullptr)
+ {
+ *ppvObject = nullptr;
+ return CLASS_E_NOAGGREGATION;
+ }
+
+ CorProfiler* profiler = new CorProfiler();
+ if (profiler == nullptr)
+ {
+ return E_FAIL;
+ }
+
+ return profiler->QueryInterface(riid, ppvObject);
+}
+
+HRESULT STDMETHODCALLTYPE ClassFactory::LockServer(BOOL fLock)
+{
+ return S_OK;
+}
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#include "unknwn.h"
+#include <atomic>
+
+class ClassFactory : public IClassFactory
+{
+private:
+ std::atomic<int> refCount;
+public:
+ ClassFactory();
+ virtual ~ClassFactory();
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
+ ULONG STDMETHODCALLTYPE AddRef(void) override;
+ ULONG STDMETHODCALLTYPE Release(void) override;
+ HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) override;
+ HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) override;
+};
\ No newline at end of file
--- /dev/null
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include "CorProfiler.h"
+#include "corhlpr.h"
+#include "profiler_pal.h"
+#include "sampler.h"
+#include <string>
+#include <assert.h>
+
+using std::shared_ptr;
+
+CorProfiler::CorProfiler() :
+ refCount(0),
+ corProfilerInfo(nullptr),
+ sampler(),
+ jitEventCount(0)
+{
+
+}
+
+CorProfiler::~CorProfiler()
+{
+ if (this->corProfilerInfo != nullptr)
+ {
+ this->corProfilerInfo->Release();
+ this->corProfilerInfo = nullptr;
+ }
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown *pICorProfilerInfoUnk)
+{
+ printf("Initialize profiler!\n");
+
+ HRESULT hr = pICorProfilerInfoUnk->QueryInterface(IID_ICorProfilerInfo10, (void**)&corProfilerInfo);
+ if (hr != S_OK)
+ {
+ printf("Got HR %X from QI for ICorProfilerInfo4", hr);
+ return E_FAIL;
+ }
+
+ corProfilerInfo->SetEventMask2(COR_PRF_ENABLE_STACK_SNAPSHOT | COR_PRF_MONITOR_JIT_COMPILATION, 0);
+
+ sampler = shared_ptr<Sampler>(new Sampler(corProfilerInfo, this));
+ sampler->Start();
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::Shutdown()
+{
+ if (this->corProfilerInfo != nullptr)
+ {
+ this->corProfilerInfo->Release();
+ this->corProfilerInfo = nullptr;
+ }
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AppDomainCreationStarted(AppDomainID appDomainId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AppDomainCreationFinished(AppDomainID appDomainId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AppDomainShutdownStarted(AppDomainID appDomainId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AppDomainShutdownFinished(AppDomainID appDomainId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AssemblyLoadStarted(AssemblyID assemblyId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AssemblyLoadFinished(AssemblyID assemblyId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AssemblyUnloadStarted(AssemblyID assemblyId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadStarted(ModuleID moduleId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ModuleUnloadStarted(ModuleID moduleId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ModuleUnloadFinished(ModuleID moduleId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ModuleAttachedToAssembly(ModuleID moduleId, AssemblyID AssemblyId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ClassLoadStarted(ClassID classId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ClassLoadFinished(ClassID classId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ClassUnloadStarted(ClassID classId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ClassUnloadFinished(ClassID classId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::FunctionUnloadStarted(FunctionID functionId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::JITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock)
+{
+ ++jitEventCount;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::JITCachedFunctionSearchStarted(FunctionID functionId, BOOL *pbUseCachedFunction)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::JITCachedFunctionSearchFinished(FunctionID functionId, COR_PRF_JIT_CACHE result)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::JITFunctionPitched(FunctionID functionId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::JITInlining(FunctionID callerId, FunctionID calleeId, BOOL *pfShouldInline)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ThreadCreated(ThreadID threadId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ThreadDestroyed(ThreadID threadId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingClientInvocationStarted()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingClientSendingMessage(GUID *pCookie, BOOL fIsAsync)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingClientReceivingReply(GUID *pCookie, BOOL fIsAsync)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingClientInvocationFinished()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingServerReceivingMessage(GUID *pCookie, BOOL fIsAsync)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingServerInvocationStarted()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingServerInvocationReturned()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RemotingServerSendingReply(GUID *pCookie, BOOL fIsAsync)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::UnmanagedToManagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ManagedToUnmanagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RuntimeSuspendFinished()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RuntimeSuspendAborted()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RuntimeResumeStarted()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RuntimeResumeFinished()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RuntimeThreadSuspended(ThreadID threadId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RuntimeThreadResumed(ThreadID threadId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::MovedReferences(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], ULONG cObjectIDRangeLength[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ObjectAllocated(ObjectID objectId, ClassID classId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ObjectsAllocatedByClass(ULONG cClassCount, ClassID classIds[], ULONG cObjects[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ObjectReferences(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RootReferences(ULONG cRootRefs, ObjectID rootRefIds[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionThrown(ObjectID thrownObjectId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFunctionEnter(FunctionID functionId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFunctionLeave()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFilterEnter(FunctionID functionId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchFilterLeave()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionSearchCatcherFound(FunctionID functionId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionOSHandlerEnter(UINT_PTR __unused)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionOSHandlerLeave(UINT_PTR __unused)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionUnwindFunctionEnter(FunctionID functionId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionUnwindFunctionLeave()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionUnwindFinallyEnter(FunctionID functionId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionUnwindFinallyLeave()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionCatcherEnter(FunctionID functionId, ObjectID objectId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionCatcherLeave()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::COMClassicVTableCreated(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable, ULONG cSlots)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::COMClassicVTableDestroyed(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionCLRCatcherFound()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionCLRCatcherExecute()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ThreadNameChanged(ThreadID threadId, ULONG cchName, WCHAR name[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::SurvivingReferences(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], ULONG cObjectIDRangeLength[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::GarbageCollectionFinished()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::FinalizeableObjectQueued(DWORD finalizerFlags, ObjectID objectID)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::RootReferences2(ULONG cRootRefs, ObjectID rootRefIds[], COR_PRF_GC_ROOT_KIND rootKinds[], COR_PRF_GC_ROOT_FLAGS rootFlags[], UINT_PTR rootIds[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::HandleCreated(GCHandleID handleId, ObjectID initialObjectId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::HandleDestroyed(GCHandleID handleId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::InitializeForAttach(IUnknown *pCorProfilerInfoUnk, void *pvClientData, UINT cbClientData)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ProfilerAttachComplete()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ProfilerDetachSucceeded()
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ReJITCompilationStarted(FunctionID functionId, ReJITID rejitId, BOOL fIsSafeToBlock)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::GetReJITParameters(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl *pFunctionControl)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ReJITCompilationFinished(FunctionID functionId, ReJITID rejitId, HRESULT hrStatus, BOOL fIsSafeToBlock)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ReJITError(ModuleID moduleId, mdMethodDef methodId, FunctionID functionId, HRESULT hrStatus)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::MovedReferences2(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], SIZE_T cObjectIDRangeLength[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::SurvivingReferences2(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], SIZE_T cObjectIDRangeLength[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ConditionalWeakTableElementReferences(ULONG cRootRefs, ObjectID keyRefIds[], ObjectID valueRefIds[], GCHandleID rootIds[])
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::GetAssemblyReferences(const WCHAR *wszAssemblyPath, ICorProfilerAssemblyReferenceProvider *pAsmRefProvider)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::ModuleInMemorySymbolsUpdated(ModuleID moduleId)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::DynamicMethodJITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock, LPCBYTE ilHeader, ULONG cbILHeader)
+{
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CorProfiler::DynamicMethodJITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock)
+{
+ return S_OK;
+}
+
+bool CorProfiler::IsRuntimeExecutingManagedCode()
+{
+ return jitEventCount.load() > 0;
+}
--- /dev/null
+LIBRARY CorProfiler
+EXPORTS
+ DllGetClassObject private
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <set>
+#include <mutex>
+#include <vector>
+#include <condition_variable>
+#include "cor.h"
+#include "corprof.h"
+#include "sampler.h"
+
+#define SHORT_LENGTH 32
+#define STRING_LENGTH 256
+#define LONG_LENGTH 1024
+
+template <class MetaInterface>
+class COMPtrHolder
+{
+public:
+ COMPtrHolder()
+ {
+ m_ptr = NULL;
+ }
+
+ COMPtrHolder(MetaInterface* ptr)
+ {
+ if (ptr != NULL)
+ {
+ ptr->AddRef();
+ }
+ m_ptr = ptr;
+ }
+
+ ~COMPtrHolder()
+ {
+ if (m_ptr != NULL)
+ {
+ m_ptr->Release();
+ m_ptr = NULL;
+ }
+ }
+ MetaInterface* operator->()
+ {
+ return m_ptr;
+ }
+
+ MetaInterface** operator&()
+ {
+ // _ASSERT(m_ptr == NULL);
+ return &m_ptr;
+ }
+
+ operator MetaInterface*()
+ {
+ return m_ptr;
+ }
+private:
+ MetaInterface* m_ptr;
+};
+
+class CorProfiler : public ICorProfilerCallback8
+{
+private:
+ std::atomic<int> refCount;
+ std::shared_ptr<Sampler> sampler;
+
+ std::atomic<int> jitEventCount;
+
+public:
+ ICorProfilerInfo10* corProfilerInfo;
+
+ CorProfiler();
+ virtual ~CorProfiler();
+ HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk) override;
+ HRESULT STDMETHODCALLTYPE Shutdown() override;
+ HRESULT STDMETHODCALLTYPE AppDomainCreationStarted(AppDomainID appDomainId) override;
+ HRESULT STDMETHODCALLTYPE AppDomainCreationFinished(AppDomainID appDomainId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE AppDomainShutdownStarted(AppDomainID appDomainId) override;
+ HRESULT STDMETHODCALLTYPE AppDomainShutdownFinished(AppDomainID appDomainId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE AssemblyLoadStarted(AssemblyID assemblyId) override;
+ HRESULT STDMETHODCALLTYPE AssemblyLoadFinished(AssemblyID assemblyId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE AssemblyUnloadStarted(AssemblyID assemblyId) override;
+ HRESULT STDMETHODCALLTYPE AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE ModuleLoadStarted(ModuleID moduleId) override;
+ HRESULT STDMETHODCALLTYPE ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE ModuleUnloadStarted(ModuleID moduleId) override;
+ HRESULT STDMETHODCALLTYPE ModuleUnloadFinished(ModuleID moduleId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE ModuleAttachedToAssembly(ModuleID moduleId, AssemblyID AssemblyId) override;
+ HRESULT STDMETHODCALLTYPE ClassLoadStarted(ClassID classId) override;
+ HRESULT STDMETHODCALLTYPE ClassLoadFinished(ClassID classId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE ClassUnloadStarted(ClassID classId) override;
+ HRESULT STDMETHODCALLTYPE ClassUnloadFinished(ClassID classId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE FunctionUnloadStarted(FunctionID functionId) override;
+ HRESULT STDMETHODCALLTYPE JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock) override;
+ HRESULT STDMETHODCALLTYPE JITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock) override;
+ HRESULT STDMETHODCALLTYPE JITCachedFunctionSearchStarted(FunctionID functionId, BOOL* pbUseCachedFunction) override;
+ HRESULT STDMETHODCALLTYPE JITCachedFunctionSearchFinished(FunctionID functionId, COR_PRF_JIT_CACHE result) override;
+ HRESULT STDMETHODCALLTYPE JITFunctionPitched(FunctionID functionId) override;
+ HRESULT STDMETHODCALLTYPE JITInlining(FunctionID callerId, FunctionID calleeId, BOOL* pfShouldInline) override;
+ HRESULT STDMETHODCALLTYPE ThreadCreated(ThreadID threadId) override;
+ HRESULT STDMETHODCALLTYPE ThreadDestroyed(ThreadID threadId) override;
+ HRESULT STDMETHODCALLTYPE ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId) override;
+ HRESULT STDMETHODCALLTYPE RemotingClientInvocationStarted() override;
+ HRESULT STDMETHODCALLTYPE RemotingClientSendingMessage(GUID* pCookie, BOOL fIsAsync) override;
+ HRESULT STDMETHODCALLTYPE RemotingClientReceivingReply(GUID* pCookie, BOOL fIsAsync) override;
+ HRESULT STDMETHODCALLTYPE RemotingClientInvocationFinished() override;
+ HRESULT STDMETHODCALLTYPE RemotingServerReceivingMessage(GUID* pCookie, BOOL fIsAsync) override;
+ HRESULT STDMETHODCALLTYPE RemotingServerInvocationStarted() override;
+ HRESULT STDMETHODCALLTYPE RemotingServerInvocationReturned() override;
+ HRESULT STDMETHODCALLTYPE RemotingServerSendingReply(GUID* pCookie, BOOL fIsAsync) override;
+ HRESULT STDMETHODCALLTYPE UnmanagedToManagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason) override;
+ HRESULT STDMETHODCALLTYPE ManagedToUnmanagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason) override;
+ HRESULT STDMETHODCALLTYPE RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason) override;
+ HRESULT STDMETHODCALLTYPE RuntimeSuspendFinished() override;
+ HRESULT STDMETHODCALLTYPE RuntimeSuspendAborted() override;
+ HRESULT STDMETHODCALLTYPE RuntimeResumeStarted() override;
+ HRESULT STDMETHODCALLTYPE RuntimeResumeFinished() override;
+ HRESULT STDMETHODCALLTYPE RuntimeThreadSuspended(ThreadID threadId) override;
+ HRESULT STDMETHODCALLTYPE RuntimeThreadResumed(ThreadID threadId) override;
+ HRESULT STDMETHODCALLTYPE MovedReferences(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], ULONG cObjectIDRangeLength[]) override;
+ HRESULT STDMETHODCALLTYPE ObjectAllocated(ObjectID objectId, ClassID classId) override;
+ HRESULT STDMETHODCALLTYPE ObjectsAllocatedByClass(ULONG cClassCount, ClassID classIds[], ULONG cObjects[]) override;
+ HRESULT STDMETHODCALLTYPE ObjectReferences(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[]) override;
+ HRESULT STDMETHODCALLTYPE RootReferences(ULONG cRootRefs, ObjectID rootRefIds[]) override;
+ HRESULT STDMETHODCALLTYPE ExceptionThrown(ObjectID thrownObjectId) override;
+ HRESULT STDMETHODCALLTYPE ExceptionSearchFunctionEnter(FunctionID functionId) override;
+ HRESULT STDMETHODCALLTYPE ExceptionSearchFunctionLeave() override;
+ HRESULT STDMETHODCALLTYPE ExceptionSearchFilterEnter(FunctionID functionId) override;
+ HRESULT STDMETHODCALLTYPE ExceptionSearchFilterLeave() override;
+ HRESULT STDMETHODCALLTYPE ExceptionSearchCatcherFound(FunctionID functionId) override;
+ HRESULT STDMETHODCALLTYPE ExceptionOSHandlerEnter(UINT_PTR __unused) override;
+ HRESULT STDMETHODCALLTYPE ExceptionOSHandlerLeave(UINT_PTR __unused) override;
+ HRESULT STDMETHODCALLTYPE ExceptionUnwindFunctionEnter(FunctionID functionId) override;
+ HRESULT STDMETHODCALLTYPE ExceptionUnwindFunctionLeave() override;
+ HRESULT STDMETHODCALLTYPE ExceptionUnwindFinallyEnter(FunctionID functionId) override;
+ HRESULT STDMETHODCALLTYPE ExceptionUnwindFinallyLeave() override;
+ HRESULT STDMETHODCALLTYPE ExceptionCatcherEnter(FunctionID functionId, ObjectID objectId) override;
+ HRESULT STDMETHODCALLTYPE ExceptionCatcherLeave() override;
+ HRESULT STDMETHODCALLTYPE COMClassicVTableCreated(ClassID wrappedClassId, REFGUID implementedIID, void* pVTable, ULONG cSlots) override;
+ HRESULT STDMETHODCALLTYPE COMClassicVTableDestroyed(ClassID wrappedClassId, REFGUID implementedIID, void* pVTable) override;
+ HRESULT STDMETHODCALLTYPE ExceptionCLRCatcherFound() override;
+ HRESULT STDMETHODCALLTYPE ExceptionCLRCatcherExecute() override;
+ HRESULT STDMETHODCALLTYPE ThreadNameChanged(ThreadID threadId, ULONG cchName, WCHAR name[]) override;
+ HRESULT STDMETHODCALLTYPE GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason) override;
+ HRESULT STDMETHODCALLTYPE SurvivingReferences(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], ULONG cObjectIDRangeLength[]) override;
+ HRESULT STDMETHODCALLTYPE GarbageCollectionFinished() override;
+ HRESULT STDMETHODCALLTYPE FinalizeableObjectQueued(DWORD finalizerFlags, ObjectID objectID) override;
+ HRESULT STDMETHODCALLTYPE RootReferences2(ULONG cRootRefs, ObjectID rootRefIds[], COR_PRF_GC_ROOT_KIND rootKinds[], COR_PRF_GC_ROOT_FLAGS rootFlags[], UINT_PTR rootIds[]) override;
+ HRESULT STDMETHODCALLTYPE HandleCreated(GCHandleID handleId, ObjectID initialObjectId) override;
+ HRESULT STDMETHODCALLTYPE HandleDestroyed(GCHandleID handleId) override;
+ HRESULT STDMETHODCALLTYPE InitializeForAttach(IUnknown* pCorProfilerInfoUnk, void* pvClientData, UINT cbClientData) override;
+ HRESULT STDMETHODCALLTYPE ProfilerAttachComplete() override;
+ HRESULT STDMETHODCALLTYPE ProfilerDetachSucceeded() override;
+ HRESULT STDMETHODCALLTYPE ReJITCompilationStarted(FunctionID functionId, ReJITID rejitId, BOOL fIsSafeToBlock) override;
+ HRESULT STDMETHODCALLTYPE GetReJITParameters(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl* pFunctionControl) override;
+ HRESULT STDMETHODCALLTYPE ReJITCompilationFinished(FunctionID functionId, ReJITID rejitId, HRESULT hrStatus, BOOL fIsSafeToBlock) override;
+ HRESULT STDMETHODCALLTYPE ReJITError(ModuleID moduleId, mdMethodDef methodId, FunctionID functionId, HRESULT hrStatus) override;
+ HRESULT STDMETHODCALLTYPE MovedReferences2(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], SIZE_T cObjectIDRangeLength[]) override;
+ HRESULT STDMETHODCALLTYPE SurvivingReferences2(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], SIZE_T cObjectIDRangeLength[]) override;
+ HRESULT STDMETHODCALLTYPE ConditionalWeakTableElementReferences(ULONG cRootRefs, ObjectID keyRefIds[], ObjectID valueRefIds[], GCHandleID rootIds[]) override;
+ HRESULT STDMETHODCALLTYPE GetAssemblyReferences(const WCHAR* wszAssemblyPath, ICorProfilerAssemblyReferenceProvider* pAsmRefProvider) override;
+ HRESULT STDMETHODCALLTYPE ModuleInMemorySymbolsUpdated(ModuleID moduleId) override;
+
+ HRESULT STDMETHODCALLTYPE DynamicMethodJITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock, LPCBYTE ilHeader, ULONG cbILHeader) override;
+ HRESULT STDMETHODCALLTYPE DynamicMethodJITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock) override;
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override
+ {
+ if (riid == __uuidof(ICorProfilerCallback8) ||
+ riid == __uuidof(ICorProfilerCallback7) ||
+ riid == __uuidof(ICorProfilerCallback6) ||
+ riid == __uuidof(ICorProfilerCallback5) ||
+ riid == __uuidof(ICorProfilerCallback4) ||
+ riid == __uuidof(ICorProfilerCallback3) ||
+ riid == __uuidof(ICorProfilerCallback2) ||
+ riid == __uuidof(ICorProfilerCallback) ||
+ riid == IID_IUnknown)
+ {
+ *ppvObject = this;
+ this->AddRef();
+ return S_OK;
+ }
+
+ *ppvObject = nullptr;
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef(void) override
+ {
+ return std::atomic_fetch_add(&this->refCount, 1) + 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release(void) override
+ {
+ int count = std::atomic_fetch_sub(&this->refCount, 1) - 1;
+
+ if (count <= 0)
+ {
+ delete this;
+ }
+
+ return count;
+ }
+
+ bool IsRuntimeExecutingManagedCode();
+};
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "ClassFactory.h"
+
+const IID IID_IUnknown = { 0x00000000, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
+
+const IID IID_IClassFactory = { 0x00000001, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
+
+BOOL STDMETHODCALLTYPE DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ return TRUE;
+}
+
+extern "C" HRESULT STDMETHODCALLTYPE DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
+{
+ // {cf0d821e-299b-5307-a3d8-b283c03916dd}
+ const GUID CLSID_CorProfiler = { 0xcf0d821e, 0x299b, 0x5307, { 0xa3, 0xd8, 0xb2, 0x83, 0xc0, 0x39, 0x16, 0xdd } };
+
+ if (ppv == nullptr || rclsid != CLSID_CorProfiler)
+ {
+ return E_FAIL;
+ }
+
+ auto factory = new ClassFactory;
+ if (factory == nullptr)
+ {
+ return E_FAIL;
+ }
+
+ return factory->QueryInterface(riid, ppv);
+}
+
+extern "C" HRESULT STDMETHODCALLTYPE DllCanUnloadNow()
+{
+ return S_OK;
+}
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#ifndef WIN32
+#include <cstdlib>
+#include "pal_mstypes.h"
+#include "pal.h"
+#include "ntimage.h"
+#include "corhdr.h"
+
+#define CoTaskMemAlloc(cb) malloc(cb)
+#define CoTaskMemFree(cb) free(cb)
+
+#define UINT_PTR_FORMAT "lx"
+
+#define PROFILER_STUB __attribute__((visibility("hidden"))) EXTERN_C void STDMETHODCALLTYPE
+
+#else
+#define PROFILER_STUB EXTERN_C void STDMETHODCALLTYPE
+#define UINT_PTR_FORMAT "llx"
+#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "CorProfiler.h"
+#include "sampler.h"
+#include <thread>
+#include <cwchar>
+#include <cstdio>
+#include <cinttypes>
+#include <locale>
+#include <codecvt>
+
+using std::wstring_convert;
+using std::codecvt_utf8;
+using std::string;
+
+ManualEvent Sampler::s_waitEvent;
+Sampler *Sampler::s_instance = nullptr;
+
+HRESULT __stdcall DoStackSnapshotStackSnapShotCallbackWrapper(
+ FunctionID funcId,
+ UINT_PTR ip,
+ COR_PRF_FRAME_INFO frameInfo,
+ ULONG32 contextSize,
+ BYTE context[],
+ void* clientData)
+{
+ return Sampler::Instance()->StackSnapshotCallback(funcId,
+ ip,
+ frameInfo,
+ contextSize,
+ context,
+ clientData);
+}
+
+Sampler::Sampler(ICorProfilerInfo10* pProfInfo, CorProfiler *parent) :
+ m_workerThread(DoSampling, pProfInfo, parent)
+{
+ Sampler::s_instance = this;
+}
+
+// static
+void Sampler::DoSampling(ICorProfilerInfo10 *pProfInfo, CorProfiler *parent)
+{
+ Sampler::Instance()->corProfilerInfo = parent->corProfilerInfo;
+
+ pProfInfo->InitializeCurrentThread();
+
+ while (true)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ s_waitEvent.Wait();
+
+ if (!parent->IsRuntimeExecutingManagedCode())
+ {
+ printf("Runtime has not started executing managed code yet.\n");
+ continue;
+ }
+
+ printf("Suspending runtime\n");
+ HRESULT hr = pProfInfo->SuspendRuntime();
+ if (FAILED(hr))
+ {
+ printf("Error suspending runtime... hr=0x%x \n", hr);
+ continue;
+ }
+
+ ICorProfilerThreadEnum* threadEnum = nullptr;
+ hr = pProfInfo->EnumThreads(&threadEnum);
+ if (FAILED(hr))
+ {
+ printf("Error getting thread enumerator\n");
+ continue;
+ }
+
+ ThreadID threadID;
+ ULONG numReturned;
+ while ((hr = threadEnum->Next(1, &threadID, &numReturned)) == S_OK)
+ {
+ printf("Starting stack walk for managed thread id=0x%" PRIx64 "\n", (uint64_t)threadID);
+
+ hr = pProfInfo->DoStackSnapshot(threadID,
+ DoStackSnapshotStackSnapShotCallbackWrapper,
+ COR_PRF_SNAPSHOT_REGISTER_CONTEXT,
+ NULL,
+ NULL,
+ 0);
+ if (FAILED(hr))
+ {
+ if (hr == E_FAIL)
+ {
+ printf("Managed thread id=0x%" PRIx64 " has no managed frames to walk \n", (uint64_t)threadID);
+ }
+ else
+ {
+ printf("DoStackSnapshot for thread id=0x%" PRIx64 " failed with hr=0x%x \n", (uint64_t)threadID, hr);
+ }
+ }
+
+ printf("Ending stack walk for managed thread id=0x%" PRIx64 "\n", (uint64_t)threadID);
+ }
+
+ printf("Resuming runtime\n");
+ hr = pProfInfo->ResumeRuntime();
+ if (FAILED(hr))
+ {
+ printf("ResumeRuntime failed with hr=0x%x \n", hr);
+ }
+ }
+}
+
+void Sampler::Start()
+{
+ s_waitEvent.Signal();
+}
+
+void Sampler::Stop()
+{
+ s_waitEvent.Reset();
+}
+
+HRESULT Sampler::StackSnapshotCallback(FunctionID funcId, UINT_PTR ip, COR_PRF_FRAME_INFO frameInfo, ULONG32 contextSize, BYTE context[], void* clientData)
+{
+ WSTRING functionName = GetFunctionName(funcId, frameInfo);
+
+#if WIN32
+ wstring_convert<codecvt_utf8<wchar_t>, wchar_t> convert;
+#else // WIN32
+ wstring_convert<codecvt_utf8<char16_t>, char16_t> convert;
+#endif // WIN32
+
+ string printable = convert.to_bytes(functionName);
+ printf(" %s (funcId=0x%" PRIx64 ")\n", printable.c_str(), (uint64_t)funcId);
+ return S_OK;
+}
+
+WSTRING Sampler::GetModuleName(ModuleID modId)
+{
+ WCHAR moduleFullName[STRING_LENGTH];
+ ULONG nameLength = 0;
+ AssemblyID assemID;
+
+ if (modId == NULL)
+ {
+ printf("NULL modId passed to GetModuleName\n");
+ return WSTR("Unknown");
+ }
+
+ HRESULT hr = corProfilerInfo->GetModuleInfo(modId,
+ NULL,
+ STRING_LENGTH,
+ &nameLength,
+ moduleFullName,
+ &assemID);
+ if (FAILED(hr))
+ {
+ printf("GetModuleInfo failed with hr=0x%x\n", hr);
+ return WSTR("Unknown");
+ }
+
+ WCHAR *ptr = NULL;
+ WCHAR *index = moduleFullName;
+ // Find the last occurence of the \ character
+ while (*index != 0)
+ {
+ if (*index == '\\' || *index == '/')
+ {
+ ptr = index;
+ }
+
+ ++index;
+ }
+
+ if (ptr == NULL)
+ {
+ return moduleFullName;
+ }
+ // Skip the last \ in the string
+ ++ptr;
+
+ WSTRING moduleName;
+ while (*ptr != 0)
+ {
+ moduleName += *ptr;
+ ++ptr;
+ }
+
+ return moduleName;
+}
+
+
+WSTRING Sampler::GetClassName(ClassID classId)
+{
+ ModuleID modId;
+ mdTypeDef classToken;
+ ClassID parentClassID;
+ ULONG32 nTypeArgs;
+ ClassID typeArgs[SHORT_LENGTH];
+ HRESULT hr = S_OK;
+
+ if (classId == NULL)
+ {
+ printf("NULL classId passed to GetClassName\n");
+ return WSTR("Unknown");
+ }
+
+ hr = corProfilerInfo->GetClassIDInfo2(classId,
+ &modId,
+ &classToken,
+ &parentClassID,
+ SHORT_LENGTH,
+ &nTypeArgs,
+ typeArgs);
+ if (CORPROF_E_CLASSID_IS_ARRAY == hr)
+ {
+ // We have a ClassID of an array.
+ return WSTR("ArrayClass");
+ }
+ else if (CORPROF_E_CLASSID_IS_COMPOSITE == hr)
+ {
+ // We have a composite class
+ return WSTR("CompositeClass");
+ }
+ else if (CORPROF_E_DATAINCOMPLETE == hr)
+ {
+ // type-loading is not yet complete. Cannot do anything about it.
+ return WSTR("DataIncomplete");
+ }
+ else if (FAILED(hr))
+ {
+ printf("GetClassIDInfo returned hr=0x%x for classID=0x%" PRIx64 "\n", hr, (uint64_t)classId);
+ return WSTR("Unknown");
+ }
+
+ COMPtrHolder<IMetaDataImport> pMDImport;
+ hr = corProfilerInfo->GetModuleMetaData(modId,
+ (ofRead | ofWrite),
+ IID_IMetaDataImport,
+ (IUnknown **)&pMDImport );
+ if (FAILED(hr))
+ {
+ printf("GetModuleMetaData failed with hr=0x%x\n", hr);
+ return WSTR("Unknown");
+ }
+
+
+ WCHAR wName[LONG_LENGTH];
+ DWORD dwTypeDefFlags = 0;
+ hr = pMDImport->GetTypeDefProps(classToken,
+ wName,
+ LONG_LENGTH,
+ NULL,
+ &dwTypeDefFlags,
+ NULL);
+ if (FAILED(hr))
+ {
+ printf("GetTypeDefProps failed with hr=0x%x\n", hr);
+ return WSTR("Unknown");
+ }
+
+ WSTRING name = GetModuleName(modId);
+ name += WSTR(" ");
+ name += wName;
+
+ if (nTypeArgs > 0)
+ {
+ name += WSTR("<");
+ }
+
+ for(ULONG32 i = 0; i < nTypeArgs; i++)
+ {
+ name += GetClassName(typeArgs[i]);
+
+ if ((i + 1) != nTypeArgs)
+ {
+ name += WSTR(", ");
+ }
+ }
+
+ if (nTypeArgs > 0)
+ {
+ name += WSTR(">");
+ }
+
+ return name;
+}
+
+WSTRING Sampler::GetFunctionName(FunctionID funcID, const COR_PRF_FRAME_INFO frameInfo)
+{
+ if (funcID == NULL)
+ {
+ return WSTR("Unknown_Native_Function");
+ }
+
+ ClassID classId = NULL;
+ ModuleID moduleId = NULL;
+ mdToken token = NULL;
+ ULONG32 nTypeArgs = NULL;
+ ClassID typeArgs[SHORT_LENGTH];
+
+ HRESULT hr = corProfilerInfo->GetFunctionInfo2(funcID,
+ frameInfo,
+ &classId,
+ &moduleId,
+ &token,
+ SHORT_LENGTH,
+ &nTypeArgs,
+ typeArgs);
+ if (FAILED(hr))
+ {
+ printf("GetFunctionInfo2 failed with hr=0x%x\n", hr);
+ }
+
+ COMPtrHolder<IMetaDataImport> pIMDImport;
+ hr = corProfilerInfo->GetModuleMetaData(moduleId,
+ ofRead,
+ IID_IMetaDataImport,
+ (IUnknown **)&pIMDImport);
+ if (FAILED(hr))
+ {
+ printf("GetModuleMetaData failed with hr=0x%x\n", hr);
+ }
+
+ WCHAR funcName[STRING_LENGTH];
+ hr = pIMDImport->GetMethodProps(token,
+ NULL,
+ funcName,
+ STRING_LENGTH,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (FAILED(hr))
+ {
+ printf("GetMethodProps failed with hr=0x%x\n", hr);
+ }
+
+ WSTRING name;
+
+ // If the ClassID returned from GetFunctionInfo is 0, then the function is a shared generic function.
+ if (classId != 0)
+ {
+ name += GetClassName(classId);
+ }
+ else
+ {
+ name += WSTR("SharedGenericFunction");
+ }
+
+ name += WSTR("::");
+
+ name += funcName;
+
+ // Fill in the type parameters of the generic method
+ if (nTypeArgs > 0)
+ {
+ name += WSTR("<");
+ }
+
+ for(ULONG32 i = 0; i < nTypeArgs; i++)
+ {
+ name += GetClassName(typeArgs[i]);
+
+ if ((i + 1) != nTypeArgs)
+ {
+ name += WSTR(", ");
+ }
+ }
+
+ if (nTypeArgs > 0)
+ {
+ name += WSTR(">");
+ }
+
+ return name;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+
+#include <thread>
+#include <mutex>
+#include <functional>
+#include <condition_variable>
+#include <string>
+
+#if WIN32
+#define WSTRING std::wstring
+#define WSTR(str) L##str
+#else // WIN32
+#define WSTRING std::u16string
+#define WSTR(str) u##str
+#endif // WIN32
+
+class CorProfiler;
+
+class ManualEvent
+{
+private:
+ std::mutex m_mtx;
+ std::condition_variable m_cv;
+ bool m_set = false;
+
+ static void DoNothing()
+ {
+
+ }
+
+public:
+ ManualEvent() = default;
+ ~ManualEvent() = default;
+ ManualEvent(ManualEvent& other) = delete;
+ ManualEvent(ManualEvent&& other) = delete;
+ ManualEvent& operator= (ManualEvent& other) = delete;
+ ManualEvent& operator= (ManualEvent&& other) = delete;
+
+ void Wait(std::function<void()> spuriousCallback = DoNothing)
+ {
+ std::unique_lock<std::mutex> lock(m_mtx);
+ while (!m_set)
+ {
+ m_cv.wait(lock, [&]() { return m_set; });
+ if (!m_set)
+ {
+ spuriousCallback();
+ }
+ }
+ }
+
+ void Signal()
+ {
+ std::unique_lock<std::mutex> lock(m_mtx);
+ m_set = true;
+ }
+
+ void Reset()
+ {
+ std::unique_lock<std::mutex> lock(m_mtx);
+ m_set = false;
+ }
+};
+
+class Sampler
+{
+private:
+ static Sampler* s_instance;
+
+ std::thread m_workerThread;
+ static ManualEvent s_waitEvent;
+
+ ICorProfilerInfo10* corProfilerInfo;
+
+ static void DoSampling(ICorProfilerInfo10* pProfInfo, CorProfiler *parent);
+
+ WSTRING GetClassName(ClassID classId);
+ WSTRING GetModuleName(ModuleID modId);
+ WSTRING GetFunctionName(FunctionID funcID, const COR_PRF_FRAME_INFO frameInfo);
+public:
+ static Sampler* Instance()
+ {
+ return s_instance;
+ }
+
+ Sampler(ICorProfilerInfo10* pProfInfo, CorProfiler *parent);
+ ~Sampler() = default;
+
+ void Start();
+ void Stop();
+
+ HRESULT StackSnapshotCallback(FunctionID funcId,
+ UINT_PTR ip,
+ COR_PRF_FRAME_INFO frameInfo,
+ ULONG32 contextSize,
+ BYTE context[],
+ void* clientData);
+};
+