Add example profiler that does stack sampling with ICorProfilerInfo10::SuspendRuntime...
authorDavid Mason <davmason@microsoft.com>
Mon, 16 Mar 2020 08:17:58 +0000 (01:17 -0700)
committerGitHub <noreply@github.com>
Mon, 16 Mar 2020 08:17:58 +0000 (01:17 -0700)
12 files changed:
docs/design/coreclr/profiling/sample-profilers/stacksampling/CMakeLists.txt [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/build.cmd [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/build.sh [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/ClassFactory.cpp [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/ClassFactory.h [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.cpp [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.def [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.h [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/dllmain.cpp [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/profiler_pal.h [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/sampler.cpp [new file with mode: 0644]
docs/design/coreclr/profiling/sample-profilers/stacksampling/src/sampler.h [new file with mode: 0644]

diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/CMakeLists.txt b/docs/design/coreclr/profiling/sample-profilers/stacksampling/CMakeLists.txt
new file mode 100644 (file)
index 0000000..710fcfd
--- /dev/null
@@ -0,0 +1,21 @@
+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
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/build.cmd b/docs/design/coreclr/profiling/sample-profilers/stacksampling/build.cmd
new file mode 100644 (file)
index 0000000..331dcf5
--- /dev/null
@@ -0,0 +1,66 @@
+@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 .
+
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/build.sh b/docs/design/coreclr/profiling/sample-profilers/stacksampling/build.sh
new file mode 100644 (file)
index 0000000..78bb71c
--- /dev/null
@@ -0,0 +1,34 @@
+#!/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'
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/ClassFactory.cpp b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/ClassFactory.cpp
new file mode 100644 (file)
index 0000000..87db0a7
--- /dev/null
@@ -0,0 +1,65 @@
+// 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
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/ClassFactory.h b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/ClassFactory.h
new file mode 100644 (file)
index 0000000..10368a6
--- /dev/null
@@ -0,0 +1,22 @@
+// 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
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.cpp b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.cpp
new file mode 100644 (file)
index 0000000..8d29e1c
--- /dev/null
@@ -0,0 +1,510 @@
+// 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;
+}
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.def b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.def
new file mode 100644 (file)
index 0000000..ddb536c
--- /dev/null
@@ -0,0 +1,4 @@
+LIBRARY CorProfiler
+EXPORTS
+    DllGetClassObject             private
+    
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.h b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/CorProfiler.h
new file mode 100644 (file)
index 0000000..1d41f6b
--- /dev/null
@@ -0,0 +1,211 @@
+// 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
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/dllmain.cpp b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/dllmain.cpp
new file mode 100644 (file)
index 0000000..ce04d31
--- /dev/null
@@ -0,0 +1,38 @@
+// 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
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/profiler_pal.h b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/profiler_pal.h
new file mode 100644 (file)
index 0000000..fc02e24
--- /dev/null
@@ -0,0 +1,24 @@
+// 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
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/sampler.cpp b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/sampler.cpp
new file mode 100644 (file)
index 0000000..69ec930
--- /dev/null
@@ -0,0 +1,380 @@
+// 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;
+}
diff --git a/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/sampler.h b/docs/design/coreclr/profiling/sample-profilers/stacksampling/src/sampler.h
new file mode 100644 (file)
index 0000000..8cd0fd1
--- /dev/null
@@ -0,0 +1,103 @@
+// 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);
+};
+