initial commit
authorAndrey Kvochko <a.kvochko@samsung.com>
Fri, 2 Jun 2017 12:22:34 +0000 (15:22 +0300)
committerAndrey Kvochko <a.kvochko@samsung.com>
Fri, 2 Jun 2017 12:22:34 +0000 (15:22 +0300)
78 files changed:
CMakeLists.txt [new file with mode: 0644]
clrdefinitions.cmake [new file with mode: 0644]
compileoptions.cmake [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/amd64/archhelpers.cpp [new file with mode: 0644]
src/amd64/asmhelpers.S [new file with mode: 0644]
src/arm/archhelpers.cpp [new file with mode: 0644]
src/arm/asmhelpers.S [new file with mode: 0644]
src/classfactory.cpp [new file with mode: 0644]
src/classfactory.h [new file with mode: 0644]
src/config/commonconfig.h [new file with mode: 0644]
src/config/commonconfigconversions.cpp [new file with mode: 0644]
src/config/commonconfigconversions.h [new file with mode: 0644]
src/config/environmentconfigprovider.cpp [new file with mode: 0644]
src/config/environmentconfigprovider.h [new file with mode: 0644]
src/config/loggerconfig.cpp [new file with mode: 0644]
src/config/loggerconfig.h [new file with mode: 0644]
src/config/loggerconfigconversions.cpp [new file with mode: 0644]
src/config/loggerconfigconversions.h [new file with mode: 0644]
src/config/profilerconfig.cpp [new file with mode: 0644]
src/config/profilerconfig.h [new file with mode: 0644]
src/config/profilerconfigconversions.cpp [new file with mode: 0644]
src/config/profilerconfigconversions.h [new file with mode: 0644]
src/config/tracelogconfig.cpp [new file with mode: 0644]
src/config/tracelogconfig.h [new file with mode: 0644]
src/config/tracelogconfigconversions.cpp [new file with mode: 0644]
src/config/tracelogconfigconversions.h [new file with mode: 0644]
src/dllmain.cpp [new file with mode: 0644]
src/guid.h [new file with mode: 0644]
src/i386/archhelpers.cpp [new file with mode: 0644]
src/i386/asmhelpers.S [new file with mode: 0644]
src/info/baseinfo.h [new file with mode: 0644]
src/info/classinfo.cpp [new file with mode: 0644]
src/info/classinfo.h [new file with mode: 0644]
src/info/functioninfo.cpp [new file with mode: 0644]
src/info/functioninfo.h [new file with mode: 0644]
src/info/mappedinfo.h [new file with mode: 0644]
src/info/threadinfo.h [new file with mode: 0644]
src/log.h [new file with mode: 0644]
src/misc/default_delete.h [new file with mode: 0644]
src/misc/intervalsplitter.h [new file with mode: 0644]
src/misc/iterator_range.h [new file with mode: 0644]
src/misc/localtime.cpp [new file with mode: 0644]
src/misc/localtime.h [new file with mode: 0644]
src/misc/shared_iterator_range.h [new file with mode: 0644]
src/misc/sigaction.cpp [new file with mode: 0644]
src/misc/sigaction.h [new file with mode: 0644]
src/profiler.cpp [new file with mode: 0644]
src/profiler.h [new file with mode: 0644]
src/profilerinfo.cpp [new file with mode: 0644]
src/profilerinfo.h [new file with mode: 0644]
src/profilermanager.cpp [new file with mode: 0644]
src/profilermanager.h [new file with mode: 0644]
src/storage/basestorage.h [new file with mode: 0644]
src/storage/classstorage.h [new file with mode: 0644]
src/storage/functionstorage.h [new file with mode: 0644]
src/storage/livestorage.h [new file with mode: 0644]
src/storage/mappedstorage.h [new file with mode: 0644]
src/storage/ringbuffer.h [new file with mode: 0644]
src/storage/threadstorage.h [new file with mode: 0644]
src/sync/binarysemaphore.h [new file with mode: 0644]
src/sync/shared_mutex.cpp [new file with mode: 0644]
src/sync/shared_mutex.h [new file with mode: 0644]
src/sync/sharedresource.h [new file with mode: 0644]
src/trace/basetrace.cpp [new file with mode: 0644]
src/trace/basetrace.h [new file with mode: 0644]
src/trace/commontrace.cpp [new file with mode: 0644]
src/trace/commontrace.h [new file with mode: 0644]
src/trace/cputrace.cpp [new file with mode: 0644]
src/trace/cputrace.h [new file with mode: 0644]
src/trace/eventchannel.cpp [new file with mode: 0644]
src/trace/eventchannel.h [new file with mode: 0644]
src/trace/executiontrace.cpp [new file with mode: 0644]
src/trace/executiontrace.h [new file with mode: 0644]
src/trace/memorytrace.cpp [new file with mode: 0644]
src/trace/memorytrace.h [new file with mode: 0644]
src/tracelog.cpp [new file with mode: 0644]
src/tracelog.h [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..edcff25
--- /dev/null
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+project(CoreProfiler)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-null-conversion")
+
+set(CLR_CMAKE_PLATFORM_UNIX 1)
+
+# Architecture specific files folder name
+if (CLR_CMAKE_TARGET_ARCH_AMD64)
+    set(ARCH_SOURCES_DIR amd64)
+    set(CLR_CMAKE_PLATFORM_UNIX_AMD64)
+    set(LIB64 TRUE)
+elseif (CLR_CMAKE_TARGET_ARCH_ARM64)
+    set(ARCH_SOURCES_DIR arm64)
+    set(CLR_CMAKE_PLATFORM_UNIX_ARM64)
+    set(LIB64 TRUE)
+elseif (CLR_CMAKE_TARGET_ARCH_ARM)
+    set(ARCH_SOURCES_DIR arm)
+    set(CLR_CMAKE_PLATFORM_UNIX_ARM)
+    set(LIB64 FALSE)
+elseif (CLR_CMAKE_TARGET_ARCH_I386)
+    set(ARCH_SOURCES_DIR i386)
+    set(CLR_CMAKE_PLATFORM_UNIX_I386)
+    set(LIB64 FALSE)
+else ()
+    clr_unknown_arch()
+endif ()
+
+include(clrdefinitions.cmake)
+include(compileoptions.cmake)
+
+add_subdirectory(src)
diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake
new file mode 100644 (file)
index 0000000..e077efc
--- /dev/null
@@ -0,0 +1,47 @@
+if (CLR_CMAKE_TARGET_ARCH_AMD64)
+  if (CLR_CMAKE_PLATFORM_UNIX)
+    add_definitions(-DDBG_TARGET_AMD64_UNIX)
+  endif()
+  add_definitions(-D_TARGET_AMD64_=1)
+  add_definitions(-DDBG_TARGET_64BIT=1)
+  add_definitions(-DDBG_TARGET_AMD64=1)
+  add_definitions(-DDBG_TARGET_WIN64=1)
+  add_definitions(-D_AMD64_)
+  add_definitions(-D_WIN64)
+  add_definitions(-DAMD64)
+  add_definitions(-DBIT64=1)
+elseif (CLR_CMAKE_TARGET_ARCH_ARM64)
+  if (CLR_CMAKE_PLATFORM_UNIX)
+    add_definitions(-DDBG_TARGET_ARM64_UNIX)
+  endif()
+  add_definitions(-D_TARGET_ARM64_=1)
+  add_definitions(-DDBG_TARGET_64BIT=1)
+  add_definitions(-DDBG_TARGET_ARM64=1)
+  add_definitions(-DDBG_TARGET_WIN64=1)
+  add_definitions(-DFEATURE_MULTIREG_RETURN)
+  add_definitions(-D_ARM64_)
+  add_definitions(-DARM64)
+  add_definitions(-D_WIN64)
+  add_definitions(-DBIT64=1)
+elseif (CLR_CMAKE_TARGET_ARCH_ARM)
+  if (CLR_CMAKE_PLATFORM_UNIX)
+    add_definitions(-DDBG_TARGET_ARM_UNIX)
+  elseif (WIN32 AND NOT DEFINED CLR_CROSS_COMPONENTS_BUILD)
+    # Set this to ensure we can use Arm SDK for Desktop binary linkage when doing native (Arm32) build
+    add_definitions(-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1)
+    add_definitions(-D_ARM_WORKAROUND_)
+  endif (CLR_CMAKE_PLATFORM_UNIX)
+  add_definitions(-D_TARGET_ARM_=1)
+  add_definitions(-DDBG_TARGET_32BIT=1)
+  add_definitions(-DDBG_TARGET_ARM=1)
+  add_definitions(-D_ARM_)
+  add_definitions(-DARM)
+elseif (CLR_CMAKE_TARGET_ARCH_I386)
+  add_definitions(-D_TARGET_X86_=1)
+  add_definitions(-DDBG_TARGET_32BIT=1)
+  add_definitions(-DDBG_TARGET_X86=1)
+  add_definitions(-D_X86_)
+else ()
+  clr_unknown_arch()
+endif (CLR_CMAKE_TARGET_ARCH_AMD64)
+
diff --git a/compileoptions.cmake b/compileoptions.cmake
new file mode 100644 (file)
index 0000000..d872275
--- /dev/null
@@ -0,0 +1,57 @@
+# Disable frame pointer optimizations so profilers can get better call stacks
+add_compile_options(-fno-omit-frame-pointer)
+
+# The -fms-extensions enable the stuff like __if_exists, __declspec(uuid()), etc.
+add_compile_options(-fms-extensions )
+#-fms-compatibility      Enable full Microsoft Visual C++ compatibility
+#-fms-extensions         Accept some non-standard constructs supported by the Microsoft compiler
+
+# Make signed arithmetic overflow of addition, subtraction, and multiplication wrap around
+# using twos-complement representation (this is normally undefined according to the C++ spec).
+add_compile_options(-fwrapv)
+
+add_definitions(-DDISABLE_CONTRACTS)
+# The -ferror-limit is helpful during the porting, it makes sure the compiler doesn't stop
+# after hitting just about 20 errors.
+add_compile_options(-ferror-limit=4096)
+
+# All warnings that are not explicitly disabled are reported as errors
+add_compile_options(-Werror)
+
+# Disabled warnings
+add_compile_options(-Wno-unused-private-field)
+add_compile_options(-Wno-unused-variable)
+# Explicit constructor calls are not supported by clang (this->ClassName::ClassName())
+add_compile_options(-Wno-microsoft)
+# This warning is caused by comparing 'this' to NULL
+add_compile_options(-Wno-tautological-compare)
+# There are constants of type BOOL used in a condition. But BOOL is defined as int
+# and so the compiler thinks that there is a mistake.
+add_compile_options(-Wno-constant-logical-operand)
+
+add_compile_options(-Wno-unknown-warning-option)
+
+#These seem to indicate real issues
+add_compile_options(-Wno-invalid-offsetof)
+# The following warning indicates that an attribute __attribute__((__ms_struct__)) was applied
+# to a struct or a class that has virtual members or a base class. In that case, clang
+# may not generate the same object layout as MSVC.
+add_compile_options(-Wno-incompatible-ms-struct)
+
+# Some architectures (e.g., ARM) assume char type is unsigned while CoreCLR assumes char is signed
+# as x64 does. It has been causing issues in ARM (https://github.com/dotnet/coreclr/issues/4746)
+add_compile_options(-fsigned-char)
+
+if(CLR_CMAKE_PLATFORM_UNIX_ARM)
+   # Because we don't use CMAKE_C_COMPILER/CMAKE_CXX_COMPILER to use clang
+   # we have to set the triple by adding a compiler argument
+   add_compile_options(-mthumb)
+   add_compile_options(-mfpu=vfpv3)
+   if(ARM_SOFTFP)
+     add_definitions(-DARM_SOFTFP)
+     add_compile_options(-mfloat-abi=softfp)
+     add_compile_options(-target armv7-linux-gnueabi)
+   else()
+     add_compile_options(-target armv7-linux-gnueabihf)
+   endif(ARM_SOFTFP)
+endif(CLR_CMAKE_PLATFORM_UNIX_ARM)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5c14c76
--- /dev/null
@@ -0,0 +1,103 @@
+add_definitions(-DPAL_STDCPP_COMPAT)
+
+include_directories(
+  BEFORE
+  .
+  config
+  info
+  misc
+  stacktrace
+  storage
+  sync
+  trace
+)
+
+if(CLR_CMAKE_TARGET_ARCH_ARM)
+  include_directories("${CLR_SRC_DIR}/src/pal/src/include")
+endif()
+
+set(CLR_INCLUDE_DIR
+  ${CLR_BIN_DIR}/inc
+  ${CLR_SRC_DIR}/src/pal/inc/rt
+  ${CLR_SRC_DIR}/src/pal/prebuilt/inc
+  ${CLR_SRC_DIR}/src/pal/inc 
+  ${CLR_SRC_DIR}/src/inc)
+
+include_directories(AFTER ${CLR_INCLUDE_DIR})
+
+set(PROFILER_SOURCES_COMMON
+  classfactory.cpp
+  config/commonconfigconversions.cpp
+  config/environmentconfigprovider.cpp
+  config/loggerconfig.cpp
+  config/loggerconfigconversions.cpp
+  config/profilerconfig.cpp
+  config/profilerconfigconversions.cpp
+  config/tracelogconfig.cpp
+  config/tracelogconfigconversions.cpp
+  dllmain.cpp
+  info/classinfo.cpp
+  info/functioninfo.cpp
+  misc/localtime.cpp
+  misc/sigaction.cpp
+  profiler.cpp
+  profilerinfo.cpp
+  profilermanager.cpp
+  sync/shared_mutex.cpp
+  trace/basetrace.cpp
+  trace/commontrace.cpp
+  trace/cputrace.cpp
+  trace/executiontrace.cpp
+  trace/eventchannel.cpp
+  trace/memorytrace.cpp
+  tracelog.cpp
+)
+
+if(CLR_CMAKE_TARGET_ARCH_AMD64)
+  set(PROFILER_SOURCES_ASM
+    ${ARCH_SOURCES_DIR}/asmhelpers.S
+    ${ARCH_SOURCES_DIR}/archhelpers.cpp
+  )
+elseif(CLR_CMAKE_TARGET_ARCH_ARM)
+  set(PROFILER_SOURCES_ASM
+    ${ARCH_SOURCES_DIR}/asmhelpers.S
+    ${ARCH_SOURCES_DIR}/archhelpers.cpp
+  )
+elseif(CLR_CMAKE_TARGET_ARCH_I386)
+  set(PROFILER_SOURCES_ASM
+    ${ARCH_SOURCES_DIR}/asmhelpers.S
+    ${ARCH_SOURCES_DIR}/archhelpers.cpp
+  )
+endif()
+
+add_library(coreprof
+  SHARED
+  ${PROFILER_SOURCES_COMMON}
+  ${PROFILER_SOURCES_ASM}
+)
+
+set(PROFILER_LINK_LIBRARIES
+  ${CLR_BIN_DIR}/lib/libcorguids.a
+  # utilcodestaticnohost
+  # gcinfo
+)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+  list(APPEND PROFILER_LINK_LIBRARIES
+    # mscorrc_debug
+    ${CLR_BIN_DIR}/lib/libcoreclrpal.a
+    ${CLR_BIN_DIR}/lib/libpalrt.a
+  )
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+target_link_libraries(coreprof
+  ${PROFILER_LINK_LIBRARIES}
+)
+
+if(LIB64)
+  set(LIBSUFFIX 64)
+else(LIB64)
+  set(LIBSUFFIX "")
+endif()
+
+install(TARGETS coreprof DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIBSUFFIX})
diff --git a/src/amd64/archhelpers.cpp b/src/amd64/archhelpers.cpp
new file mode 100644 (file)
index 0000000..45513e0
--- /dev/null
@@ -0,0 +1,41 @@
+#include <sys/ucontext.h>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+HRESULT ContextToStackSnapshotContext(
+    const void *context, CONTEXT *winContext) noexcept
+{
+    _ASSERTE(context != nullptr && winContext != nullptr);
+
+    *winContext = {CONTEXT_INTEGER};
+    const mcontext_t *mc =
+        &(reinterpret_cast<const ucontext_t*>(context))->uc_mcontext;
+
+    {
+        winContext->Rax  = mc->gregs[REG_RAX];
+        winContext->Rbx  = mc->gregs[REG_RBX];
+        winContext->Rcx  = mc->gregs[REG_RCX];
+        winContext->Rdx  = mc->gregs[REG_RDX];
+        winContext->Rsi  = mc->gregs[REG_RSI];
+        winContext->Rdi  = mc->gregs[REG_RDI];
+        winContext->Rbp  = mc->gregs[REG_RBP];
+        winContext->Rsp  = mc->gregs[REG_RSP];
+        winContext->R8  = mc->gregs[REG_R8];
+        winContext->R9  = mc->gregs[REG_R9];
+        winContext->R10  = mc->gregs[REG_R10];
+        winContext->R11  = mc->gregs[REG_R11];
+        winContext->R12  = mc->gregs[REG_R12];
+        winContext->R13  = mc->gregs[REG_R13];
+        winContext->R14  = mc->gregs[REG_R14];
+        winContext->R15  = mc->gregs[REG_R15];
+        winContext->EFlags  = mc->gregs[REG_EFL];
+        winContext->Rip = mc->gregs[REG_RIP];
+        winContext->SegCs = (*((WORD *)mc->gregs[REG_CSGSFS] + 0));
+        winContext->SegGs = (*((WORD *)mc->gregs[REG_CSGSFS] + 1));
+        winContext->SegFs = (*((WORD *)mc->gregs[REG_CSGSFS] + 2));
+    }
+
+    return S_OK;
+}
\ No newline at end of file
diff --git a/src/amd64/asmhelpers.S b/src/amd64/asmhelpers.S
new file mode 100644 (file)
index 0000000..6382cc7
--- /dev/null
@@ -0,0 +1,179 @@
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+
+//
+// EXTERN_C void EnterNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY EnterNaked3, _TEXT, NoHandler
+    push rbp
+    mov  rbp, rsp
+    push rax
+    push rbx
+    push rcx
+    push rdx
+    push rsi
+    push rdi
+    push r8
+    push r9
+    push r10
+    push r11
+    push r12
+    push r13
+    push r14
+    push r15
+    sub  rsp, 16*8 /* xmm0-xmm7 */
+    and  rsp, (-16) /* must be alligned */
+    movdqa     [rsp+0x00], xmm0
+    movdqa     [rsp+0x10], xmm1
+    movdqa     [rsp+0x20], xmm2
+    movdqa     [rsp+0x30], xmm3
+    movdqa     [rsp+0x40], xmm4
+    movdqa     [rsp+0x50], xmm5
+    movdqa     [rsp+0x60], xmm6
+    movdqa     [rsp+0x70], xmm7
+    mov  rdi, r14
+    call    EXTERNAL_C_FUNC(EnterStub)
+    movdqa     xmm0, [rsp+0x00]
+    movdqa     xmm1, [rsp+0x10]
+    movdqa     xmm2, [rsp+0x20]
+    movdqa     xmm3, [rsp+0x30]
+    movdqa     xmm4, [rsp+0x40]
+    movdqa     xmm5, [rsp+0x50]
+    movdqa     xmm6, [rsp+0x60]
+    movdqa     xmm7, [rsp+0x70]
+    lea  rsp, [rbp - (14 * 8)]
+    pop  r15
+    pop  r14
+    pop  r13
+    pop  r12
+    pop  r11
+    pop  r10
+    pop  r9
+    pop  r8
+    pop  rdi
+    pop  rsi
+    pop  rdx
+    pop  rcx
+    pop  rbx
+    pop  rax
+    pop  rbp
+    ret
+NESTED_END EnterNaked3, _TEXT
+
+//
+// EXTERN_C void LeaveNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY LeaveNaked3, _TEXT, NoHandler
+    push rbp
+    mov  rbp, rsp
+    push rax
+    push rbx
+    push rcx
+    push rdx
+    push rsi
+    push rdi
+    push r8
+    push r9
+    push r10
+    push r11
+    push r12
+    push r13
+    push r14
+    push r15
+    sub  rsp, 16*8 /* xmm0-xmm7 */
+    and  rsp, (-16) /* must be alligned */
+    movdqa     [rsp+0x00], xmm0
+    movdqa     [rsp+0x10], xmm1
+    movdqa     [rsp+0x20], xmm2
+    movdqa     [rsp+0x30], xmm3
+    movdqa     [rsp+0x40], xmm4
+    movdqa     [rsp+0x50], xmm5
+    movdqa     [rsp+0x60], xmm6
+    movdqa     [rsp+0x70], xmm7
+
+    call    EXTERNAL_C_FUNC(LeaveStub)
+    movdqa     xmm0, [rsp+0x00]
+    movdqa     xmm1, [rsp+0x10]
+    movdqa     xmm2, [rsp+0x20]
+    movdqa     xmm3, [rsp+0x30]
+    movdqa     xmm4, [rsp+0x40]
+    movdqa     xmm5, [rsp+0x50]
+    movdqa     xmm6, [rsp+0x60]
+    movdqa     xmm7, [rsp+0x70]
+    lea  rsp, [rbp - (14 * 8)]
+    pop  r15
+    pop  r14
+    pop  r13
+    pop  r12
+    pop  r11
+    pop  r10
+    pop  r9
+    pop  r8
+    pop  rdi
+    pop  rsi
+    pop  rdx
+    pop  rcx
+    pop  rbx
+    pop  rax
+    pop  rbp
+    ret
+NESTED_END LeaveNaked3, _TEXT
+
+//
+// EXTERN_C void TailcallNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY TailcallNaked3, _TEXT, NoHandler
+    push rbp
+    mov  rbp, rsp
+    push rax
+    push rbx
+    push rcx
+    push rdx
+    push rsi
+    push rdi
+    push r8
+    push r9
+    push r10
+    push r11
+    push r12
+    push r13
+    push r14
+    push r15
+    sub  rsp, 16*8 /* xmm0-xmm7 */
+    and  rsp, (-16) /* must be alligned */
+    movdqa     [rsp+0x00], xmm0
+    movdqa     [rsp+0x10], xmm1
+    movdqa     [rsp+0x20], xmm2
+    movdqa     [rsp+0x30], xmm3
+    movdqa     [rsp+0x40], xmm4
+    movdqa     [rsp+0x50], xmm5
+    movdqa     [rsp+0x60], xmm6
+    movdqa     [rsp+0x70], xmm7
+
+    call    EXTERNAL_C_FUNC(TailcallStub)
+    movdqa     xmm0, [rsp+0x00]
+    movdqa     xmm1, [rsp+0x10]
+    movdqa     xmm2, [rsp+0x20]
+    movdqa     xmm3, [rsp+0x30]
+    movdqa     xmm4, [rsp+0x40]
+    movdqa     xmm5, [rsp+0x50]
+    movdqa     xmm6, [rsp+0x60]
+    movdqa     xmm7, [rsp+0x70]
+    lea  rsp, [rbp - (14 * 8)]
+    pop  r15
+    pop  r14
+    pop  r13
+    pop  r12
+    pop  r11
+    pop  r10
+    pop  r9
+    pop  r8
+    pop  rdi
+    pop  rsi
+    pop  rdx
+    pop  rcx
+    pop  rbx
+    pop  rax
+    pop  rbp
+    ret
+NESTED_END TailcallNaked3, _TEXT
diff --git a/src/arm/archhelpers.cpp b/src/arm/archhelpers.cpp
new file mode 100644 (file)
index 0000000..fb4dcc3
--- /dev/null
@@ -0,0 +1,36 @@
+#include <sys/ucontext.h>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+HRESULT ContextToStackSnapshotContext(
+    const void *context, CONTEXT *winContext) noexcept
+{
+    _ASSERTE(context != nullptr && winContext != nullptr);
+
+    *winContext = {CONTEXT_INTEGER};
+    const mcontext_t *mc =
+        &(reinterpret_cast<const ucontext_t*>(context))->uc_mcontext;
+
+    {
+        winContext->R0  = mc->arm_r0;
+        winContext->R1  = mc->arm_r1;
+        winContext->R2  = mc->arm_r2;
+        winContext->R3  = mc->arm_r3;
+        winContext->R4  = mc->arm_r4;
+        winContext->R5  = mc->arm_r5;
+        winContext->R6  = mc->arm_r6;
+        winContext->R7  = mc->arm_r7;
+        winContext->R8  = mc->arm_r8;
+        winContext->R9  = mc->arm_r9;
+        winContext->R10 = mc->arm_r10;
+        winContext->R11 = mc->arm_fp;
+        winContext->R12 = mc->arm_ip;
+        winContext->Sp  = mc->arm_sp;
+        winContext->Lr  = mc->arm_lr;
+        winContext->Pc  = mc->arm_pc;
+    }
+
+    return S_OK;
+}
diff --git a/src/arm/asmhelpers.S b/src/arm/asmhelpers.S
new file mode 100644 (file)
index 0000000..6dcc892
--- /dev/null
@@ -0,0 +1,39 @@
+#include "unixasmmacros.inc"
+
+.syntax unified
+.thumb
+
+//
+// EXTERN_C void EnterNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY EnterNaked3, _TEXT, NoHandler
+    push    {r0-r7, lr}
+    .save   {r0-r7, lr}
+    bl      C_FUNC(EnterStub)
+    pop     {r0-r7, pc}
+NESTED_END EnterNaked3, _TEXT
+
+//
+// EXTERN_C void LeaveNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY LeaveNaked3, _TEXT, NoHandler
+    push    {r0-r7, lr}
+    .save   {r0-r7, lr}
+    bl      C_FUNC(LeaveStub)
+    pop     {r0-r7, pc}
+NESTED_END LeaveNaked3, _TEXT
+
+//
+// EXTERN_C void TailcallNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY TailcallNaked3, _TEXT, NoHandler
+    push    {r0-r7, lr}
+    .save   {r0-r7, lr}
+    bl      C_FUNC(TailcallStub)
+    pop     {r0-r7, pc}
+NESTED_END TailcallNaked3, _TEXT
+
+NESTED_ENTRY getPrevPC, _TEXT, NoHandler
+        ldr r0, [r11, #4]
+        bx lr
+NESTED_END getPrevPC, _TEXT
diff --git a/src/classfactory.cpp b/src/classfactory.cpp
new file mode 100644 (file)
index 0000000..27c9a5e
--- /dev/null
@@ -0,0 +1,75 @@
+#include "classfactory.h"
+
+CClassFactory::CClassFactory(PFN_CREATE_OBJ pfnCreateObject)
+    : m_cRef(1)
+    , m_pfnCreateObject(pfnCreateObject)
+{
+}
+
+CClassFactory::~CClassFactory()
+{
+}
+
+HRESULT STDMETHODCALLTYPE CClassFactory::QueryInterface(
+    REFIID riid,
+    void   **ppvObject)
+{
+    if (ppvObject == nullptr)
+        return E_POINTER;
+
+    // Pick the right v-table based on the IID passed in.
+    if (riid == IID_IClassFactory)
+    {
+        *ppvObject = static_cast<IClassFactory*>(this);
+    }
+    else if (riid == IID_IUnknown)
+    {
+        *ppvObject = static_cast<IUnknown*>(this);
+    }
+    else
+    {
+        *ppvObject = nullptr;
+        return E_NOINTERFACE;
+    }
+
+    // If successful, add a reference for out pointer and return.
+    this->AddRef();
+
+    return S_OK;
+}
+
+ULONG STDMETHODCALLTYPE CClassFactory::AddRef(void)
+{
+    return InterlockedIncrement(&m_cRef);
+}
+
+ULONG STDMETHODCALLTYPE CClassFactory::Release(void)
+{
+    LONG cRef = InterlockedDecrement(&m_cRef);
+    if (cRef == 0)
+        delete this;
+
+    return cRef;
+}
+
+HRESULT STDMETHODCALLTYPE CClassFactory::CreateInstance(
+    IUnknown *pUnkOuter,
+    REFIID   riid,
+    void     **ppvObject)
+{
+    // Avoid confusion.
+    *ppvObject = NULL;
+
+    // Aggregation is not supported by these objects.
+    if (pUnkOuter != NULL)
+        return CLASS_E_NOAGGREGATION;
+
+    // Ask the object to create an instance of itself, and check the iid.
+    return (*m_pfnCreateObject)(riid, ppvObject);
+}
+
+HRESULT STDMETHODCALLTYPE CClassFactory::LockServer(BOOL fLock)
+{
+    // NOTE: not need to implement.
+    return E_FAIL;
+}
diff --git a/src/classfactory.h b/src/classfactory.h
new file mode 100644 (file)
index 0000000..ba750e9
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _CLASS_FACTORY_H_
+#define _CLASS_FACTORY_H_
+
+#include <unknwn.h>
+
+// This typedef is for a function which will create a new instance of an object.
+typedef HRESULT (*PFN_CREATE_OBJ)(REFIID riid, void **ppvObject);
+
+// One class factory object satifies all of our clsid's, to reduce overall
+// code bloat.
+class CClassFactory : public IClassFactory
+{
+public:
+    CClassFactory(PFN_CREATE_OBJ pfnCreateObject);
+
+private:
+    CClassFactory(); // Can't use without data.
+
+    virtual ~CClassFactory(); // Factory should be destroyed through public API.
+
+public:
+    //
+    // IUnknown methods.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+        REFIID riid,
+        void   **ppvObject);
+
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    //
+    // IClassFactory methods.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE CreateInstance(
+        IUnknown *pUnkOuter,
+        REFIID   riid,
+        void     **ppvObject);
+
+    virtual HRESULT STDMETHODCALLTYPE LockServer(
+        BOOL fLock);
+
+private:
+    LONG           m_cRef;            // Reference count.
+    PFN_CREATE_OBJ m_pfnCreateObject; // Creation function for an instance.
+};
+
+#endif // _CLASS_FACTORY_H_
diff --git a/src/config/commonconfig.h b/src/config/commonconfig.h
new file mode 100644 (file)
index 0000000..75dd8f4
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _COMMON_CONFIG_H_
+#define _COMMON_CONFIG_H_
+
+#include <stdexcept>
+
+//
+// Exceptions of type bad_conversion are thrown when value of some type
+// can't be converted to another type. Exception should contain description
+// of reason why convertion can't be performed.
+//
+class bad_conversion : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+//
+// Exceptions of type config_error are thrown during configuration is updated
+// to prevent data inconsistency. It reports about errors in update process.
+// Exception should contain description of the problem that causes update
+// interruption.
+//
+// Also this type of exceptions is used in validation process.
+//
+class config_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+//
+// Common declaration of convert() template function specialized for various
+// types. Function returns its argument converted to Target type that should
+// be specified as template parameter.
+//
+template<typename Target, typename Source>
+Target convert(Source);
+
+#endif // _COMMON_CONFIG_H_
diff --git a/src/config/commonconfigconversions.cpp b/src/config/commonconfigconversions.cpp
new file mode 100644 (file)
index 0000000..cb13b03
--- /dev/null
@@ -0,0 +1,78 @@
+#include <string>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <strings.h>
+
+#include "commonconfigconversions.h"
+
+template<>
+bool convert(const char *str)
+{
+    if (
+        strcmp    (str, "1")        == 0 ||
+        strcasecmp(str, "true")     == 0 ||
+        strcasecmp(str, "on")       == 0 ||
+        strcasecmp(str, "yes")      == 0 ||
+        strcasecmp(str, "enabled")  == 0 ||
+        strcasecmp(str, "")         == 0
+    )
+    {
+        return true;
+    }
+    else if (
+        strcmp    (str, "0")        == 0 ||
+        strcasecmp(str, "false")    == 0 ||
+        strcasecmp(str, "off")      == 0 ||
+        strcasecmp(str, "no")       == 0 ||
+        strcasecmp(str, "disabled") == 0
+    )
+    {
+        return false;
+    }
+    else
+    {
+        throw bad_conversion("incorrect value for type bool");
+    }
+}
+
+template<>
+unsigned long convert(const char *str)
+{
+    unsigned long value;
+    char *str_end;
+
+    errno = 0;
+    value = strtoul(str, &str_end, 0);
+
+    if (errno == ERANGE)
+    {
+        throw bad_conversion("is out of range for unsigned long");
+    }
+
+    if (str == str_end)
+    {
+        throw bad_conversion("incorrect value for type unsigned long");
+    }
+
+    if (*str_end != '\0')
+    {
+        throw bad_conversion("contains not number symbols");
+    }
+
+    return value;
+}
+
+template<>
+std::string convert(const char *str)
+{
+    return str;
+}
+
+template<>
+const char *convert(bool value)
+{
+    return value ? "T" : "F";
+}
diff --git a/src/config/commonconfigconversions.h b/src/config/commonconfigconversions.h
new file mode 100644 (file)
index 0000000..be8f217
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _COMMON_CONFIG_CONVERSIONS_H_
+#define _COMMON_CONFIG_CONVERSIONS_H_
+
+#include "commonconfig.h"
+
+template<>
+bool convert(const char *str);
+
+template<>
+unsigned long convert(const char *str);
+
+template<>
+std::string convert(const char *str);
+
+template<>
+const char *convert(bool value);
+
+#endif // _COMMON_CONFIG_CONVERSIONS_H_
diff --git a/src/config/environmentconfigprovider.cpp b/src/config/environmentconfigprovider.cpp
new file mode 100644 (file)
index 0000000..d32aaf7
--- /dev/null
@@ -0,0 +1,136 @@
+#include <stdexcept>
+#include <sstream>
+
+#include <stdlib.h>
+
+#include "commonconfig.h"
+#include "commonconfigconversions.h"
+#include "loggerconfigconversions.h"
+#include "tracelogconfigconversions.h"
+#include "profilerconfigconversions.h"
+#include "environmentconfigprovider.h"
+
+template<typename T>
+bool EnvironmentConfigProvider::FetchValue(const char *name, T &value) const
+{
+    const char *env = getenv(name);
+    if (env)
+    {
+        try
+        {
+            value = convert<T>(env);
+        }
+        catch (const std::runtime_error &e)
+        {
+            std::stringstream ss;
+            ss << "variable " << name << "=" << env <<
+                " can't be parsed: " << e.what();
+            throw config_error(ss.str());
+        }
+
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+void EnvironmentConfigProvider::FetchConfig(LoggerConfig &config) const
+{
+    // Save current configuration to temporary.
+    LoggerConfig new_config(config);
+
+    //
+    // Fetching from the environment can cause exceptions.
+    //
+
+    FetchValue("PROF_LOG_LEVEL", new_config.Level);
+
+    bool file_name_specified =
+        FetchValue("PROF_LOG_FILENAME", new_config.FileName);
+    if (file_name_specified)
+    {
+        new_config.OutputStream = LoggerOutputStream::File;
+    }
+
+    FetchValue("PROF_LOG_STREAM", new_config.OutputStream);
+
+    // Apply changes to the current configuration.
+    config = new_config;
+}
+
+void EnvironmentConfigProvider::FetchConfig(TraceLogConfig &config) const
+{
+    // Save current configuration to temporary.
+    TraceLogConfig new_config(config);
+
+    //
+    // Fetching from the environment can cause exceptions.
+    //
+
+    bool file_name_specified =
+        FetchValue("PROF_TRACE_FILENAME", new_config.FileName);
+    if (file_name_specified)
+    {
+        new_config.OutputStream = TraceLogOutputStream::File;
+    }
+
+    FetchValue("PROF_TRACE_STREAM", new_config.OutputStream);
+
+    // Apply changes to the current configuration.
+    config = new_config;
+}
+
+void EnvironmentConfigProvider::FetchConfig(ProfilerConfig &config) const
+{
+    // Save current configuration to temporary.
+    ProfilerConfig new_config(config);
+
+    //
+    // Fetching from the environment can cause exceptions.
+    //
+    // We don't check whether the environment variables override the
+    // configuration or not.
+    //
+
+    FetchValue("PROF_COLLECT_METHOD", new_config.CollectionMethod);
+    if (new_config.CollectionMethod != CollectionMethod::Sampling)
+    {
+        new_config.SamplingTimeoutMs = 0;
+        new_config.HighGranularityEnabled = false;
+    }
+    if (FetchValue("PROF_SAMPLING_TIMEOUT", new_config.SamplingTimeoutMs))
+    {
+        new_config.CpuTraceTimeoutMs = new_config.SamplingTimeoutMs;
+    }
+    FetchValue("PROF_HIGH_GRAN",     new_config.HighGranularityEnabled);
+    FetchValue("PROF_DELAYED_START", new_config.TracingSuspendedOnStart);
+    FetchValue("PROF_LINE_TRACE",    new_config.LineTraceEnabled);
+
+    bool CpuTraceEnabled;
+    if (FetchValue("PROF_CPU_TRACE", CpuTraceEnabled))
+    {
+        new_config.CpuTraceProcessEnabled = CpuTraceEnabled;
+        new_config.CpuTraceThreadEnabled  = CpuTraceEnabled;
+    }
+    FetchValue("PROF_CPU_TRACE_PROC",   new_config.CpuTraceProcessEnabled);
+    FetchValue("PROF_CPU_TRACE_THREAD", new_config.CpuTraceThreadEnabled);
+    if (!new_config.CpuTraceProcessEnabled && !new_config.CpuTraceThreadEnabled)
+    {
+        new_config.CpuTraceTimeoutMs = 0;
+    }
+    FetchValue("PROF_CPU_TRACE_TIMEOUT", new_config.CpuTraceTimeoutMs);
+
+    FetchValue("PROF_EXECUTION_TRACE",  new_config.ExecutionTraceEnabled);
+    FetchValue("PROF_MEMORY_TRACE",     new_config.MemoryTraceEnabled);
+    if (!new_config.MemoryTraceEnabled)
+    {
+        new_config.StackTrackingEnabled = false;
+    }
+
+    FetchValue("PROF_STACK_TRACK",      new_config.StackTrackingEnabled);
+
+    // Apply changes to the current configuration.
+    config = new_config;
+}
diff --git a/src/config/environmentconfigprovider.h b/src/config/environmentconfigprovider.h
new file mode 100644 (file)
index 0000000..9be9162
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _ENVIRONMENT_CONFIG_PROVIDER_H_
+#define _ENVIRONMENT_CONFIG_PROVIDER_H_
+
+#include "loggerconfig.h"
+#include "tracelogconfig.h"
+#include "profilerconfig.h"
+
+class EnvironmentConfigProvider
+{
+private:
+    // Internal template method that takes name of the environment variable and
+    // tries to fetch it and convert to type T. If the value of the variable
+    // is incorrect, bad_conversion exception should be thrown.
+    //
+    // FetchValue() will return true on success convertion or false if specified
+    // variable is not presented.
+    template<typename T>
+    bool FetchValue(const char *name, T &value) const;
+
+public:
+    //
+    // FetchConfig() overrides the configuration with values fetched from the
+    // environment. Other values are not changed. The configuration wouldn't
+    // chang if exception were thrown. Error messages of runtime exceptions are
+    // complemented by information about the variable that caused the problem.
+    // config_error class is used for this exceptions.
+    //
+
+    void FetchConfig(LoggerConfig &config) const;
+
+    void FetchConfig(TraceLogConfig &config) const;
+
+    void FetchConfig(ProfilerConfig &config) const;
+};
+
+#endif // _ENVIRONMENT_CONFIG_PROVIDER_H_
diff --git a/src/config/loggerconfig.cpp b/src/config/loggerconfig.cpp
new file mode 100644 (file)
index 0000000..8599da7
--- /dev/null
@@ -0,0 +1,36 @@
+#include "commonconfig.h"
+#include "loggerconfig.h"
+
+//
+// Configuration parameters should be assigned to its defaults here.
+//
+LoggerConfig::LoggerConfig()
+    : Level(LogLevel::Warn)
+    , OutputStream(LoggerOutputStream::Stdout)
+    , FileName()
+{}
+
+void LoggerConfig::Validate()
+{
+    if (OutputStream == LoggerOutputStream::File && FileName.empty())
+    {
+        throw config_error("file name is required for file output");
+    }
+}
+
+std::vector<std::string> LoggerConfig::Verify()
+{
+    std::vector<std::string> warnings;
+
+    if (!FileName.empty() && OutputStream != LoggerOutputStream::File)
+    {
+        warnings.push_back("file name is ignored for non-file output");
+    }
+
+    return warnings;
+}
+
+const char *LoggerConfig::Name()
+{
+    return "Logger configuration";
+}
diff --git a/src/config/loggerconfig.h b/src/config/loggerconfig.h
new file mode 100644 (file)
index 0000000..24b9290
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef _LOGGER_CONFIG_H_
+#define _LOGGER_CONFIG_H_
+
+#include <vector>
+#include <string>
+
+#include "log.h"
+
+//
+// The LoggerConfig structure describes configuration of the Logger.
+//
+
+enum class LoggerOutputStream
+{
+    Stdout,
+    Stderr,
+    File,
+};
+
+struct LoggerConfig
+{
+    // Creates configuration with default values.
+    LoggerConfig();
+
+    //
+    // Common options.
+    //
+
+    // The level of the Logger verbosity. Can be: None, Fatal, Error, Warn,
+    // Info, Debug, Trace, All. Each next level of verbosity enables all
+    // previous levels.
+    LogLevel Level;
+
+    // An output stream where the Logger puts information. Can be: Stdout,
+    // Stderr, File.
+    LoggerOutputStream OutputStream;
+
+    //
+    // File Output options.
+    //
+
+    // The name of the file or path to which is used by the Logger to store
+    // information when File output stream is used.
+    std::string FileName;
+
+    // TODO: other settings for File Output.
+
+    //
+    // Validation and verification.
+    //
+
+    void Validate();
+
+    std::vector<std::string> Verify();
+
+    const char *Name();
+};
+
+#endif // _LOGGER_CONFIG_H_
diff --git a/src/config/loggerconfigconversions.cpp b/src/config/loggerconfigconversions.cpp
new file mode 100644 (file)
index 0000000..c9e3429
--- /dev/null
@@ -0,0 +1,95 @@
+#include <stdlib.h>
+#include <errno.h>
+
+#include <strings.h>
+
+#include "loggerconfigconversions.h"
+
+template<>
+LogLevel convert(const char *str)
+{
+    if (strcasecmp(str, "None") == 0)
+    {
+        return LogLevel::None;
+    }
+    else if (strcasecmp(str, "Fatal") == 0)
+    {
+        return LogLevel::Fatal;
+    }
+    else if (strcasecmp(str, "Error") == 0)
+    {
+        return LogLevel::Error;
+    }
+    else if (strcasecmp(str, "Warn") == 0)
+    {
+        return LogLevel::Warn;
+    }
+    else if (strcasecmp(str, "Info") == 0)
+    {
+        return LogLevel::Info;
+    }
+    else if (strcasecmp(str, "Debug") == 0)
+    {
+        return LogLevel::Debug;
+    }
+    else if (strcasecmp(str, "Trace") == 0)
+    {
+        return LogLevel::Trace;
+    }
+    else if (strcasecmp(str, "All") == 0)
+    {
+        return LogLevel::All;
+    }
+
+    // Trying number values.
+
+    long value;
+    char *str_end;
+
+    errno = 0;
+    value = strtol(str, &str_end, 0);
+
+    if (errno == ERANGE)
+    {
+        throw bad_conversion("is out of range");
+    }
+
+    if (str == str_end)
+    {
+        throw bad_conversion("incorrect value for type LogLevel");
+    }
+
+    if (*str_end != '\0')
+    {
+        throw bad_conversion("contains not number symbols");
+    }
+
+    if (value < static_cast<long>(LogLevel::None) ||
+        value > static_cast<long>(LogLevel::All))
+    {
+        throw bad_conversion("is out of range");
+    }
+
+    return static_cast<LogLevel>(value);
+}
+
+template<>
+LoggerOutputStream convert(const char *str)
+{
+    if (strcasecmp(str, "Stdout") == 0)
+    {
+        return LoggerOutputStream::Stdout;
+    }
+    else if (strcasecmp(str, "Stderr") == 0)
+    {
+        return LoggerOutputStream::Stderr;
+    }
+    else if (strcasecmp(str, "File") == 0)
+    {
+        return LoggerOutputStream::File;
+    }
+    else
+    {
+        throw bad_conversion("incorrect value for type LoggerOutputStream");
+    }
+}
diff --git a/src/config/loggerconfigconversions.h b/src/config/loggerconfigconversions.h
new file mode 100644 (file)
index 0000000..ae0556d
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _LOGGER_CONFIG_CONVERSIONS_H_
+#define _LOGGER_CONFIG_CONVERSIONS_H_
+
+#include "loggerconfig.h"
+#include "commonconfig.h"
+
+template<>
+LogLevel convert(const char *str);
+
+template<>
+LoggerOutputStream convert(const char *str);
+
+#endif // _LOGGER_CONFIG_CONVERSIONS_H_
diff --git a/src/config/profilerconfig.cpp b/src/config/profilerconfig.cpp
new file mode 100644 (file)
index 0000000..4bd262b
--- /dev/null
@@ -0,0 +1,131 @@
+#include <assert.h>
+
+#include "commonconfig.h"
+#include "profilerconfig.h"
+
+//
+// Configuration parameters should be assigned to its defaults here.
+//
+ProfilerConfig::ProfilerConfig()
+    : CollectionMethod(CollectionMethod::None)
+    , SamplingTimeoutMs(10)
+    , HighGranularityEnabled(true)
+    , TracingSuspendedOnStart(false)
+    , LineTraceEnabled(false)
+    , CpuTraceProcessEnabled(false)
+    , CpuTraceThreadEnabled(false)
+    , CpuTraceTimeoutMs(10)
+    , ExecutionTraceEnabled(false)
+    , MemoryTraceEnabled(false)
+    , StackTrackingEnabled(true)
+{}
+
+void ProfilerConfig::Validate()
+{
+    if (CollectionMethod == CollectionMethod::Sampling)
+    {
+        if (SamplingTimeoutMs == 0)
+        {
+            throw config_error("sampling timeout should be non-zero");
+        }
+    }
+
+    if (CpuTraceProcessEnabled || CpuTraceThreadEnabled)
+    {
+        if (CpuTraceTimeoutMs == 0)
+        {
+            throw config_error("CPU tracing timeout should be non-zero");
+        }
+    }
+}
+
+std::vector<std::string> ProfilerConfig::Verify()
+{
+    std::vector<std::string> warnings;
+
+    if (CollectionMethod != CollectionMethod::Instrumentation)
+    {
+        // Instrumentation specific options verification.
+    }
+    else
+    {
+        if (StackTrackingEnabled)
+        {
+            warnings.push_back(
+                "stack tracking option is redundant for instrumentation");
+        }
+    }
+
+    if (CollectionMethod != CollectionMethod::Sampling)
+    {
+        // Sampling specific options verification.
+
+        if (SamplingTimeoutMs != 0)
+        {
+            warnings.push_back(
+                "sampling timeout specification requires sampling");
+        }
+
+        if (HighGranularityEnabled)
+        {
+            // We don't show this message if sampling have been required for
+            // line tracing above.
+            warnings.push_back("hight granularity option requires sampling");
+        }
+    }
+
+    if (CollectionMethod == CollectionMethod::None)
+    {
+        // Common options verification.
+
+        if (LineTraceEnabled)
+        {
+            warnings.push_back(
+                "line tracing requires sampling or instrumentation");
+        }
+    }
+
+    if (!CpuTraceProcessEnabled && !CpuTraceThreadEnabled)
+    {
+        // CPU Trace specific options verification.
+
+        if (CpuTraceTimeoutMs != 0)
+        {
+            warnings.push_back(
+                "CPU tracing timeout specified when tracing disabled");
+        }
+    }
+
+    if (!ExecutionTraceEnabled && !MemoryTraceEnabled)
+    {
+        // When all traces are disabled.
+
+        if (CollectionMethod != CollectionMethod::None)
+        {
+            warnings.push_back(
+                "collection method specification requires execution or "
+                "memory tracing");
+        }
+
+        if (LineTraceEnabled)
+        {
+            warnings.push_back(
+                "line tracing requires execution or memory tracing");
+        }
+    }
+
+    if (!MemoryTraceEnabled)
+    {
+        if (StackTrackingEnabled)
+        {
+            warnings.push_back("stack tracking is memory tracing option");
+        }
+    }
+
+    return warnings;
+}
+
+const char *ProfilerConfig::Name()
+{
+    return "Profiler configuration";
+}
diff --git a/src/config/profilerconfig.h b/src/config/profilerconfig.h
new file mode 100644 (file)
index 0000000..93bb604
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef _PROFILER_CONFIG_H_
+#define _PROFILER_CONFIG_H_
+
+#include <vector>
+#include <string>
+
+//
+// The ProfilerConfig structure describes configuration of the Profiler.
+//
+// NOTE: structure of the configuration can be changed when appropriate logic
+// will be implemented.
+//
+// TODO: configuration parameters should be described after they will be used
+// in the implementation.
+//
+
+enum class CollectionMethod
+{
+    None,
+    Instrumentation,
+    Sampling,
+};
+
+struct ProfilerConfig
+{
+    // Creates configuration with default values.
+    ProfilerConfig();
+
+    //
+    // Common Trace features.
+    //
+
+    CollectionMethod CollectionMethod;
+    unsigned long    SamplingTimeoutMs;
+    bool             HighGranularityEnabled;
+    bool             TracingSuspendedOnStart;
+    bool             LineTraceEnabled;
+
+    //
+    // CPU Trace features.
+    //
+
+    bool             CpuTraceProcessEnabled;
+    bool             CpuTraceThreadEnabled;
+    unsigned long    CpuTraceTimeoutMs;
+
+    //
+    // Execution Trace features.
+    //
+
+    bool             ExecutionTraceEnabled;
+
+    //
+    // Memory Trace features.
+    //
+
+    bool             MemoryTraceEnabled;
+    bool             StackTrackingEnabled;
+
+    //
+    // Validation and verification.
+    //
+
+    void Validate();
+
+    std::vector<std::string> Verify();
+
+    const char *Name();
+};
+
+#endif // _PROFILER_CONFIG_H_
diff --git a/src/config/profilerconfigconversions.cpp b/src/config/profilerconfigconversions.cpp
new file mode 100644 (file)
index 0000000..6ed0538
--- /dev/null
@@ -0,0 +1,46 @@
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+
+#include "profilerconfigconversions.h"
+
+template<>
+CollectionMethod convert(const char *str)
+{
+    if (strcasecmp(str, "None") == 0 || strlen(str) == 0)
+    {
+        return CollectionMethod::None;
+    }
+    else if (strcasecmp(str, "Instrumentation") == 0)
+    {
+        return CollectionMethod::Instrumentation;
+    }
+    else if (strcasecmp(str, "Sampling") == 0)
+    {
+        return CollectionMethod::Sampling;
+    }
+    else
+    {
+        throw bad_conversion("incorrect value for type CollectionMethod");
+    }
+}
+
+template<>
+const char *convert(CollectionMethod method)
+{
+    switch (method)
+    {
+    case CollectionMethod::None:
+        return "None";
+
+    case CollectionMethod::Instrumentation:
+        return "Instrumentation";
+
+    case CollectionMethod::Sampling:
+        return "Sampling";
+
+    default:
+        assert(!"Unreachable");
+        return "UNKNOWN";
+    }
+}
diff --git a/src/config/profilerconfigconversions.h b/src/config/profilerconfigconversions.h
new file mode 100644 (file)
index 0000000..dba3afb
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _PROFILER_CONFIG_CONVERSIONS_H_
+#define _PROFILER_CONFIG_CONVERSIONS_H_
+
+#include "profilerconfig.h"
+#include "commonconfig.h"
+
+template<>
+CollectionMethod convert(const char *str);
+
+template<>
+const char *convert(CollectionMethod method);
+
+#endif // _PROFILER_CONFIG_CONVERSIONS_H_
diff --git a/src/config/tracelogconfig.cpp b/src/config/tracelogconfig.cpp
new file mode 100644 (file)
index 0000000..f0465c7
--- /dev/null
@@ -0,0 +1,35 @@
+#include "commonconfig.h"
+#include "tracelogconfig.h"
+
+//
+// Configuration parameters should be assigned to its defaults here.
+//
+TraceLogConfig::TraceLogConfig()
+    : OutputStream(TraceLogOutputStream::Stdout)
+    , FileName()
+{}
+
+void TraceLogConfig::Validate()
+{
+    if (OutputStream == TraceLogOutputStream::File && FileName.empty())
+    {
+        throw config_error("file name is required for file output");
+    }
+}
+
+std::vector<std::string> TraceLogConfig::Verify()
+{
+    std::vector<std::string> warnings;
+
+    if (!FileName.empty() && OutputStream != TraceLogOutputStream::File)
+    {
+        warnings.push_back("file name is ignored for non-file output");
+    }
+
+    return warnings;
+}
+
+const char *TraceLogConfig::Name()
+{
+    return "TraceLog configuration";
+}
diff --git a/src/config/tracelogconfig.h b/src/config/tracelogconfig.h
new file mode 100644 (file)
index 0000000..716cc70
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef _TRACE_LOG_CONFIG_H_
+#define _TRACE_LOG_CONFIG_H_
+
+#include <vector>
+#include <string>
+
+//
+// The TraceLogConfig structure describes configuration of the TraceLog.
+//
+
+enum class TraceLogOutputStream
+{
+    Stdout,
+    Stderr,
+    File,
+};
+
+struct TraceLogConfig
+{
+    // Creates configuration with default values.
+    TraceLogConfig();
+
+    //
+    // Common options.
+    //
+
+    // An output stream where the TraceLog puts information. Can be: Stdout,
+    // Stderr, File.
+    TraceLogOutputStream OutputStream;
+
+    //
+    // File Output options.
+    //
+
+    // The name of the file or path to which is used by the TraceLog to store
+    // information when File output stream is used.
+    std::string FileName;
+
+    // TODO: other settings for File Output.
+
+    //
+    // Validation and verification.
+    //
+
+    void Validate();
+
+    std::vector<std::string> Verify();
+
+    const char *Name();
+};
+
+#endif // _TRACE_LOG_CONFIG_H_
diff --git a/src/config/tracelogconfigconversions.cpp b/src/config/tracelogconfigconversions.cpp
new file mode 100644 (file)
index 0000000..f7c13d2
--- /dev/null
@@ -0,0 +1,24 @@
+#include <strings.h>
+
+#include "tracelogconfigconversions.h"
+
+template<>
+TraceLogOutputStream convert(const char *str)
+{
+    if (strcasecmp(str, "Stdout") == 0)
+    {
+        return TraceLogOutputStream::Stdout;
+    }
+    else if (strcasecmp(str, "Stderr") == 0)
+    {
+        return TraceLogOutputStream::Stderr;
+    }
+    else if (strcasecmp(str, "File") == 0)
+    {
+        return TraceLogOutputStream::File;
+    }
+    else
+    {
+        throw bad_conversion("incorrect value for type TraceLogOutputStream");
+    }
+}
diff --git a/src/config/tracelogconfigconversions.h b/src/config/tracelogconfigconversions.h
new file mode 100644 (file)
index 0000000..a9da078
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _TRACE_LOG_CONFIG_CONVERSIONS_H_
+#define _TRACE_LOG_CONFIG_CONVERSIONS_H_
+
+#include "tracelogconfig.h"
+#include "commonconfig.h"
+
+template<>
+TraceLogOutputStream convert(const char *str);
+
+#endif // _TRACE_LOG_CONFIG_CONVERSIONS_H_
diff --git a/src/dllmain.cpp b/src/dllmain.cpp
new file mode 100644 (file)
index 0000000..62c31dc
--- /dev/null
@@ -0,0 +1,99 @@
+#include <new>
+
+#include <windows.h>
+
+#include "classfactory.h"
+#include "profilermanager.h"
+#include "profiler.h"
+
+#define  INITGUID
+#include "guid.h"
+#undef   INITGUID
+
+// This structure is used to declare a global list of coclasses. The class
+// factory object is created with a pointer to the correct one of these, so
+// that when create instance is called, it can be created.
+struct COCLASS_REGISTER
+{
+    const GUID     *pClsid;         // Class ID of the coclass.
+    LPCWSTR        szProgID;        // Prog ID of the class.
+    PFN_CREATE_OBJ pfnCreateObject; // Creation function to create instance.
+};
+
+// This map contains the list of coclasses which are exported from this module.
+const COCLASS_REGISTER g_CoClasses[] =
+{
+//   pClsid           szProgID       pfnCreateObject
+    {&CLSID_PROFILER, W("Profiler"), Profiler::CreateObject},
+    {NULL,            NULL,          NULL                  }
+};
+
+// An optional entry point into this DLL.
+EXTERN_C
+BOOL WINAPI DllMain(
+    HINSTANCE hinstDLL,    // A handle to the DLL module.
+    DWORD     fdwReason,   // The reason code that indicates
+                           // why the DLL entry-point function is being called.
+    LPVOID    lpvReserved) // The reserved parameter provides additional info
+                           // when the DLL is being loaded/unloaded.
+{
+    switch (fdwReason)
+    {
+    // The DLL is being loaded into the virtual address space of the current
+    // process. The lpReserved parameter indicates whether the DLL is being
+    // loaded statically (non-NULL) or dynamically (NULL).
+    case DLL_PROCESS_ATTACH:
+        // Disables the DLL_THREAD_ATTACH and DLL_THREAD_DETACH
+        // notifications for this DLL.
+        DisableThreadLibraryCalls(hinstDLL);
+        break;
+
+    // The DLL is being unloaded from the virtual address space of the calling
+    // process. The lpReserved parameter indicates whether the DLL is being
+    // unloaded as a result of a FreeLibrary call (NULL), a failure to load
+    // (NULL), or process termination (non-NULL).
+    case DLL_PROCESS_DETACH:
+        // Notify the Profiler Manager about detach event.
+        ProfilerManager::Instance().DllDetachShutdown();
+        break;
+
+    default:
+        break;
+    }
+
+    return TRUE;
+}
+
+// Retrieves the class object from a DLL object handler or object application.
+EXTERN_C HRESULT __stdcall DllGetClassObject(
+    REFCLSID rclsid, // The class to desired.
+    REFIID   riid,   // An interface wanted on the class factory.
+    LPVOID   *ppv)   // Return the interface pointer here.
+{
+    HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
+    CClassFactory *pClassFactory;     // To create class factory object.
+    const COCLASS_REGISTER *pCoClass; // Loop control.
+
+    // Scan for the right one.
+    for (pCoClass = g_CoClasses; pCoClass->pClsid != NULL; pCoClass++)
+    {
+        if (*pCoClass->pClsid == rclsid)
+        {
+            // Allocate the new factory object.
+            pClassFactory = new (std::nothrow) CClassFactory(
+                pCoClass->pfnCreateObject);
+            if (!pClassFactory)
+                return E_OUTOFMEMORY;
+
+            // Pick the v-table based on the caller's request.
+            hr = pClassFactory->QueryInterface(riid, ppv);
+
+            // Always release the local reference, if QI failed it will be
+            // the only one and the object gets freed.
+            pClassFactory->Release();
+            break;
+        }
+    }
+
+    return hr;
+}
diff --git a/src/guid.h b/src/guid.h
new file mode 100644 (file)
index 0000000..ec7585a
--- /dev/null
@@ -0,0 +1,3 @@
+#include <guiddef.h>
+
+DEFINE_GUID(CLSID_PROFILER, 0x101DA8FE, 0xFDCA, 0x4D0E, 0x97, 0x12, 0x76, 0x39, 0xCD, 0xE4, 0x8E, 0xBA);
diff --git a/src/i386/archhelpers.cpp b/src/i386/archhelpers.cpp
new file mode 100644 (file)
index 0000000..90408cb
--- /dev/null
@@ -0,0 +1,32 @@
+#include <sys/ucontext.h>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+HRESULT ContextToStackSnapshotContext(
+    const void *context, CONTEXT *winContext) noexcept
+{
+    _ASSERTE(context != nullptr && winContext != nullptr);
+
+    *winContext = {CONTEXT_INTEGER};
+    const mcontext_t *mc =
+        &(reinterpret_cast<const ucontext_t*>(context))->uc_mcontext;
+
+    {
+        winContext->Eax  = mc->gregs[REG_EAX];
+        winContext->Ebx  = mc->gregs[REG_EBX];
+        winContext->Ecx  = mc->gregs[REG_ECX];
+        winContext->Edx  = mc->gregs[REG_EDX];
+        winContext->Esi  = mc->gregs[REG_ESI];
+        winContext->Edi  = mc->gregs[REG_EDI];
+        winContext->Ebp  = mc->gregs[REG_EBP];
+        winContext->Esp  = mc->gregs[REG_ESP];
+        winContext->SegSs  = mc->gregs[REG_SS];
+        winContext->EFlags  = mc->gregs[REG_EFL];
+        winContext->Eip = mc->gregs[REG_EIP];
+        winContext->SegCs = mc->gregs[REG_CS];
+    }
+
+    return S_OK;
+}
diff --git a/src/i386/asmhelpers.S b/src/i386/asmhelpers.S
new file mode 100644 (file)
index 0000000..f8248ed
--- /dev/null
@@ -0,0 +1,74 @@
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+
+//
+// EXTERN_C void EnterNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY EnterNaked3, _TEXT, NoHandler
+    PROLOG_BEG
+    PROLOG_PUSH eax
+    PROLOG_PUSH ebx
+    PROLOG_PUSH ecx
+    PROLOG_PUSH edx
+    PROLOG_END
+    
+    mov ebx, [ebp+0x8]
+    push ebx
+    call    C_FUNC(EnterStub)
+    
+    EPILOG_BEG
+    EPILOG_POP edx
+    EPILOG_POP ecx
+    EPILOG_POP ebx
+    EPILOG_POP eax
+    EPILOG_END
+    ret 4
+NESTED_END EnterNaked3, _TEXT
+
+//
+// EXTERN_C void LeaveNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY LeaveNaked3, _TEXT, NoHandler
+    PROLOG_BEG
+    PROLOG_PUSH eax
+    PROLOG_PUSH ebx
+    PROLOG_PUSH ecx
+    PROLOG_PUSH edx
+    PROLOG_END
+    
+    mov ebx, [ebp+0x8]
+    push ebx
+    call    C_FUNC(LeaveStub)
+    
+    EPILOG_BEG
+    EPILOG_POP edx
+    EPILOG_POP ecx
+    EPILOG_POP ebx
+    EPILOG_POP eax
+    EPILOG_END
+    ret 4
+NESTED_END LeaveNaked3, _TEXT
+
+//
+// EXTERN_C void TailcallNaked3(FunctionIDOrClientID functionIDOrClientID);
+//
+NESTED_ENTRY TailcallNaked3, _TEXT, NoHandler
+    PROLOG_BEG
+    PROLOG_PUSH eax
+    PROLOG_PUSH ebx
+    PROLOG_PUSH ecx
+    PROLOG_PUSH edx
+    PROLOG_END
+    
+    mov ebx, [ebp+0x8]
+    push ebx
+    call    C_FUNC(TailcallStub)
+    
+    EPILOG_BEG
+    EPILOG_POP edx
+    EPILOG_POP ecx
+    EPILOG_POP ebx
+    EPILOG_POP eax
+    EPILOG_END
+    ret 4
+NESTED_END TailcallNaked3, _TEXT
diff --git a/src/info/baseinfo.h b/src/info/baseinfo.h
new file mode 100644 (file)
index 0000000..ad415b2
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _BASE_INFO_H_
+#define _BASE_INFO_H_
+
+#include <stddef.h>
+
+// As struct we can use this type for overloading.
+struct InternalID
+{
+    size_t id;
+};
+
+struct BaseInfo
+{
+    InternalID internalId;
+};
+
+#endif // _BASE_INFO_H_
diff --git a/src/info/classinfo.cpp b/src/info/classinfo.cpp
new file mode 100644 (file)
index 0000000..8bd6dff
--- /dev/null
@@ -0,0 +1,665 @@
+#include <utility>
+#include <array>
+#include <vector>
+#include <exception>
+#include <stdexcept>
+
+#include "profiler.h"
+#include "classstorage.h"
+#include "default_delete.h"
+#include "classinfo.h"
+
+// static
+HRESULT ClassInfo::GetClassNameFromMetaData(
+    const Profiler &profiler,
+    IMetaDataImport *pMDImport,
+    mdToken classToken,
+    String &className,
+    ULONG32 *typeArgsCount) noexcept
+{
+    HRESULT hr = S_OK;
+
+    if (IsNilToken(classToken))
+    {
+        className =  W("<UNKNOWN>");
+        return hr;
+    }
+
+    try
+    {
+        std::vector<WCHAR> classNameBuffer;
+        ULONG classNameSize;
+        if (TypeFromToken(classToken) == mdtTypeDef)
+        {
+            DWORD dwTypeDefFlags;
+
+            hr = pMDImport->GetTypeDefProps(
+                /* [in]  type token  */ classToken,
+                /* [out] name buffer */ nullptr,
+                /* [in]  buffer size */ 0,
+                /* [out] name length */ &classNameSize,
+                /* [out] type flags  */ &dwTypeDefFlags,
+                /* [out] base type   */ nullptr
+            );
+            if (SUCCEEDED(hr))
+            {
+                classNameBuffer.resize(classNameSize);
+                // classNameBuffer.data() can be used safety now.
+                hr = pMDImport->GetTypeDefProps(
+                    /* [in]  type token  */ classToken,
+                    /* [out] name buffer */ classNameBuffer.data(),
+                    /* [in]  buffer size */ classNameSize,
+                    /* [out] name length */ &classNameSize,
+                    /* [out] type flags  */ nullptr,
+                    /* [out] base type   */ nullptr
+                );
+            }
+            if (FAILED(hr))
+            {
+                throw HresultException(
+                    "ClassInfo::GetClassNameFromMetaData(): "
+                    "GetTypeDefProps()", hr
+                );
+            }
+
+            if (IsTdNested(dwTypeDefFlags))
+            {
+                mdTypeDef enclosingClass;
+                hr = pMDImport->GetNestedClassProps(classToken, &enclosingClass);
+                if (SUCCEEDED(hr))
+                {
+                    hr = ClassInfo::GetClassNameFromMetaData(
+                        profiler, pMDImport, enclosingClass, className,
+                        typeArgsCount);
+                }
+                else
+                {
+                    throw HresultException(
+                        "ClassInfo::GetClassNameFromMetaData(): "
+                        "GetNestedClassProps()", hr
+                    );
+                }
+                className.append(1, '.').append(classNameBuffer.data());
+            }
+            else
+            {
+                className = classNameBuffer.data();
+            }
+        }
+        else if (TypeFromToken(classToken) == mdtTypeRef)
+        {
+            mdToken scopeToken;
+
+            hr = pMDImport->GetTypeRefProps(
+                /* [in]  type token  */ classToken,
+                /* [out] scope token */ &scopeToken,
+                /* [out] name buffer */ nullptr,
+                /* [in]  buffer size */ 0,
+                /* [out] name length */ &classNameSize
+            );
+            if (SUCCEEDED(hr))
+            {
+                classNameBuffer.resize(classNameSize);
+                // classNameBuffer.data() can be used safety now.
+                hr = pMDImport->GetTypeRefProps(
+                    /* [in]  type token  */ classToken,
+                    /* [out] scope token */ nullptr,
+                    /* [out] name buffer */ classNameBuffer.data(),
+                    /* [in]  buffer size */ classNameSize,
+                    /* [out] name length */ &classNameSize
+                );
+            }
+            if (FAILED(hr))
+            {
+                throw HresultException(
+                    "ClassInfo::GetClassNameFromMetaData(): "
+                    "GetTypeRefProps()", hr
+                );
+            }
+
+            if (TypeFromToken(scopeToken) == mdtTypeRef)
+            {
+                hr = ClassInfo::GetClassNameFromMetaData(
+                    profiler, pMDImport, scopeToken, className, typeArgsCount);
+                className.append(1, '.').append(classNameBuffer.data());
+            }
+            else
+            {
+                className = classNameBuffer.data();
+            }
+        }
+        else
+        {
+            throw std::logic_error(
+                "ClassInfo::GetClassNameFromMetaData(): Unexpected token type");
+        }
+
+        String::size_type pos = className.find_last_of('`');
+        if (pos != String::npos)
+        {
+            if (typeArgsCount)
+            {
+                ULONG32 count = PAL_wcstoul(
+                    className.data() + pos + 1, nullptr, 10);
+                *typeArgsCount += count;
+            }
+            className.erase(pos);
+        }
+        if (className.empty())
+        {
+            className = W("<EMPTY>");
+        }
+    }
+    catch (const std::exception &e)
+    {
+        className = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+// static
+ClassInfo::String ClassInfo::TypeArgName(
+    ULONG argIndex,
+    bool methodFormalArg)
+{
+    char argStart = methodFormalArg ? 'M' : 'T';
+    if (argIndex <= 6)
+    {
+        // The first 7 parameters are printed as M, N, O, P, Q, R, S
+        // or as T, U, V, W, X, Y, Z.
+        return String(1, argStart + argIndex);
+    }
+    else
+    {
+        // Everything after that as M7, M8, ... or T7, T8, ...
+        std::array<WCHAR, 4> argName;
+        _snwprintf_s(
+            argName.data(), argName.size(), _TRUNCATE, W("%c%u"), argStart, argIndex);
+        return argName.data();
+    }
+}
+
+// static
+void ClassInfo::AppendTypeArgNames(
+    String &str,
+    const std::vector<ClassInfo*> &typeArgs,
+    bool methodFormalArg)
+{
+    _ASSERTE(!typeArgs.empty());
+
+    str.append(1, W('<'));
+    for (ULONG i = 0; i < typeArgs.size(); i++)
+    {
+        if (i != 0)
+        {
+            str.append(W(", "));
+        }
+        str.append(ClassInfo::TypeArgName(i, methodFormalArg));
+        if (typeArgs[i])
+        {
+            str.append(W("=")).append(typeArgs[i]->name);
+        }
+    }
+    str.append(1, W('>'));
+}
+
+// static
+__forceinline ClassInfo::String ClassInfo::GetNameFromElementType(
+    CorElementType elementType) noexcept
+{
+    switch (elementType)
+    {
+    case ELEMENT_TYPE_VOID:
+        return W("System.Void");
+
+    case ELEMENT_TYPE_BOOLEAN:
+        return W("System.Boolean");
+
+    case ELEMENT_TYPE_CHAR:
+        return W("System.Char");
+
+    case ELEMENT_TYPE_I1:
+        return W("System.SByte");
+
+    case ELEMENT_TYPE_U1:
+        return W("System.Byte");
+
+    case ELEMENT_TYPE_I2:
+        return W("System.Int16");
+
+    case ELEMENT_TYPE_U2:
+        return W("System.UInt16");
+
+    case ELEMENT_TYPE_I4:
+        return W("System.Int32");
+
+    case ELEMENT_TYPE_U4:
+        return W("System.UInt32");
+
+    case ELEMENT_TYPE_I8:
+        return W("System.Int64");
+
+    case ELEMENT_TYPE_U8:
+        return W("System.UInt64");
+
+    case ELEMENT_TYPE_R4:
+        return W("System.Single");
+
+    case ELEMENT_TYPE_R8:
+        return W("System.Double");
+
+    case ELEMENT_TYPE_STRING:
+        return W("System.String");
+
+    case ELEMENT_TYPE_PTR:
+        return W("<UNKNOWN>*");
+
+    case ELEMENT_TYPE_BYREF:
+        return W("ref <UNKNOWN>");
+
+    case ELEMENT_TYPE_VALUETYPE:
+        return W("System.ValueType");
+
+    case ELEMENT_TYPE_CLASS:
+    case ELEMENT_TYPE_OBJECT:
+        return W("System.Object");
+
+    case ELEMENT_TYPE_ARRAY:
+    case ELEMENT_TYPE_SZARRAY:
+        return W("System.Array");
+
+    case ELEMENT_TYPE_TYPEDBYREF:
+        return W("System.TypedReference");
+
+    case ELEMENT_TYPE_I:
+        return W("System.IntPtr");
+
+    case ELEMENT_TYPE_U:
+        return W("System.UIntPtr");
+
+    default:
+        return W("<UNKNOWN>");
+    }
+}
+
+__forceinline HRESULT ClassInfo::InitializeArrayClass(
+    const Profiler &profiler,
+    ClassStorage &storage,
+    ClassID realClassID,
+    CorElementType elementType) noexcept
+{
+    HRESULT hr = S_OK;
+
+    try
+    {
+        if (realClassID != 0 &&
+            (
+                elementType != ELEMENT_TYPE_PTR   &&
+                elementType != ELEMENT_TYPE_BYREF &&
+                elementType != ELEMENT_TYPE_FNPTR
+            )
+        )
+        {
+            ClassInfo &realClass = storage.Place(realClassID).first;
+            hr = realClass.Initialize(profiler, storage);
+            // Class name of array class can contains type arguments
+            // of real class. Array brackets is calculated separately.
+            if (realClass.arrayBrackets.empty())
+            {
+                this->name = realClass.fullName;
+            }
+            else
+            {
+                this->name = realClass.name;
+            }
+            this->arrayBrackets = realClass.arrayBrackets.c_str();
+        }
+        else
+        {
+            this->name = ClassInfo::GetNameFromElementType(elementType);
+        }
+
+        _ASSERTE(this->rank >= 1);
+        this->arrayBrackets.insert(
+            0, String(1, W('['))
+                .append(this->rank - 1, W(','))
+                .append(1, W(']'))
+        );
+    }
+    catch (const std::exception &e)
+    {
+        this->name = W("<UNKNOWN>");
+        this->arrayBrackets = W("[?]");
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+__forceinline HRESULT ClassInfo::InitializeRegularClassName(
+    const Profiler &profiler,
+    const ProfilerInfo &info) noexcept
+{
+    HRESULT hr = S_OK;
+
+    try
+    {
+        IUnknown *pUnknown;
+        hr = info.v1()->GetModuleMetaData(
+            this->moduleId, ofRead, IID_IMetaDataImport, &pUnknown);
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "ClassInfo::InitializeRegularClassName(): "
+                "GetModuleMetaData()", hr
+            );
+        }
+        std::unique_ptr<IUnknown> pUnknownHolder(pUnknown);
+        IMetaDataImport *pMDImport = dynamic_cast<IMetaDataImport*>(pUnknown);
+
+        ULONG32 typeArgsSize = 0;
+        hr = ClassInfo::GetClassNameFromMetaData(
+            profiler, pMDImport, this->classToken, this->name, &typeArgsSize);
+        if (this->typeArgs.empty())
+        {
+            this->typeArgs.resize(typeArgsSize);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->name = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+__forceinline HRESULT ClassInfo::InitializeTypeArgs(
+    const Profiler &profiler,
+    ClassStorage &storage,
+    const ProfilerInfo &info,
+    ULONG32 typeArgsSize) noexcept
+{
+    HRESULT hrReturn = S_OK;
+    HRESULT hr;
+
+    try
+    {
+        if (typeArgsSize > 0)
+        {
+            this->typeArgs.resize(typeArgsSize);
+            std::vector<ClassID> typeArgIds(typeArgsSize);
+            // typeArgIds.data() can be used safety now.
+            hr = info.v2()->GetClassIDInfo2(
+                /* [in]  class ID              */ this->id,
+                /* [out] module ID             */ nullptr,
+                /* [out] class token           */ nullptr,
+                /* [out] parent class ID       */ nullptr,
+                /* [in]  type args buffer size */ typeArgsSize,
+                /* [out] number of type args   */ nullptr,
+                /* [out] type args buffer      */ typeArgIds.data()
+            );
+            if (FAILED(hr))
+            {
+                throw HresultException(
+                    "ClassInfo::InitializeTypeArgs(): "
+                    "GetClassIDInfo2()", hr
+                );
+            }
+
+            for (ULONG32 i = 0; i < typeArgsSize; i++)
+            {
+                try
+                {
+                    if (typeArgIds[i] != 0)
+                    {
+                        this->typeArgs[i] = &storage.Place(typeArgIds[i]).first;
+                        hr = this->typeArgs[i]->Initialize(profiler, storage);
+                    }
+                }
+                catch (const std::exception &e)
+                {
+                    hr = profiler.HandleException(e);
+                }
+                if (FAILED(hr) && SUCCEEDED(hrReturn))
+                {
+                    hrReturn = hr;
+                }
+            }
+        }
+    }
+    catch (const std::exception &e)
+    {
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    return hrReturn;
+}
+
+HRESULT ClassInfo::Initialize(
+    const Profiler &profiler,
+    ClassStorage &storage) noexcept
+{
+    HRESULT hrReturn = S_OK;
+    HRESULT hr;
+
+    if (this->isInitialized)
+    {
+        return hrReturn;
+    }
+
+    _ASSERTE(this->id != 0);
+    const ProfilerInfo &info = profiler.GetProfilerInfo();
+
+    try
+    {
+        //
+        // Get Common Info.
+        //
+
+        ClassID realClassID;
+        CorElementType elementType;
+        hr = info.v1()->IsArrayClass(
+            this->id, &elementType, &realClassID, &this->rank);
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "ClassInfo::Initialize(): IsArrayClass()", hr);
+        }
+
+        if (hr == S_OK)
+        {
+            //
+            // Array class handling.
+            //
+
+            if (rank == 0)
+            {
+                throw std::logic_error(
+                    "ClassInfo::Initialize(): IsArrayClass(): "
+                    "Zero rank for array class"
+                );
+            }
+
+            hr = this->InitializeArrayClass(
+                profiler, storage, realClassID, elementType);
+            if (FAILED(hr) && SUCCEEDED(hrReturn))
+            {
+                hrReturn = hr;
+            }
+        }
+        else if (hr == S_FALSE)
+        {
+            //
+            // Regular class handling.
+            //
+
+            if (rank != 0)
+            {
+                throw std::logic_error(
+                    "ClassInfo::Initialize(): IsArrayClass(): "
+                    "Non-zero rank for regular class"
+                );
+            }
+
+            ULONG32 typeArgsSize;
+            _ASSERTE(info.version() >= 2);
+            hr = info.v2()->GetClassIDInfo2(
+                /* [in]  class ID              */ this->id,
+                /* [out] module ID             */ &this->moduleId,
+                /* [out] class token           */ &this->classToken,
+                /* [out] parent class ID       */ nullptr,
+                /* [in]  type args buffer size */ 0,
+                /* [out] number of type args   */ &typeArgsSize,
+                /* [out] type args buffer      */ nullptr
+            );
+            if (FAILED(hr))
+            {
+                throw HresultException(
+                    "ClassInfo::Initialize(): GetClassIDInfo2()", hr);
+            }
+
+            //
+            // Get Type Arguments.
+            //
+
+            hr = this->InitializeTypeArgs(
+                profiler, storage, info, typeArgsSize);
+            if (FAILED(hr) && SUCCEEDED(hrReturn))
+            {
+                hrReturn = hr;
+            }
+
+            //
+            // Get Class Name.
+            //
+
+            hr = this->InitializeRegularClassName(profiler, info);
+            if (FAILED(hr) && SUCCEEDED(hrReturn))
+            {
+                hrReturn = hr;
+            }
+        }
+        else
+        {
+            throw std::logic_error(
+                "ClassInfo::Initialize(): IsArrayClass(): Unexpected HRESULT");
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->name = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    try
+    {
+        this->fullName = this->name;
+        _ASSERTE(this->arrayBrackets.empty() || this->typeArgs.empty());
+        if (!this->arrayBrackets.empty())
+        {
+            //
+            // Array class handling.
+            //
+
+            this->fullName.append(this->arrayBrackets);
+        }
+        else if (!this->typeArgs.empty())
+        {
+            //
+            // Generic class handling.
+            //
+
+            ClassInfo::AppendTypeArgNames(
+                this->fullName, this->typeArgs, false);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->fullName = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    this->isInitialized = true;
+    return hrReturn;
+}
+
+HRESULT ClassInfo::InitializeFromToken(
+    const Profiler &profiler,
+    ModuleID moduleId,
+    mdTypeDef classToken) noexcept
+{
+    HRESULT hrReturn = S_OK;
+    HRESULT hr;
+
+    if (this->isInitialized)
+    {
+        return hrReturn;
+    }
+
+    _ASSERTE(this->id == 0);
+    _ASSERTE(this->rank == 0);
+    _ASSERTE(this->arrayBrackets.empty());
+    const ProfilerInfo &info = profiler.GetProfilerInfo();
+
+    try
+    {
+        this->moduleId = moduleId;
+        this->classToken = classToken;
+
+        //
+        // Get Class Name.
+        //
+
+        hr = this->InitializeRegularClassName(profiler, info);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->name = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    try
+    {
+        this->fullName = this->name;
+        if (!this->typeArgs.empty())
+        {
+            ClassInfo::AppendTypeArgNames(
+                this->fullName, this->typeArgs, false);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->fullName = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    this->isInitialized = true;
+    return hrReturn;
+}
diff --git a/src/info/classinfo.h b/src/info/classinfo.h
new file mode 100644 (file)
index 0000000..f9f7dd4
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef _CLASS_INFO_
+#define _CLASS_INFO_
+
+#include <vector>
+#include <string>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "mappedinfo.h"
+
+class Profiler;
+
+class ProfilerInfo;
+
+class ClassStorage;
+
+struct ClassInfo : public MappedInfo<ClassID>
+{
+    typedef std::basic_string<WCHAR> String;
+
+    ModuleID  moduleId;
+    mdTypeDef classToken = mdTypeDefNil;
+    std::vector<ClassInfo*> typeArgs;
+    ULONG     rank;
+    String    name;
+    String    arrayBrackets;
+    String    fullName;
+    bool      isInitialized;
+    bool      isNamePrinted;
+
+public:
+    static HRESULT GetClassNameFromMetaData(
+        const Profiler &profiler,
+        IMetaDataImport *pMDImport,
+        mdToken classToken,
+        String &className,
+        ULONG32 *typeArgsCount) noexcept;
+
+    static String TypeArgName(
+        ULONG argIndex,
+        bool methodFormalArg);
+
+    static void AppendTypeArgNames(
+        String &str,
+        const std::vector<ClassInfo*> &typeArgs,
+        bool methodFormalArg);
+
+private:
+    static String GetNameFromElementType(
+        CorElementType elementType);
+
+    HRESULT InitializeArrayClass(
+        const Profiler &profiler,
+        ClassStorage &storage,
+        ClassID realClassID,
+        CorElementType elementType) noexcept;
+
+    HRESULT InitializeRegularClassName(
+        const Profiler &profiler,
+        const ProfilerInfo &info) noexcept;
+
+    HRESULT InitializeTypeArgs(
+        const Profiler &profiler,
+        ClassStorage &storage,
+        const ProfilerInfo &info,
+        ULONG32 typeArgsSize) noexcept;
+
+public:
+    HRESULT Initialize(
+        const Profiler &profiler,
+        ClassStorage &storage) noexcept;
+
+    HRESULT InitializeFromToken(
+        const Profiler &profiler,
+        ModuleID moduleId,
+        mdTypeDef classToken) noexcept;
+};
+
+#endif // _CLASS_INFO_
diff --git a/src/info/functioninfo.cpp b/src/info/functioninfo.cpp
new file mode 100644 (file)
index 0000000..11977c1
--- /dev/null
@@ -0,0 +1,900 @@
+#include <utility>
+#include <vector>
+#include <exception>
+#include <stdexcept>
+
+//#include <corhlpr.h>
+
+#include "profiler.h"
+#include "classstorage.h"
+#include "classinfo.h"
+#include "default_delete.h"
+#include "functioninfo.h"
+
+inline bool isCallConv(unsigned sigByte, CorCallingConvention conv)
+{
+    return ((sigByte & IMAGE_CEE_CS_CALLCONV_MASK) == (unsigned) conv);
+}
+
+static __forceinline ULONG CorSigUncompressDataWrapper(
+    PCCOR_SIGNATURE &sigBlob, ULONG &sigBlobSize)
+{
+    HRESULT hr;
+    ULONG dataOut;
+    ULONG dataLen;
+
+    hr = CorSigUncompressData(sigBlob, sigBlobSize, &dataOut, &dataLen);
+    if (FAILED(hr))
+    {
+        throw HresultException("CorSigUncompressData()", hr);
+    }
+    sigBlob += dataLen;
+    sigBlobSize -= dataLen;
+
+    return dataOut;
+}
+
+static __forceinline mdToken CorSigUncompressTokenWrapper(
+    PCCOR_SIGNATURE &sigBlob, ULONG &sigBlobSize)
+{
+    HRESULT hr;
+    mdToken token;
+    ULONG tokenLen;
+
+    hr = CorSigUncompressToken(sigBlob, sigBlobSize, &token, &tokenLen);
+    if (FAILED(hr))
+    {
+        throw HresultException("CorSigUncompressToken()", hr);
+    }
+    sigBlob += tokenLen;
+    sigBlobSize -= tokenLen;
+
+    return token;
+}
+
+static __forceinline ULONG CorSigUncompressCallingConvWrapper(
+    PCCOR_SIGNATURE &sigBlob, ULONG &sigBlobSize)
+{
+    HRESULT hr;
+    ULONG callConv;
+
+    hr = CorSigUncompressCallingConv(sigBlob, sigBlobSize, &callConv);
+    if (FAILED(hr))
+    {
+        throw HresultException("CorSigUncompressCallingConv()", hr);
+    }
+    sigBlob++;
+    sigBlobSize--;
+
+    return callConv;
+}
+
+static __forceinline CorElementType CorSigUncompressElementTypeWrapper(
+    PCCOR_SIGNATURE &sigBlob, ULONG &sigBlobSize)
+{
+    if (sigBlobSize > 0)
+    {
+        sigBlobSize--;
+        return CorSigUncompressElementType(sigBlob);
+    }
+    else
+    {
+        throw HresultException(
+            "CorSigUncompressElementType()", META_E_BAD_SIGNATURE);
+    }
+}
+
+// static
+void FunctionInfo::ParseElementType(
+    const Profiler &profiler,
+    IMetaDataImport *pMDImport,
+    PCCOR_SIGNATURE &sigBlob,
+    ULONG &sigBlobSize,
+    String &str,
+    String &arrayBrackets)
+{
+    bool reiterate;
+    ULONG n;
+    bool methodFormalArg;
+    String appendix;
+
+    do {
+        reiterate = false;
+        switch (CorSigUncompressElementTypeWrapper(sigBlob, sigBlobSize))
+        {
+        case ELEMENT_TYPE_VOID:
+            str.append(W("void"));
+            break;
+
+        case ELEMENT_TYPE_BOOLEAN:
+            str.append(W("bool"));
+            break;
+
+        case ELEMENT_TYPE_CHAR:
+            str.append(W("char"));
+            break;
+
+        case ELEMENT_TYPE_I1:
+            str.append(W("sbyte"));
+            break;
+
+        case ELEMENT_TYPE_U1:
+            str.append(W("byte"));
+            break;
+
+        case ELEMENT_TYPE_I2:
+            str.append(W("short"));
+            break;
+
+        case ELEMENT_TYPE_U2:
+            str.append(W("ushort"));
+            break;
+
+        case ELEMENT_TYPE_I4:
+            str.append(W("int"));
+            break;
+
+        case ELEMENT_TYPE_U4:
+            str.append(W("uint"));
+            break;
+
+        case ELEMENT_TYPE_I8:
+            str.append(W("long"));
+            break;
+
+        case ELEMENT_TYPE_U8:
+            str.append(W("ulong"));
+            break;
+
+        case ELEMENT_TYPE_R4:
+            str.append(W("float"));
+            break;
+
+        case ELEMENT_TYPE_R8:
+            str.append(W("double"));
+            break;
+
+        case ELEMENT_TYPE_STRING:
+            str.append(W("string"));
+            break;
+
+        case ELEMENT_TYPE_TYPEDBYREF:
+            str.append(W("System.TypedReference"));
+            break;
+
+        case ELEMENT_TYPE_I:
+            str.append(W("System.IntPtr"));
+            break;
+
+        case ELEMENT_TYPE_U:
+            str.append(W("System.UIntPtr"));
+            break;
+
+        case ELEMENT_TYPE_OBJECT:
+            str.append(W("object"));
+            break;
+
+        case ELEMENT_TYPE_VALUETYPE:
+        case ELEMENT_TYPE_CLASS:
+            {
+                mdToken token = CorSigUncompressTokenWrapper(
+                    sigBlob, sigBlobSize);
+                String tmp;
+                ClassInfo::GetClassNameFromMetaData(
+                    profiler, pMDImport, token, tmp, nullptr);
+                str.append(tmp);
+            }
+            break;
+
+        case ELEMENT_TYPE_VAR:
+            methodFormalArg = false;
+            goto TYPE_ARG;
+
+        case ELEMENT_TYPE_MVAR:
+            methodFormalArg = true;
+            goto TYPE_ARG;
+
+        TYPE_ARG:
+            n = CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+            str.append(ClassInfo::TypeArgName(n, methodFormalArg));
+            break;
+
+        case ELEMENT_TYPE_GENERICINST:
+            {
+                String arrayBrackets;
+                FunctionInfo::ParseElementType(
+                    profiler, pMDImport, sigBlob, sigBlobSize,
+                    str, arrayBrackets);
+                if (!arrayBrackets.empty())
+                {
+                    throw std::logic_error(
+                        "FunctionInfo::ParseElementType(): "
+                        "Parsing error: Can't parse generic instantiation: "
+                        "Instantiation of array class"
+                    );
+                }
+                str.append(1, '<');
+                n = CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+                for (ULONG i = 0; i < n; i++)
+                {
+                    if (i != 0)
+                    {
+                        str.append(W(", "));
+                    }
+                    String arrayBrackets;
+                    FunctionInfo::ParseElementType(
+                        profiler, pMDImport, sigBlob, sigBlobSize,
+                        str, arrayBrackets);
+                    str.append(arrayBrackets);
+                }
+                str.append(1, '>');
+            }
+            break;
+
+        case ELEMENT_TYPE_FNPTR:
+            {
+                String returnType;
+                String signature;
+                FunctionInfo::ParseSignature(
+                    profiler, pMDImport, sigBlob, sigBlobSize,
+                    returnType, signature);
+                str.append(returnType).append(W(" *")).append(signature);
+            }
+            break;
+
+        case ELEMENT_TYPE_ARRAY:
+            {
+                FunctionInfo::ParseElementType(
+                    profiler, pMDImport, sigBlob, sigBlobSize,
+                    str, arrayBrackets);
+                ULONG rank = CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+                if (rank == 0)
+                {
+                    throw std::logic_error(
+                        "FunctionInfo::ParseElementType(): "
+                        "Parsing error: Can't parse array class: "
+                        "Zero rank of array class"
+                    );
+                }
+
+                // Skip array sizes.
+                n = CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+                if (n > rank)
+                {
+                    throw std::logic_error(
+                        "FunctionInfo::ParseElementType(): "
+                        "Parsing error: Can't parse array class: "
+                        "Too many sizes"
+                    );
+                }
+                for(ULONG i = 0; i < n; i++)
+                {
+                    CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+                }
+
+                // Skip array lower bounds.
+                n = CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+                if (n > rank)
+                {
+                    throw std::logic_error(
+                        "FunctionInfo::ParseElementType(): "
+                        "Parsing error: Can't parse array class: "
+                        "Too many lower bounds"
+                    );
+                }
+                for(ULONG i = 0; i < n; i++)
+                {
+                    CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+                }
+
+                arrayBrackets.insert(
+                    0, String(1, W('['))
+                        .append(rank - 1, W(','))
+                        .append(1, W(']'))
+                );
+            }
+            break;
+
+        case ELEMENT_TYPE_SZARRAY:
+            FunctionInfo::ParseElementType(
+                profiler, pMDImport, sigBlob, sigBlobSize,
+                str, arrayBrackets);
+            arrayBrackets.insert(0, W("[]"));
+            break;
+
+        case ELEMENT_TYPE_BYREF:
+            str.append(W("ref "));
+            goto REITERATE;
+
+        case ELEMENT_TYPE_PTR:
+            appendix.insert(0, W("*"));
+            goto REITERATE;
+
+        case ELEMENT_TYPE_CMOD_OPT:
+        case ELEMENT_TYPE_CMOD_REQD:
+            // Skip class token.
+            CorSigUncompressTokenWrapper(sigBlob, sigBlobSize);
+        case ELEMENT_TYPE_PINNED:
+            goto REITERATE;
+
+        default:
+            throw std::logic_error(
+                "FunctionInfo::ParseElementType(): "
+                "Parsing error: Can't parse unknown element type"
+            );
+            break;
+
+        REITERATE:
+            reiterate = true;
+            break;
+        }
+    } while(reiterate);
+    str.append(appendix);
+}
+
+// static
+void FunctionInfo::ParseSignature(
+    const Profiler &profiler,
+    IMetaDataImport *pMDImport,
+    PCCOR_SIGNATURE &sigBlob,
+    ULONG &sigBlobSize,
+    String &returnType,
+    String &signature)
+{
+    ULONG argNum = 0;
+    ULONG argCount = 0;
+    bool argCountDetermined = false;
+    bool openBracketAppended = false;
+    bool closeBracketAppended = false;
+
+    try
+    {
+        // Get the calling convention out.
+        ULONG callConv = CorSigUncompressCallingConvWrapper(
+            sigBlob, sigBlobSize);
+
+        // Should not be a local variable, field or generic instantiation
+        // signature.
+        if (
+            isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) ||
+            isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD) ||
+            isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_GENERICINST)
+        )
+        {
+            throw std::logic_error(
+                "FunctionInfo::ParseSignature(): "
+                "Parsing error: Can't parse signature: "
+                "Unexpected calling convention"
+            );
+        }
+
+        // Skip the number of method type arguments for generic methods.
+        if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+        {
+            CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+        }
+
+        // Get the number of arguments.
+        argCount = CorSigUncompressDataWrapper(sigBlob, sigBlobSize);
+        argCountDetermined = true;
+
+        // Get the return type.
+        String type;
+        String arrayBrackets;
+        FunctionInfo::ParseElementType(
+            profiler, pMDImport, sigBlob, sigBlobSize, type, arrayBrackets);
+        returnType.append(type + arrayBrackets);
+
+        // Calculate signature.
+        signature.append(1, W('('));
+        openBracketAppended = true;
+        while(argNum < argCount)
+        {
+            String delimeter = argNum != 0 ? W(", ") : W("");
+
+            if (sigBlobSize == 0)
+            {
+                throw HresultException(
+                    "CorSigUncompressElementType()", META_E_BAD_SIGNATURE);
+            }
+            else if (*sigBlob == ELEMENT_TYPE_SENTINEL)
+            {
+                CorSigUncompressElementTypeWrapper(sigBlob, sigBlobSize);
+                signature.append(delimeter + W("..."));
+            }
+            else
+            {
+                String type;
+                String arrayBrackets;
+                FunctionInfo::ParseElementType(
+                    profiler, pMDImport, sigBlob, sigBlobSize,
+                    type, arrayBrackets);
+                signature.append(delimeter + type + arrayBrackets);
+                argNum++;
+            }
+        }
+        signature.append(1, W(')'));
+        closeBracketAppended = true;
+    }
+    catch (const std::exception &e)
+    {
+        if (returnType.empty())
+        {
+            returnType = W("<UNKNOWN>");
+        }
+
+        try
+        {
+            if (!openBracketAppended)
+            {
+                signature.append(1, W('('));
+            }
+            if (argCountDetermined)
+            {
+                for(; argNum < argCount; argNum++)
+                {
+                    if (argNum != 0)
+                    {
+                        signature.append(W(", <UNKNOWN>"));
+                    }
+                    else
+                    {
+                        signature.append(W("<UNKNOWN>"));
+                    }
+                }
+            }
+            else
+            {
+                signature.append(W("?"));
+            }
+            if (!closeBracketAppended)
+            {
+                signature.append(1, W(')'));
+            }
+        }
+        catch (const std::exception &e)
+        {
+            signature = W("(?)");
+        }
+
+        throw;
+    }
+}
+
+__forceinline HRESULT FunctionInfo::InitializeCodeInfo(
+    const Profiler &profiler,
+    const ProfilerInfo &info) noexcept
+{
+    HRESULT hr = S_OK;
+
+    try
+    {
+        _ASSERTE(info.version() >= 2);
+
+        ULONG32 size;
+        hr = info.v2()->GetCodeInfo2(this->id, 0, &size, nullptr);
+        if (SUCCEEDED(hr) && size > 0)
+        {
+            this->codeInfo.resize(size);
+            // codeInfo.data() can be used safety now.
+            hr = info.v2()->GetCodeInfo2(
+                this->id, size, nullptr, this->codeInfo.data());
+        }
+
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "FunctionInfo::InitializeCodeInfo(): GetCodeInfo2()", hr);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->codeInfo.clear();
+        this->codeInfo.shrink_to_fit();
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+__forceinline HRESULT FunctionInfo::InitializeILToNativeMapping(
+    const Profiler &profiler,
+    const ProfilerInfo &info) noexcept
+{
+    HRESULT hr;
+
+    try
+    {
+        ULONG32 size;
+        hr = info.v1()->GetILToNativeMapping(this->id, 0, &size, nullptr);
+        if (SUCCEEDED(hr) && size > 0)
+        {
+            this->ILToNativeMapping.resize(size);
+            // ILToNativeMapping.data() can be used safety now.
+            hr = info.v1()->GetILToNativeMapping(
+                this->id, size, &size, this->ILToNativeMapping.data());
+        }
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "FunctionInfo::InitializeILToNativeMapping(): "
+                "GetILToNativeMapping()", hr
+            );
+        }
+        else if (this->ILToNativeMapping.size() != size)
+        {
+            throw std::logic_error(
+                "FunctionInfo::InitializeILToNativeMapping(): "
+                "GetILToNativeMapping(): Unexpected map size"
+            );
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->ILToNativeMapping.clear();
+        this->ILToNativeMapping.shrink_to_fit();
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+__forceinline HRESULT FunctionInfo::InitializeFunctionName(
+    const Profiler &profiler,
+    IMetaDataImport *pMDImport,
+    ULONG funcNameSize) noexcept
+{
+    HRESULT hr = S_OK;
+
+    try
+    {
+        std::vector<WCHAR> funcNameBuffer(funcNameSize);
+        // funcNameBuffer.data() can be used safety now.
+        hr = pMDImport->GetMethodProps(
+            /* [in]  method token   */ this->funcToken,
+            /* [out] class token    */ nullptr,
+            /* [out] name buffer    */ funcNameBuffer.data(),
+            /* [in]  buffer size    */ funcNameSize,
+            /* [out] name length    */ nullptr,
+            /* [out] method flags   */ nullptr,
+            /* [out] signature blob */ nullptr,
+            /* [out] size of blob   */ nullptr,
+            /* [out] RVA pointer    */ nullptr,
+            /* [out] impl. flags    */ nullptr
+        );
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "FunctionInfo::InitializeFunctionName(): "
+                "GetMethodProps()", hr
+            );
+        }
+        this->name = funcNameBuffer.data();
+    }
+    catch (const std::exception &e)
+    {
+        this->name = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+__forceinline HRESULT FunctionInfo::InitializeOwnerClassFromClassId(
+    const Profiler &profiler,
+    ClassStorage &storage) noexcept
+{
+    HRESULT hr = S_OK;
+
+    try
+    {
+        this->ownerClass = &storage.Place(this->classId).first;
+        hr = this->ownerClass->Initialize(profiler, storage);
+    }
+    catch (const std::exception &e)
+    {
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+__forceinline HRESULT FunctionInfo::InitializeOwnerClassFromClassToken(
+    const Profiler &profiler,
+    ClassStorage &storage,
+    mdTypeDef classToken) noexcept
+{
+    HRESULT hr = S_OK;
+
+    try
+    {
+        this->ownerClass = &storage.Add();
+        hr = this->ownerClass->InitializeFromToken(
+            profiler, this->moduleId, classToken);
+    }
+    catch (const std::exception &e)
+    {
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+__forceinline HRESULT FunctionInfo::InitializeTypeArgs(
+    const Profiler &profiler,
+    ClassStorage &storage,
+    const ProfilerInfo &info,
+    ULONG32 typeArgsSize) noexcept
+{
+    HRESULT hrReturn = S_OK;
+    HRESULT hr;
+
+    try
+    {
+        if (typeArgsSize > 0)
+        {
+            this->typeArgs.resize(typeArgsSize);
+            std::vector<ClassID> typeArgIds(typeArgsSize);
+            // typeArgIds.data() can be used safety now.
+            hr = info.v2()->GetFunctionInfo2(
+                /* [in]  function ID           */ this->id,
+                /* [in]  frame info            */ 0,
+                /* [in]  class ID              */ nullptr,
+                /* [out] module ID             */ nullptr,
+                /* [out] function token        */ nullptr,
+                /* [in]  type args buffer size */ typeArgsSize,
+                /* [out] number of type args   */ nullptr,
+                /* [out] type args buffer      */ typeArgIds.data()
+            );
+            if (FAILED(hr))
+            {
+                throw HresultException(
+                    "FunctionInfo::InitializeTypeArgs(): "
+                    "GetFunctionInfo2()", hr
+                );
+            }
+
+            for (ULONG32 i = 0; i < typeArgsSize; i++)
+            {
+                try
+                {
+                    if (typeArgIds[i] != 0)
+                    {
+                        this->typeArgs[i] = &storage.Place(typeArgIds[i]).first;
+                        hr = this->typeArgs[i]->Initialize(profiler, storage);
+                    }
+                }
+                catch (const std::exception &e)
+                {
+                    hr = profiler.HandleException(e);
+                }
+                if (FAILED(hr) && SUCCEEDED(hrReturn))
+                {
+                    hrReturn = hr;
+                }
+            }
+        }
+    }
+    catch (const std::exception &e)
+    {
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    return hrReturn;
+}
+
+__forceinline HRESULT FunctionInfo::InitializeSignature(
+    const Profiler &profiler,
+    IMetaDataImport *pMDImport,
+    PCCOR_SIGNATURE &sigBlob,
+    ULONG &sigBlobSize) noexcept
+{
+    HRESULT hr = S_OK;
+
+    try
+    {
+        FunctionInfo::ParseSignature(
+            profiler, pMDImport, sigBlob, sigBlobSize,
+            this->returnType, this->signature);
+        if (sigBlobSize != 0)
+        {
+            profiler.LOG().Warn() <<
+                "FunctionInfo::InitializeSignature(): Ambiguous signature blob";
+        }
+    }
+    catch (const std::exception &e)
+    {
+        hr = profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT FunctionInfo::Initialize(
+    const Profiler &profiler,
+    ClassStorage &storage) noexcept
+{
+    HRESULT hrReturn = S_OK;
+    HRESULT hr;
+
+    if (this->isInitialized)
+    {
+        return hrReturn;
+    }
+
+    _ASSERTE(this->id != 0);
+    const ProfilerInfo &info = profiler.GetProfilerInfo();
+
+    hr = FunctionInfo::InitializeCodeInfo(profiler, info);
+    if (FAILED(hr) && SUCCEEDED(hrReturn))
+    {
+        hrReturn = hr;
+    }
+
+    hr = FunctionInfo::InitializeILToNativeMapping(profiler, info);
+    if (FAILED(hr) && SUCCEEDED(hrReturn))
+    {
+        hrReturn = hr;
+    }
+
+    try
+    {
+        //
+        // Get Common Info.
+        //
+
+        ULONG32 typeArgsSize;
+        _ASSERTE(info.version() >= 2);
+        hr = info.v2()->GetFunctionInfo2(
+            /* [in]  function ID           */ this->id,
+            /* [in]  frame info            */ 0,
+            /* [in]  class ID              */ &this->classId,
+            /* [out] module ID             */ &this->moduleId,
+            /* [out] function token        */ &this->funcToken,
+            /* [in]  type args buffer size */ 0,
+            /* [out] number of type args   */ &typeArgsSize,
+            /* [out] type args buffer      */ nullptr
+        );
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "FunctionInfo::Initialize(): GetFunctionInfo2()", hr);
+        }
+        else if (TypeFromToken(this->funcToken) != mdtMethodDef)
+        {
+            throw std::logic_error(
+                "FunctionInfo::Initialize(): "
+                "GetTokenAndMetaDataFromFunction(): Unexpected method token"
+            );
+        }
+
+        IUnknown *pUnknown;
+        hr = info.v1()->GetModuleMetaData(
+            this->moduleId, ofRead, IID_IMetaDataImport, &pUnknown);
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "FunctionInfo::Initialize(): GetModuleMetaData()", hr);
+        }
+        std::unique_ptr<IUnknown> pUnknownHolder(pUnknown);
+        IMetaDataImport *pMDImport = dynamic_cast<IMetaDataImport*>(pUnknown);
+
+        ULONG funcNameSize;
+        mdTypeDef classToken;
+        DWORD funcAttr;
+        PCCOR_SIGNATURE sigBlob;
+        ULONG sigBlobSize;
+        hr = pMDImport->GetMethodProps(
+            /* [in]  method token   */ this->funcToken,
+            /* [out] class token    */ &classToken,
+            /* [out] name buffer    */ nullptr,
+            /* [in]  buffer size    */ 0,
+            /* [out] name length    */ &funcNameSize,
+            /* [out] method flags   */ &funcAttr,
+            /* [out] signature blob */ &sigBlob,
+            /* [out] size of blob   */ &sigBlobSize,
+            /* [out] RVA pointer    */ nullptr,
+            /* [out] impl. flags    */ nullptr
+        );
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "FunctionInfo::Initialize(): GetMethodProps()", hr);
+        }
+
+        //
+        // Get Function Name.
+        //
+
+        hr = this->InitializeFunctionName(profiler, pMDImport, funcNameSize);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+
+        //
+        // Get Owner Class.
+        //
+
+        if (this->classId != 0)
+        {
+            hr = this->InitializeOwnerClassFromClassId(profiler, storage);
+        }
+        else
+        {
+            hr = this->InitializeOwnerClassFromClassToken(
+                profiler, storage, classToken);
+        }
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+
+        //
+        // Get Type Arguments.
+        //
+
+        hr = this->InitializeTypeArgs(
+            profiler, storage, info, typeArgsSize);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+
+        //
+        // Get Return Type and Signature.
+        //
+
+        hr = FunctionInfo::InitializeSignature(
+            profiler, pMDImport, sigBlob, sigBlobSize);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->name       = W("<UNKNOWN>");
+        this->returnType = W("<UNKNOWN>");
+        this->signature  = W("(?)");
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    try
+    {
+        if (this->ownerClass)
+        {
+            this->fullName = this->ownerClass->fullName;
+        }
+        else
+        {
+            this->fullName = W("<UNKNOWN>");
+        }
+        this->fullName.append(W("::")).append(this->name);
+        if (!this->typeArgs.empty())
+        {
+            ClassInfo::AppendTypeArgNames(
+                this->fullName, this->typeArgs, true);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        this->fullName = W("<UNKNOWN>");
+        hr = profiler.HandleException(e);
+        if (FAILED(hr) && SUCCEEDED(hrReturn))
+        {
+            hrReturn = hr;
+        }
+    }
+
+    this->isInitialized = true;
+    return hr;
+}
diff --git a/src/info/functioninfo.h b/src/info/functioninfo.h
new file mode 100644 (file)
index 0000000..2fb4071
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef _FUNCTION_INFO_
+#define _FUNCTION_INFO_
+
+#include <string>
+#include <vector>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "classinfo.h"
+#include "mappedinfo.h"
+
+class Profiler;
+
+class ProfilerInfo;
+
+class ClassStorage;
+
+class ExecutionTrace;
+
+struct FunctionInfo : public MappedInfo<FunctionID>
+{
+    typedef std::basic_string<WCHAR> String;
+
+    ExecutionTrace *executionTrace;
+    std::vector<COR_PRF_CODE_INFO> codeInfo;
+    std::vector<COR_DEBUG_IL_TO_NATIVE_MAP> ILToNativeMapping;
+    ModuleID    moduleId;
+    ModuleID    classId;
+    mdMethodDef funcToken = mdMethodDefNil;
+    ClassInfo*  ownerClass;
+    std::vector<ClassInfo*> typeArgs;
+    String      name;
+    String      fullName;
+    String      returnType;
+    String      signature;
+    bool        isInitialized;
+    bool        isNamePrinted;
+
+private:
+
+    static void ParseElementType(
+        const Profiler &profiler,
+        IMetaDataImport *pMDImport,
+        PCCOR_SIGNATURE &sigBlob,
+        ULONG &sigBlobSize,
+        String &str,
+        String &arrayBrackets);
+
+    static void ParseSignature(
+        const Profiler &profiler,
+        IMetaDataImport *pMDImport,
+        PCCOR_SIGNATURE &sigBlob,
+        ULONG &sigBlobSize,
+        String &returnType,
+        String &signature);
+
+    HRESULT InitializeCodeInfo(
+        const Profiler &profiler,
+        const ProfilerInfo &info) noexcept;
+
+    HRESULT InitializeILToNativeMapping(
+        const Profiler &profiler,
+        const ProfilerInfo &info) noexcept;
+
+    HRESULT InitializeFunctionName(
+        const Profiler &profiler,
+        IMetaDataImport *pMDImport,
+        ULONG funcNameSize) noexcept;
+
+    HRESULT InitializeOwnerClassFromClassId(
+        const Profiler &profiler,
+        ClassStorage &storage) noexcept;
+
+    HRESULT InitializeOwnerClassFromClassToken(
+        const Profiler &profiler,
+        ClassStorage &storage,
+        mdTypeDef classToken) noexcept;
+
+    HRESULT InitializeTypeArgs(
+        const Profiler &profiler,
+        ClassStorage &storage,
+        const ProfilerInfo &info,
+        ULONG32 typeArgsSize) noexcept;
+
+    HRESULT InitializeSignature(
+        const Profiler &profiler,
+        IMetaDataImport *pMDImport,
+        PCCOR_SIGNATURE &sigBlob,
+        ULONG &sigBlobSize) noexcept;
+
+public:
+    HRESULT Initialize(
+        const Profiler &profiler,
+        ClassStorage   &storage) noexcept;
+};
+
+#endif // _FUNCTION_INFO_
diff --git a/src/info/mappedinfo.h b/src/info/mappedinfo.h
new file mode 100644 (file)
index 0000000..2e66650
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _MAPPED_INFO_H_
+#define _MAPPED_INFO_H_
+
+#include "baseinfo.h"
+
+template<typename ID>
+struct MappedInfo : public BaseInfo
+{
+    ID id;
+};
+
+#endif // _MAPPED_INFO_H_
diff --git a/src/info/threadinfo.h b/src/info/threadinfo.h
new file mode 100644 (file)
index 0000000..e4e00af
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _THREAD_INFO_H_
+#define _THREAD_INFO_H_
+
+#include <atomic>
+
+#include <signal.h>
+#include <pthread.h>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "eventchannel.h"
+#include "mappedinfo.h"
+
+struct ThreadInfo : public MappedInfo<ThreadID>
+{
+    DWORD                 osThreadId;
+    pthread_t             nativeHandle;
+
+    DWORD64               lastUserTime;
+
+    EventChannel          eventChannel;
+    std::atomic_ulong     genTicks;
+    ULONG                 fixTicks;
+    volatile sig_atomic_t interruptible;
+    size_t                maxRestoreIpIdx;
+
+    ThreadInfo()
+        : osThreadId(0)
+        , nativeHandle()
+        , lastUserTime(0)
+        , eventChannel()
+        , genTicks(0)
+        , fixTicks(0)
+        , interruptible(false)
+        , maxRestoreIpIdx(0)
+    {}
+};
+
+#endif // _THREAD_INFO_H_
diff --git a/src/log.h b/src/log.h
new file mode 100644 (file)
index 0000000..5e980af
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,323 @@
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <iostream>
+#include <fstream>
+
+enum class LogLevel
+{
+    None,
+    Fatal,
+    Error,
+    Warn,
+    Info,
+    Debug,
+    Trace,
+    All
+};
+
+static inline const char *LogLevelName(LogLevel level)
+{
+    switch (level)
+    {
+    case LogLevel::None:
+        return "NONE";
+    case LogLevel::Fatal:
+        return "FATAL";
+    case LogLevel::Error:
+        return "ERROR";
+    case LogLevel::Warn:
+        return "WARN";
+    case LogLevel::Info:
+        return "INFO";
+    case LogLevel::Debug:
+        return "DEBUG";
+    case LogLevel::Trace:
+        return "TRACE";
+    case LogLevel::All:
+        return "ALL";
+    default:
+        return "";
+    }
+}
+
+#ifdef _DEBUG
+
+#include <utility>
+
+class Log
+{
+private:
+    class LogLine
+    {
+    public:
+        LogLine(LogLevel level, std::ostream *stream = nullptr)
+            : m_stream(stream)
+        {
+            if (m_stream)
+            {
+                *m_stream << "[" << LogLevelName(level) << "]\t";
+            }
+        }
+
+        LogLine(const LogLine&) = delete;
+
+        LogLine &operator=(const LogLine&) = delete;
+
+        LogLine(LogLine &&other)
+        {
+            m_stream = other.m_stream;
+            other.m_stream = nullptr;
+        }
+
+        LogLine &operator=(LogLine&&) = delete;
+
+        ~LogLine()
+        {
+            if (m_stream)
+            {
+                *m_stream << std::endl;
+            }
+        }
+
+        template<typename T>
+        LogLine &operator<<(T value)
+        {
+            if (m_stream)
+            {
+                *m_stream << value;
+            }
+            return *this;
+        }
+
+    private:
+        std::ostream *m_stream;
+    };
+
+public:
+    Log(LogLevel level, const std::string &filename)
+        : m_level(level)
+        , m_stream(new std::ofstream())
+        , m_stream_owner(true)
+    {
+        try
+        {
+            m_stream->exceptions(m_stream->exceptions() | std::ios::failbit);
+            static_cast<std::ofstream*>(m_stream)->open(filename);
+        }
+        catch (...)
+        {
+            delete m_stream;
+            throw;
+        }
+    }
+
+    explicit Log(const std::string &filename)
+        : Log(LogLevel::Warn, filename)
+    {}
+
+    Log(LogLevel level, std::ostream &stream)
+        : m_level(level)
+        , m_stream(&stream)
+        , m_stream_owner(false)
+    {}
+
+    explicit Log(LogLevel level)
+        : Log(level, std::cout)
+    {}
+
+    explicit Log(std::ostream &stream)
+        : Log(LogLevel::Warn, stream)
+    {}
+
+    Log()
+        : Log(LogLevel::Warn, std::cout)
+    {}
+
+    Log(const Log&) = delete;
+
+    Log &operator=(const Log&) = delete;
+
+    Log(Log &&other)
+    {
+        m_level        = other.m_level;
+        m_stream       = other.m_stream;
+        m_stream_owner = other.m_stream_owner;
+
+        other.m_level        = LogLevel::None;
+        other.m_stream       = nullptr;
+        other.m_stream_owner = false;
+    }
+
+    Log &operator=(Log &&other)
+    {
+        Log(std::move(other)).swap(*this);
+        return *this;
+    }
+
+    ~Log()
+    {
+        if (m_stream_owner)
+        {
+            delete m_stream;
+        }
+    }
+
+    void swap(Log &other)
+    {
+        std::swap(m_level,        other.m_level);
+        std::swap(m_stream,       other.m_stream);
+        std::swap(m_stream_owner, other.m_stream_owner);
+    }
+
+    std::ostream::iostate exceptions() const
+    {
+        return m_stream->exceptions();
+    }
+
+    void exceptions(std::ostream::iostate except)
+    {
+        m_stream->exceptions(except);
+    }
+
+    LogLine Fatal()
+    {
+        return DoLog<LogLevel::Fatal>();
+    }
+
+    LogLine Error()
+    {
+        return DoLog<LogLevel::Error>();
+    }
+
+    LogLine Warn()
+    {
+        return DoLog<LogLevel::Warn>();
+    }
+
+    LogLine Info()
+    {
+        return DoLog<LogLevel::Info>();
+    }
+
+    LogLine Debug()
+    {
+        return DoLog<LogLevel::Debug>();
+    }
+
+    LogLine Trace()
+    {
+        return DoLog<LogLevel::Trace>();
+    }
+
+private:
+    LogLevel      m_level;
+    std::ostream *m_stream;
+    bool          m_stream_owner;
+
+    template<LogLevel L>
+    LogLine DoLog()
+    {
+        // With RVO optimization LogLine destructor will be called only once.
+        // Otherwise with overloaded move constructor only last destructor call
+        // will print std::endl.
+        if (m_level >= L)
+        {
+            return LogLine(L, m_stream);
+        }
+        else
+        {
+            return LogLine(L);
+        }
+    }
+};
+
+#else // !_DEBUG
+
+class Log
+{
+private:
+    class LogLine
+    {
+    public:
+        LogLine() {}
+
+        LogLine(const LogLine&) = delete;
+
+        LogLine &operator=(const LogLine&) = delete;
+
+        LogLine(LogLine&&) = default;
+
+        LogLine &operator=(LogLine&&) = delete;
+
+        template<typename T>
+        LogLine &operator<<(T value)
+        {
+            return *this;
+        }
+    };
+
+public:
+    Log(LogLevel, const std::string&) {}
+
+    explicit Log(const std::string&) {}
+
+    Log(LogLevel, std::ostream&) {}
+
+    explicit Log(LogLevel) {}
+
+    explicit Log(std::ostream&) {}
+
+    Log() {}
+
+    Log(const Log&) = delete;
+
+    Log &operator=(const Log&) = delete;
+
+    Log(Log&&) = default;
+
+    Log &operator=(Log&&) = default;
+
+    void swap(Log &other) {}
+
+    std::ostream::iostate exceptions() const
+    {
+        return std::ostream::goodbit;
+    }
+
+    void exceptions(std::ostream::iostate except) {}
+
+    LogLine Fatal()
+    {
+        return LogLine();
+    }
+
+    LogLine Error()
+    {
+        return LogLine();
+    }
+
+    LogLine Warn()
+    {
+        return LogLine();
+    }
+
+    LogLine Info()
+    {
+        return LogLine();
+    }
+
+    LogLine Debug()
+    {
+        return LogLine();
+    }
+
+    LogLine Trace()
+    {
+        return LogLine();
+    }
+};
+
+#endif // !_DEBUG
+
+#endif // _LOG_H_
diff --git a/src/misc/default_delete.h b/src/misc/default_delete.h
new file mode 100644 (file)
index 0000000..e511f7d
--- /dev/null
@@ -0,0 +1,14 @@
+#include <utility>
+
+#include <cor.h>
+
+namespace std
+{
+    template<>
+    struct default_delete<IUnknown> {
+        void operator()(IUnknown* pUnknown)
+        {
+            pUnknown->Release();
+        }
+    };
+}
diff --git a/src/misc/intervalsplitter.h b/src/misc/intervalsplitter.h
new file mode 100644 (file)
index 0000000..4bf72b8
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef _INTERVAL_SPLITTER_H_
+#define _INTERVAL_SPLITTER_H_
+
+#include <assert.h>
+#include <math.h>
+
+class IntervalSplitter
+{
+public:
+    IntervalSplitter() noexcept = default;
+
+    explicit IntervalSplitter(unsigned long length) noexcept
+        : m_length(length)
+    {}
+
+    IntervalSplitter(unsigned long length, unsigned long count) noexcept
+        : m_length(length)
+        , m_count(count)
+    {}
+
+    void Reset(unsigned long count) noexcept
+    {
+        m_count   = count;
+        m_current = 0;
+        m_index   = 0;
+    }
+
+    void Reset(unsigned long length, unsigned long count) noexcept
+    {
+        m_length  = length;
+        m_count   = count;
+        m_current = 0;
+        m_index   = 0;
+    }
+
+    bool HasNext() noexcept
+    {
+        return m_index < m_count;
+    }
+
+    unsigned long GetNext() noexcept
+    {
+        assert(this->HasNext());
+        unsigned long prev = m_current;
+        m_current = llround(m_length * (++m_index / m_count));
+        return m_current - prev;
+    }
+
+private:
+    unsigned long m_length  = 0;
+    double        m_count   = 0;
+    unsigned long m_current = 0;
+    unsigned long m_index   = 0;
+};
+
+#endif // _INTERVAL_SPLITTER_H_
diff --git a/src/misc/iterator_range.h b/src/misc/iterator_range.h
new file mode 100644 (file)
index 0000000..1b9c0c8
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef _ITERATOR_RANGE_H_
+#define _ITERATOR_RANGE_H_
+
+#include <iterator>
+
+template<typename Iterator>
+class iterator_range
+{
+public:
+    //
+    // Types.
+    //
+
+    typedef typename std::iterator_traits<Iterator>::iterator_category
+        iterator_category;
+    typedef typename std::iterator_traits<Iterator>::value_type
+        value_type;
+    typedef typename std::iterator_traits<Iterator>::difference_type
+        difference_type;
+    typedef typename std::iterator_traits<Iterator>::reference
+        reference;
+    typedef typename std::iterator_traits<Iterator>::pointer
+        pointer;
+
+    //
+    // Constructors.
+    //
+
+    iterator_range() = default;
+
+    iterator_range(Iterator begin, Iterator end)
+        : m_begin(begin)
+        , m_end(end)
+    {}
+
+    //
+    // Iterator access.
+    //
+
+    Iterator begin() const
+    {
+        return m_begin;
+    }
+
+    Iterator end() const
+    {
+        return m_end;
+    }
+
+private:
+    Iterator m_begin;
+    Iterator m_end;
+};
+
+// Deducing constructor wrappers.
+template<typename Iterator>
+inline iterator_range<Iterator>
+make_iterator_range(Iterator begin, Iterator end)
+{
+    return iterator_range<Iterator>(begin, end);
+}
+
+#endif // _ITERATOR_RANGE_H_
diff --git a/src/misc/localtime.cpp b/src/misc/localtime.cpp
new file mode 100644 (file)
index 0000000..d733b3c
--- /dev/null
@@ -0,0 +1,63 @@
+#include <system_error>
+
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "localtime.h"
+
+VOID
+PALAPI
+GetLocalTime(OUT LPSYSTEMTIME lpLocalTime)
+{
+    time_t tt;
+    struct tm ut;
+    struct tm *utPtr;
+    struct timeval timeval;
+    int timeofday_retval;
+
+    tt = time(NULL);
+
+    /* We can't get millisecond resolution from time(), so we get it from
+       gettimeofday() */
+    timeofday_retval = gettimeofday(&timeval, NULL);
+
+    utPtr = &ut;
+    if (localtime_r(&tt, utPtr) == NULL)
+    {
+        throw std::system_error(errno, std::system_category(),
+            "localtime_r() failed");
+    }
+
+    lpLocalTime->wYear = 1900 + utPtr->tm_year;
+    lpLocalTime->wMonth = utPtr->tm_mon + 1;
+    lpLocalTime->wDayOfWeek = utPtr->tm_wday;
+    lpLocalTime->wDay = utPtr->tm_mday;
+    lpLocalTime->wHour = utPtr->tm_hour;
+    lpLocalTime->wMinute = utPtr->tm_min;
+    lpLocalTime->wSecond = utPtr->tm_sec;
+
+    if(-1 == timeofday_retval)
+    {
+        lpLocalTime->wMilliseconds = 0;
+        throw std::system_error(errno, std::system_category(),
+            "gettimeofday() failed");
+    }
+    else
+    {
+        int old_seconds;
+        int new_seconds;
+
+        lpLocalTime->wMilliseconds = timeval.tv_usec / 1000;
+
+        old_seconds = utPtr->tm_sec;
+        new_seconds = timeval.tv_sec%60;
+
+        /* just in case we reached the next second in the interval between
+           time() and gettimeofday() */
+        if(old_seconds != new_seconds)
+        {
+            lpLocalTime->wMilliseconds = 999;
+        }
+    }
+}
diff --git a/src/misc/localtime.h b/src/misc/localtime.h
new file mode 100644 (file)
index 0000000..e316556
--- /dev/null
@@ -0,0 +1,6 @@
+#include <pal.h>
+
+PALIMPORT
+VOID
+PALAPI
+GetLocalTime(OUT LPSYSTEMTIME lpLocalTime);
diff --git a/src/misc/shared_iterator_range.h b/src/misc/shared_iterator_range.h
new file mode 100644 (file)
index 0000000..18cfc7c
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _SHARED_ITERATOR_RANGE_H_
+#define _SHARED_ITERATOR_RANGE_H_
+
+#include <utility>
+
+#include "iterator_range.h"
+
+template<typename Iterator, typename Lock>
+class shared_iterator_range : public iterator_range<Iterator>
+{
+public:
+    shared_iterator_range(Iterator begin, Iterator end, Lock &&lock)
+        : iterator_range<Iterator>(begin, end)
+        , m_lock(std::forward<Lock>(lock))
+    {}
+
+private:
+    Lock m_lock;
+};
+
+// Deducing constructor wrappers.
+template<typename Iterator, typename Lock>
+inline shared_iterator_range<Iterator, Lock>
+make_shared_iterator_range(Iterator begin, Iterator end, Lock &&lock)
+{
+    return shared_iterator_range<Iterator, Lock>(
+        begin, end, std::forward<Lock>(lock));
+}
+
+#endif // _SHARED_ITERATOR_RANGE_H_
diff --git a/src/misc/sigaction.cpp b/src/misc/sigaction.cpp
new file mode 100644 (file)
index 0000000..f89a8b1
--- /dev/null
@@ -0,0 +1,119 @@
+#include <utility>
+#include <system_error>
+#include <exception>
+#include <stdexcept>
+
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "sigaction.h"
+
+// static
+void SigAction::SwapActions(
+    int signum,
+    const struct sigaction &newAction,
+    const struct sigaction &oldAction,
+    bool doFirstCheck)
+{
+    struct sigaction curAction;
+
+    if (doFirstCheck)
+    {
+        if (sigaction(signum, nullptr, &curAction))
+        {
+            throw std::system_error(errno, std::system_category(),
+                "SigAction::SwapActions(): sigaction()");
+        }
+
+        if (curAction.sa_handler   != oldAction.sa_handler ||
+            curAction.sa_sigaction != oldAction.sa_sigaction)
+        {
+            throw std::runtime_error(
+                "SigAction::SwapActions(): Signal handler was changed");
+        }
+    }
+
+    if (sigaction(signum, &newAction, &curAction)) {
+        throw std::system_error(errno, std::system_category(),
+            "SigAction::SwapActions(): sigaction()");
+    }
+    // NOTE: double-check of old action to avoid some race conditions.
+    if (curAction.sa_handler   != oldAction.sa_handler ||
+        curAction.sa_sigaction != oldAction.sa_sigaction)
+    {
+        sigaction(signum, &curAction, nullptr);
+        throw std::runtime_error(
+            "SigAction::SwapActions(): Signal handler was changed");
+    }
+}
+
+SigAction::SigAction()
+    : m_haveAction(false)
+{}
+
+SigAction::SigAction(int signum, const struct sigaction &action)
+    : m_haveAction(true)
+    , m_signum(signum)
+    , m_action(action)
+{
+    if (sigaction(signum, nullptr, &m_oldAction))
+    {
+        throw std::system_error(errno, std::system_category(),
+            "SigAction::SigAction(): sigaction()");
+    }
+
+    if (m_oldAction.sa_handler != SIG_DFL &&
+        m_oldAction.sa_handler != SIG_IGN)
+    {
+        throw std::runtime_error(
+            "SigAction::SigAction(): Signal handler was changed");
+    }
+
+    SigAction::SwapActions(m_signum, m_action, m_oldAction, false);
+}
+
+SigAction::SigAction(SigAction &&other) noexcept
+    : m_haveAction(false)
+{
+    *this = std::move(other);
+}
+
+SigAction::~SigAction()
+{
+    try
+    {
+        this->Release();
+    }
+    catch (...) {}
+}
+
+SigAction &SigAction::operator=(SigAction &&other)
+{
+    assert(this != &other);
+    this->Release();
+
+    m_haveAction = other.m_haveAction;
+    m_signum     = other.m_signum;
+    m_action     = other.m_action;
+    m_oldAction  = other.m_oldAction;
+
+    other.m_haveAction = false;
+    return *this;
+}
+
+SigAction::operator bool() const noexcept
+{
+    return m_haveAction;
+}
+
+void SigAction::Release()
+{
+    if (!m_haveAction)
+    {
+        return;
+    }
+
+    m_haveAction = false;
+    SigAction::SwapActions(m_signum, m_oldAction, m_action);
+}
diff --git a/src/misc/sigaction.h b/src/misc/sigaction.h
new file mode 100644 (file)
index 0000000..e7f427f
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _SIG_ACTION_H_
+#define _SIG_ACTION_H_
+
+#include <signal.h>
+
+class SigAction
+{
+private:
+    static void SwapActions(
+        int signum,
+        const struct sigaction &newAction,
+        const struct sigaction &oldAction,
+        bool doFirstCheck = true);
+
+public:
+    SigAction();
+
+    SigAction(int signum, const struct sigaction &action);
+
+    SigAction(const SigAction &) = delete;
+
+    SigAction(SigAction &&other) noexcept;
+
+    ~SigAction();
+
+    SigAction &operator=(const SigAction&) = delete;
+
+    SigAction &operator=(SigAction &&other);
+
+    explicit operator bool() const noexcept;
+
+    void Release();
+
+private:
+    bool m_haveAction;
+    int  m_signum;
+    struct sigaction m_action;
+    struct sigaction m_oldAction;
+};
+
+#endif // _SIG_ACTION_H_
diff --git a/src/profiler.cpp b/src/profiler.cpp
new file mode 100644 (file)
index 0000000..01f574a
--- /dev/null
@@ -0,0 +1,1122 @@
+#include <stdexcept>
+#include <system_error>
+#include <new>
+#include <iostream>
+#include <memory>
+
+#include <errno.h>
+#include <string.h>
+
+#include "localtime.h"
+#include "profilermanager.h"
+#include "profiler.h"
+
+// static
+HRESULT Profiler::CreateObject(
+    REFIID riid,
+    void **ppInterface) noexcept
+{
+    //
+    // We should perform some prechecks to avoid unnecessary initialization.
+    //
+
+    if (ppInterface == nullptr)
+        return E_POINTER;
+
+    *ppInterface = nullptr;
+
+    if (
+        (riid != IID_ICorProfilerCallback3) &&
+        (riid != IID_ICorProfilerCallback2) &&
+        (riid != IID_ICorProfilerCallback) &&
+        (riid != IID_IUnknown)
+    )
+    {
+        return E_NOINTERFACE;
+    }
+
+    // This profiler implements the "profile-first" alternative of dealing
+    // with multiple in-process side-by-side CLR instances. First CLR
+    // to try to load us into this process wins.
+    // TODO: remove this when Global Profiler Manager will be implemented
+    // (see https://blogs.msdn.microsoft.com/davbr/2010/08/25/profilers-in-process-side-by-side-clr-instances-and-a-free-test-harness/).
+    {
+        static volatile LONG s_nFirstTime = 1;
+        if (s_nFirstTime == 0)
+        {
+            // Someone beat us to it.
+            return CORPROF_E_PROFILER_CANCEL_ACTIVATION;
+        }
+
+        // Dirty-read says this is the first load. Double-check that
+        // with a clean-read.
+        if (InterlockedCompareExchange(&s_nFirstTime, 0, 1) == 0)
+        {
+            // Someone beat us to it.
+            return CORPROF_E_PROFILER_CANCEL_ACTIVATION;
+        }
+    }
+
+    //
+    // Profiler instantiation.
+    //
+
+    Profiler *pProfiler;
+    try
+    {
+        pProfiler = new (std::nothrow) Profiler();
+        if (!pProfiler)
+            return E_OUTOFMEMORY;
+    }
+    catch (...)
+    {
+        // Exceptions in the Profiler's constructor.
+        return E_FAIL;
+    }
+
+    //
+    // Profiler registration.
+    //
+
+    HRESULT hr = S_OK;
+    try
+    {
+        ProfilerManager::Instance().RegisterProfiler(pProfiler);
+    }
+    catch (const std::exception &e)
+    {
+        hr = pProfiler->HandleException(e);
+        delete pProfiler;
+        return hr;
+    }
+
+    //
+    // Preparing results for returning.
+    //
+
+    hr = pProfiler->QueryInterface(riid, ppInterface);
+    if (FAILED(hr))
+    {
+        pProfiler->HandleHresult("Profiler::CreateObject()", hr);
+    }
+    // Profiler already had 1 on reference counter so we need to call
+    // Release() after QueryInterface() to retrieve it to 1.
+    pProfiler->Release();
+
+    return hr;
+}
+
+// static
+void Profiler::RemoveObject(
+    Profiler *pProfiler) noexcept
+{
+    //
+    // We should unregister profiler, destruct it and remove it from the memory.
+    //
+    _ASSERTE(ProfilerManager::Instance().IsProfilerRegistered(pProfiler));
+    ProfilerManager::Instance().UnregisterProfiler(pProfiler);
+    delete pProfiler;
+}
+
+Profiler::Profiler()
+    : m_cRef(1)
+    , m_logger()
+    , m_initialized(false)
+    , m_shutdowned(false)
+    , m_loggerConfig()
+    , m_traceLogConfig()
+    , m_profConfig()
+    , m_info()
+    , m_traceLog()
+    , m_commonTrace(*this)    // Should be after m_logger, m_info and
+                              // m_traceLog.
+    , m_cpuTrace(*this)       // Should be after m_commonTrace.
+    , m_executionTrace(*this) // Should be after m_commonTrace.
+    , m_memoryTrace(*this)    // Should be after m_executionTrace.
+    , m_firstTickCount(0)
+{
+}
+
+Profiler::~Profiler()
+{
+}
+
+Log &Profiler::LOG() const noexcept
+{
+    return const_cast<Log&>(m_logger);
+}
+
+ITraceLog &Profiler::TRACE() const noexcept
+{
+    // NOTE: default-constructed TraceLog object should not be used for output!
+    _ASSERTE(m_traceLog != nullptr);
+    return const_cast<ITraceLog&>(*m_traceLog);
+}
+
+DWORD Profiler::GetTickCountFromInit() const noexcept
+{
+    return GetTickCount() - m_firstTickCount;
+}
+
+HRESULT Profiler::HandleException(const std::exception &e) const noexcept
+{
+    // Find type of exception.
+
+    const HresultException* pHresultException =
+        dynamic_cast<const HresultException*>(&e);
+
+    const std::system_error* p_system_error =
+        dynamic_cast<const std::system_error*>(&e);
+
+    const std::ios_base::failure* p_ios_base_failure =
+        dynamic_cast<const std::ios_base::failure*>(&e);
+
+    const std::bad_alloc* p_bad_alloc =
+        dynamic_cast<const std::bad_alloc*>(&e);
+
+    const std::logic_error* p_logic_error =
+        dynamic_cast<const std::logic_error*>(&e);
+
+    // Send information about the exception to the log.
+
+    try
+    {
+        if (pHresultException)
+        {
+            LOG().Error() << "Exception: " << pHresultException->what()
+                << " (HR = " << pHresultException->hresult() << ")";
+        }
+        else if (p_system_error)
+        {
+            LOG().Error() << "Exception: " << p_system_error->what()
+                << " (EC = " << p_system_error->code() << ")";
+        }
+        else if (p_ios_base_failure)
+        {
+            // std::ios_base::failure should be inherited from std::system_error
+            // for C++11. This workaround applied if it is not true.
+            LOG().Error() << "Exception: " << p_ios_base_failure->what() << ": "
+                << strerror(errno);
+        }
+        else
+        {
+            LOG().Error() << "Exception: " << e.what();
+        }
+    }
+    catch (...)
+    {
+        // We can do nothing with information about exception if logging failed.
+    }
+
+    // Return appropriate for the exception HRESULT.
+
+    if (pHresultException)
+    {
+        return pHresultException->hresult();
+    }
+    else if (p_bad_alloc)
+    {
+        return E_OUTOFMEMORY;
+    }
+    else if (p_logic_error)
+    {
+        return E_UNEXPECTED;
+    }
+    else
+    {
+        return E_FAIL;
+    }
+}
+
+void Profiler::HandleSysErr(
+    const std::string& what_arg, int ev) const noexcept
+{
+    this->HandleException(
+        std::system_error(ev, std::system_category(), what_arg)
+    );
+}
+
+void Profiler::HandleHresult(
+    const std::string& what_arg, HRESULT hr) const noexcept
+{
+    this->HandleException(HresultException(what_arg, hr));
+}
+
+ProfilerConfig &Profiler::GetConfig() noexcept
+{
+    return m_profConfig;
+}
+
+const ProfilerInfo &Profiler::GetProfilerInfo() const noexcept
+{
+    return m_info;
+}
+
+CommonTrace &Profiler::GetCommonTrace() noexcept
+{
+    return m_commonTrace;
+}
+
+CpuTrace &Profiler::GetCpuTrace() noexcept
+{
+    return m_cpuTrace;
+}
+
+ExecutionTrace &Profiler::GetExecutionTrace() noexcept
+{
+    return m_executionTrace;
+}
+
+MemoryTrace &Profiler::GetMemoryTrace() noexcept
+{
+    return m_memoryTrace;
+}
+
+void Profiler::SetupLogging(LoggerConfig &config)
+{
+    if (config.OutputStream == LoggerOutputStream::Stdout)
+    {
+        m_logger = Log(config.Level, std::cout);
+    }
+    else if (config.OutputStream == LoggerOutputStream::Stderr)
+    {
+        m_logger = Log(config.Level, std::cerr);
+    }
+    else if (config.OutputStream == LoggerOutputStream::File)
+    {
+        m_logger = Log(config.Level, config.FileName);
+    }
+
+    // Disabling exceptions so Logger can be used without exceptions checking.
+    m_logger.exceptions(std::ostream::goodbit);
+}
+
+void Profiler::SetupTraceLog(TraceLogConfig &config)
+{
+    if (config.OutputStream == TraceLogOutputStream::Stdout)
+    {
+        m_traceLog.reset(ITraceLog::Create(ITraceLog::StdOutStream));
+    }
+    else if (config.OutputStream == TraceLogOutputStream::Stderr)
+    {
+        m_traceLog.reset(ITraceLog::Create(ITraceLog::StdErrStream));
+    }
+    else if (config.OutputStream == TraceLogOutputStream::File)
+    {
+        m_traceLog.reset(
+            ITraceLog::Create(ITraceLog::FileStream, config.FileName));
+    }
+}
+
+void Profiler::ProcessConfig(ProfilerConfig &config)
+{
+    // Check method is called during initialization phase.
+    _ASSERTE(m_initialized == false);
+
+    // Ensure the Profiler Info is initialized.
+    _ASSERTE(m_info.v1() != nullptr);
+
+    m_commonTrace.ProcessConfig(config);
+    m_cpuTrace.ProcessConfig(config);
+    m_executionTrace.ProcessConfig(config);
+    m_memoryTrace.ProcessConfig(config);
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::QueryInterface(
+    REFIID riid,
+    void **ppvObject)
+{
+    if (ppvObject == nullptr)
+        return E_POINTER;
+
+    // Pick the right v-table based on the IID passed in.
+    if (riid == IID_ICorProfilerCallback3)
+    {
+        *ppvObject = static_cast<ICorProfilerCallback3*>(this);
+    }
+    else if (riid == IID_ICorProfilerCallback2)
+    {
+        *ppvObject = static_cast<ICorProfilerCallback2*>(this);
+    }
+    else if (riid == IID_ICorProfilerCallback)
+    {
+        *ppvObject = static_cast<ICorProfilerCallback*>(this);
+    }
+    else if (riid == IID_IUnknown)
+    {
+        *ppvObject = static_cast<IUnknown*>(this);
+    }
+    else
+    {
+        *ppvObject = nullptr;
+        return E_NOINTERFACE;
+    }
+
+    // If successful, add a reference for out pointer and return.
+    this->AddRef();
+
+    return S_OK;
+}
+
+ULONG STDMETHODCALLTYPE Profiler::AddRef()
+{
+    return InterlockedIncrement(&m_cRef);
+}
+
+ULONG STDMETHODCALLTYPE Profiler::Release()
+{
+    LONG result = InterlockedDecrement(&m_cRef);
+    if (result == 0)
+    {
+        // Notify the Global Area that the profiler instance is not longer used.
+        Profiler::RemoveObject(this);
+    }
+
+    return result;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::Initialize(
+    IUnknown *pICorProfilerInfoUnk)
+{
+    // Check method is called only once.
+    _ASSERTE(m_initialized == false);
+
+    SYSTEMTIME systime;
+    GetLocalTime(&systime);
+    m_firstTickCount = GetTickCount();
+
+    HRESULT hr = S_OK;
+
+    try {
+        //
+        // Fetching the Configuration and setup logging.
+        //
+
+        m_loggerConfig = ProfilerManager::Instance().FetchLoggerConfig(this);
+        this->SetupLogging(m_loggerConfig);
+
+        m_traceLogConfig =
+            ProfilerManager::Instance().FetchTraceLogConfig(this);
+        m_profConfig = ProfilerManager::Instance().FetchProfilerConfig(this);
+
+        //
+        // Applying the Configuration to the TraceLog.
+        //
+
+        this->SetupTraceLog(m_traceLogConfig);
+
+        //
+        // Announce start time.
+        //
+
+        TRACE().DumpStartTime(systime);
+
+        //
+        // Initializing the Profiler Info.
+        //
+
+        hr = m_info.Initialize(pICorProfilerInfoUnk);
+        if (FAILED(hr))
+        {
+            throw HresultException("ProfilerInfo::Initialize()", hr);
+        }
+        _ASSERTE(m_info.version() > 0);
+
+        //
+        // Applying the Configuration to the Profiler.
+        //
+
+        this->ProcessConfig(m_profConfig);
+        TRACE().DumpProfilerConfig(m_profConfig);
+
+        //
+        // Initialization completion.
+        //
+
+        m_initialized = true;
+    }
+    catch (const std::exception &e)
+    {
+        this->Shutdown();
+        hr = this->HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::Shutdown()
+{
+    // Check method is called only once.
+    _ASSERTE(m_shutdowned == false);
+
+    m_memoryTrace.Shutdown();
+    m_executionTrace.Shutdown();
+    m_cpuTrace.Shutdown();
+    m_commonTrace.Shutdown();
+
+    // ProfilerInfo can't be used after Shutdown event.
+    m_info.Reset();
+
+    m_shutdowned = true;
+
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AppDomainCreationStarted(
+    AppDomainID appDomainId)
+{
+    LOG().Trace() << "AppDomainCreationStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AppDomainCreationFinished(
+    AppDomainID appDomainId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "AppDomainCreationFinished()";
+
+    HRESULT hr;
+    hr = m_commonTrace.AppDomainCreationFinished(appDomainId, hrStatus);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AppDomainShutdownStarted(
+    AppDomainID appDomainId)
+{
+    LOG().Trace() << "AppDomainShutdownStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AppDomainShutdownFinished(
+    AppDomainID appDomainId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "AppDomainShutdownFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AssemblyLoadStarted(
+    AssemblyID assemblyId)
+{
+    LOG().Trace() << "AssemblyLoadStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AssemblyLoadFinished(
+    AssemblyID assemblyId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "AssemblyLoadFinished()";
+
+    HRESULT hr;
+    hr = m_commonTrace.AssemblyLoadFinished(assemblyId, hrStatus);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AssemblyUnloadStarted(
+    AssemblyID assemblyId)
+{
+    LOG().Trace() << "AssemblyUnloadStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::AssemblyUnloadFinished(
+    AssemblyID assemblyId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "AssemblyUnloadFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ModuleLoadStarted(
+    ModuleID moduleId)
+{
+    LOG().Trace() << "ModuleLoadStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ModuleLoadFinished(
+    ModuleID moduleId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "ModuleLoadFinished()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ModuleLoadFinished(moduleId, hrStatus);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ModuleUnloadStarted(
+    ModuleID moduleId)
+{
+    LOG().Trace() << "ModuleUnloadStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ModuleUnloadFinished(
+    ModuleID moduleId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "ModuleUnloadFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ModuleAttachedToAssembly(
+    ModuleID moduleId,
+    AssemblyID assemblyId)
+{
+    LOG().Trace() << "ModuleAttachedToAssembly()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ModuleAttachedToAssembly(moduleId, assemblyId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ClassLoadStarted(
+    ClassID classId)
+{
+    LOG().Trace() << "ClassLoadStarted()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ClassLoadStarted(classId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ClassLoadFinished(
+    ClassID classId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "ClassLoadFinished()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ClassLoadFinished(classId, hrStatus);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ClassUnloadStarted(
+    ClassID classId)
+{
+    LOG().Trace() << "ClassUnloadStarted()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ClassUnloadStarted(classId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ClassUnloadFinished(
+    ClassID classId,
+    HRESULT hrStatus)
+{
+    LOG().Trace() << "ClassUnloadFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::FunctionUnloadStarted(
+    FunctionID functionId)
+{
+    LOG().Trace() << "FunctionUnloadStarted()";
+
+    HRESULT hr;
+    hr = m_executionTrace.FunctionUnloadStarted(functionId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::JITCompilationStarted(
+    FunctionID functionId,
+    BOOL fIsSafeToBlock)
+{
+    LOG().Trace() << "JITCompilationStarted()";
+
+    HRESULT hr;
+    hr = m_executionTrace.JITCompilationStarted(functionId, fIsSafeToBlock);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::JITCompilationFinished(
+    FunctionID functionId,
+    HRESULT hrStatus, BOOL fIsSafeToBlock)
+{
+    LOG().Trace() << "JITCompilationFinished()";
+
+    HRESULT hr;
+    hr = m_executionTrace.JITCompilationFinished(
+        functionId, hrStatus, fIsSafeToBlock);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::JITCachedFunctionSearchStarted(
+    FunctionID functionId,
+    BOOL *pbUseCachedFunction)
+{
+    LOG().Trace() << "JITCachedFunctionSearchStarted()";
+
+    HRESULT hr;
+    hr = m_executionTrace.JITCachedFunctionSearchStarted(
+        functionId, pbUseCachedFunction);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::JITCachedFunctionSearchFinished(
+    FunctionID functionId,
+    COR_PRF_JIT_CACHE result)
+{
+    LOG().Trace() << "JITCachedFunctionSearchFinished()";
+
+    HRESULT hr;
+    hr = m_executionTrace.JITCachedFunctionSearchFinished(functionId, result);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::JITFunctionPitched(
+    FunctionID functionId)
+{
+    LOG().Trace() << "JITFunctionPitched()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::JITInlining(
+    FunctionID callerId,
+    FunctionID calleeId,
+    BOOL *pfShouldInline)
+{
+    LOG().Trace() << "JITInlining()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ThreadCreated(
+    ThreadID threadId)
+{
+    LOG().Trace() << "ThreadCreated()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ThreadCreated(threadId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ThreadDestroyed(
+    ThreadID threadId)
+{
+    LOG().Trace() << "ThreadDestroyed()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ThreadDestroyed(threadId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ThreadAssignedToOSThread(
+    ThreadID managedThreadId,
+    DWORD osThreadId)
+{
+    LOG().Trace() << "ThreadAssignedToOSThread()";
+
+    HRESULT hr;
+    hr = m_commonTrace.ThreadAssignedToOSThread(managedThreadId, osThreadId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ThreadNameChanged(
+    ThreadID threadId,
+    ULONG cchName,
+    _In_reads_opt_(cchName) WCHAR name[])
+{
+    LOG().Trace() << "ThreadNameChanged()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingClientInvocationStarted()
+{
+    LOG().Trace() << "RemotingClientInvocationStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingClientSendingMessage(
+    GUID *pCookie,
+    BOOL fIsAsync)
+{
+    LOG().Trace() << "RemotingClientSendingMessage()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingClientReceivingReply(
+    GUID *pCookie,
+    BOOL fIsAsync)
+{
+    LOG().Trace() << "RemotingClientReceivingReply()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingClientInvocationFinished()
+{
+    LOG().Trace() << "RemotingClientInvocationFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingServerReceivingMessage(
+    GUID *pCookie,
+    BOOL fIsAsync)
+{
+    LOG().Trace() << "RemotingServerReceivingMessage()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingServerInvocationStarted()
+{
+    LOG().Trace() << "RemotingServerInvocationStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingServerInvocationReturned()
+{
+    LOG().Trace() << "RemotingServerInvocationReturned()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RemotingServerSendingReply(
+    GUID *pCookie,
+    BOOL fIsAsync)
+{
+    LOG().Trace() << "RemotingServerSendingReply()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::UnmanagedToManagedTransition(
+    FunctionID functionId,
+    COR_PRF_TRANSITION_REASON reason)
+{
+    LOG().Trace() << "UnmanagedToManagedTransition()";
+
+    HRESULT hr;
+    hr = m_executionTrace.UnmanagedToManagedTransition(functionId, reason);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ManagedToUnmanagedTransition(
+    FunctionID functionId,
+    COR_PRF_TRANSITION_REASON reason)
+{
+    LOG().Trace() << "ManagedToUnmanagedTransition()";
+
+    HRESULT hr;
+    hr = m_executionTrace.ManagedToUnmanagedTransition(functionId, reason);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RuntimeSuspendStarted(
+    COR_PRF_SUSPEND_REASON suspendReason)
+{
+    LOG().Trace() << "RuntimeSuspendStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RuntimeSuspendFinished()
+{
+    LOG().Trace() << "RuntimeSuspendFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RuntimeSuspendAborted()
+{
+    LOG().Trace() << "RuntimeSuspendAborted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RuntimeResumeStarted()
+{
+    LOG().Trace() << "RuntimeResumeStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RuntimeResumeFinished()
+{
+    LOG().Trace() << "RuntimeResumeFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RuntimeThreadSuspended(
+    ThreadID threadId)
+{
+    LOG().Trace() << "RuntimeThreadSuspended()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RuntimeThreadResumed(
+    ThreadID threadId)
+{
+    LOG().Trace() << "RuntimeThreadResumed()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::MovedReferences(
+    ULONG cMovedObjectIDRanges,
+    ObjectID oldObjectIDRangeStart[],
+    ObjectID newObjectIDRangeStart[],
+    ULONG cObjectIDRangeLength[])
+{
+    LOG().Trace() << "MovedReferences()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ObjectAllocated(
+    ObjectID objectId,
+    ClassID classId)
+{
+    LOG().Trace() << "ObjectAllocated()";
+
+    HRESULT hr;
+    hr = m_memoryTrace.ObjectAllocated(objectId, classId);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ObjectsAllocatedByClass(
+    ULONG cClassCount,
+    ClassID classIds[],
+    ULONG cObjects[])
+{
+    LOG().Trace() << "ObjectsAllocatedByClass()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ObjectReferences(
+    ObjectID objectId,
+    ClassID classId,
+    ULONG cObjectRefs,
+    ObjectID objectRefIds[])
+{
+    LOG().Trace() << "ObjectReferences()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RootReferences(
+    ULONG cRootRefs,
+    ObjectID rootRefIds[])
+{
+    LOG().Trace() << "RootReferences()";
+    return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE Profiler::GarbageCollectionStarted(
+    int cGenerations,
+    BOOL generationCollected[],
+    COR_PRF_GC_REASON reason)
+{
+    LOG().Trace() << "GarbageCollectionStarted()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::SurvivingReferences(
+    ULONG cSurvivingObjectIDRanges,
+    ObjectID objectIDRangeStart[],
+    ULONG cObjectIDRangeLength[])
+{
+    LOG().Trace() << "SurvivingReferences()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::GarbageCollectionFinished()
+{
+    LOG().Trace() << "GarbageCollectionFinished()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::FinalizeableObjectQueued(
+    DWORD finalizerFlags,
+    ObjectID objectID)
+{
+    LOG().Trace() << "FinalizeableObjectQueued()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::RootReferences2(
+    ULONG cRootRefs,
+    ObjectID rootRefIds[],
+    COR_PRF_GC_ROOT_KIND rootKinds[],
+    COR_PRF_GC_ROOT_FLAGS rootFlags[],
+    UINT_PTR rootIds[])
+{
+    LOG().Trace() << "RootReferences2()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::HandleCreated(
+    GCHandleID handleId,
+    ObjectID initialObjectId)
+{
+    LOG().Trace() << "HandleCreated()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::HandleDestroyed(
+    GCHandleID handleId)
+{
+    LOG().Trace() << "HandleDestroyed()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionThrown(
+    ObjectID thrownObjectId)
+{
+    LOG().Trace() << "ExceptionThrown()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionSearchFunctionEnter(
+    FunctionID functionId)
+{
+    LOG().Trace() << "ExceptionSearchFunctionEnter()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionSearchFunctionLeave()
+{
+    LOG().Trace() << "ExceptionSearchFunctionLeave()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionSearchFilterEnter(
+    FunctionID functionId)
+{
+    LOG().Trace() << "ExceptionSearchFilterEnter()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionSearchFilterLeave()
+{
+    LOG().Trace() << "ExceptionSearchFilterLeave()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionSearchCatcherFound(
+    FunctionID functionId)
+{
+    LOG().Trace() << "ExceptionSearchCatcherFound()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionOSHandlerEnter(
+    UINT_PTR __unused)
+{
+    LOG().Trace() << "ExceptionOSHandlerEnter()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionOSHandlerLeave(
+    UINT_PTR __unused)
+{
+    LOG().Trace() << "ExceptionOSHandlerLeave()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionUnwindFunctionEnter(
+    FunctionID functionId)
+{
+    LOG().Trace() << "ExceptionUnwindFunctionEnter()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionUnwindFunctionLeave()
+{
+    LOG().Trace() << "ExceptionUnwindFunctionLeave()";
+
+    HRESULT hr;
+    hr = m_executionTrace.ExceptionUnwindFunctionLeave();
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionUnwindFinallyEnter(
+    FunctionID functionId)
+{
+    LOG().Trace() << "ExceptionUnwindFinallyEnter()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionUnwindFinallyLeave()
+{
+    LOG().Trace() << "ExceptionUnwindFinallyLeave()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionCatcherEnter(
+    FunctionID functionId,
+    ObjectID objectId)
+{
+    LOG().Trace() << "ExceptionCatcherEnter()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionCatcherLeave()
+{
+    LOG().Trace() << "ExceptionCatcherLeave()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::COMClassicVTableCreated(
+    ClassID wrappedClassId,
+    REFGUID implementedIID,
+    void *pVTable, ULONG cSlots)
+{
+    LOG().Trace() << "COMClassicVTableCreated()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::COMClassicVTableDestroyed(
+    ClassID wrappedClassId,
+    REFGUID implementedIID,
+    void *pVTable)
+{
+    LOG().Trace() << "COMClassicVTableDestroyed()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::InitializeForAttach(
+    IUnknown *pCorProfilerInfoUnk,
+    void *pvClientData,
+    UINT cbClientData)
+{
+    LOG().Trace() << "InitializeForAttach()";
+    // TODO: implement attaching functionality.
+    return CORPROF_E_PROFILER_NOT_ATTACHABLE;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ProfilerAttachComplete()
+{
+    LOG().Trace() << "ProfilerAttachComplete()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ProfilerDetachSucceeded()
+{
+    LOG().Trace() << "ProfilerDetachSucceeded()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionCLRCatcherFound()
+{
+    LOG().Trace() << "ExceptionCLRCatcherFound()";
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE Profiler::ExceptionCLRCatcherExecute()
+{
+    LOG().Trace() << "ExceptionCLRCatcherExecute()";
+    return S_OK;
+}
diff --git a/src/profiler.h b/src/profiler.h
new file mode 100644 (file)
index 0000000..c84588c
--- /dev/null
@@ -0,0 +1,498 @@
+#ifndef _PROFILER_H_
+#define _PROFILER_H_
+
+#include <exception>
+#include <memory>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "log.h"
+
+#include "loggerconfig.h"
+#include "tracelogconfig.h"
+#include "profilerconfig.h"
+#include "profilerinfo.h"
+
+#include "tracelog.h"
+
+#include "commontrace.h"
+#include "cputrace.h"
+#include "executiontrace.h"
+#include "memorytrace.h"
+
+// TODO: port to the std::system_error approach.
+class HresultException : public std::runtime_error
+{
+public:
+    HresultException(const std::string& what_arg, HRESULT hr)
+        : std::runtime_error(what_arg + ": UNKNOWN")
+        , m_hresult(hr)
+    {}
+
+    HRESULT hresult() const
+    {
+        return m_hresult;
+    }
+
+private:
+    HRESULT m_hresult;
+};
+
+class Profiler final : public ICorProfilerCallback3
+{
+public:
+    // Instantiate an instance of the callback interface in the Global Area.
+    static HRESULT CreateObject(
+        REFIID riid,
+        void   **ppInterface) noexcept;
+
+    // Remove an instance of the callback interface from the Global Area.
+    static void RemoveObject(
+        Profiler *pProfiler) noexcept;
+
+private:
+    //
+    // Profiler should be created and destroyed through public API.
+    //
+
+    Profiler();
+
+    virtual ~Profiler();
+
+public:
+    // Returns mutable reference to the Logger even for a constant
+    // reference to the Profiler.
+    Log &LOG() const noexcept;
+
+    // Returns mutable reference to the TraceLog even for a constant
+    // reference to the Profiler.
+    ITraceLog &TRACE() const noexcept;
+
+    // Retrieves the number of milliseconds that have elapsed since the Profiler
+    // was initialized.
+    DWORD GetTickCountFromInit() const noexcept;
+
+    // Check type of the exception, send corresponding information to the log
+    // and return HRESULT related to this exception.
+    HRESULT HandleException(const std::exception &e) const noexcept;
+
+    // Wrap error code ev with std::system_error using std::system_category()
+    // and call to HandleException() to handle it.
+    void HandleSysErr(const std::string& what_arg, int ev) const noexcept;
+
+    // Wrap HRESULT with HresultException and call to HandleException()
+    // to handle it.
+    void HandleHresult(const std::string& what_arg, HRESULT hr) const noexcept;
+
+    //
+    // Simple Getters.
+    //
+
+    ProfilerConfig &GetConfig() noexcept;
+
+    const ProfilerInfo &GetProfilerInfo() const noexcept;
+
+    CommonTrace &GetCommonTrace() noexcept;
+
+    CpuTrace &GetCpuTrace() noexcept;
+
+    ExecutionTrace &GetExecutionTrace() noexcept;
+
+    MemoryTrace &GetMemoryTrace() noexcept;
+
+private:
+    //
+    // Various useful instance methods.
+    //
+
+    // Apply the configuration to the Logger.
+    void SetupLogging(LoggerConfig &config);
+
+    // Apply the configuration to the TraceLog.
+    void SetupTraceLog(TraceLogConfig &config);
+
+    // Apply the configuration to the Profiler.
+    void ProcessConfig(ProfilerConfig &config);
+
+public:
+    //
+    // IUnknown methods.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+        REFIID riid,
+        void **ppvObject) override;
+
+    virtual ULONG STDMETHODCALLTYPE AddRef() override;
+
+    virtual ULONG STDMETHODCALLTYPE Release() override;
+
+    //
+    // Startup/shutdown events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE Initialize(
+        IUnknown *pICorProfilerInfoUnk) override;
+
+    virtual HRESULT STDMETHODCALLTYPE Shutdown() override;
+
+    //
+    // Application domain events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE AppDomainCreationStarted(
+        AppDomainID appDomainId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE AppDomainCreationFinished(
+        AppDomainID appDomainId,
+        HRESULT hrStatus) override;
+
+    virtual HRESULT STDMETHODCALLTYPE AppDomainShutdownStarted(
+        AppDomainID appDomainId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE AppDomainShutdownFinished(
+        AppDomainID appDomainId,
+        HRESULT hrStatus) override;
+
+    //
+    // Assembly events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE AssemblyLoadStarted(
+        AssemblyID assemblyId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE AssemblyLoadFinished(
+        AssemblyID assemblyId,
+        HRESULT hrStatus) override;
+
+    virtual HRESULT STDMETHODCALLTYPE AssemblyUnloadStarted(
+        AssemblyID assemblyId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE AssemblyUnloadFinished(
+        AssemblyID assemblyId,
+        HRESULT hrStatus) override;
+
+    //
+    // Module events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE ModuleLoadStarted(
+        ModuleID moduleId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ModuleLoadFinished(
+        ModuleID moduleId,
+        HRESULT hrStatus) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ModuleUnloadStarted(
+        ModuleID moduleId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ModuleUnloadFinished(
+        ModuleID moduleId,
+        HRESULT hrStatus) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ModuleAttachedToAssembly(
+        ModuleID moduleId,
+        AssemblyID assemblyId) override;
+
+    //
+    // Class events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE ClassLoadStarted(
+        ClassID classId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ClassLoadFinished(
+        ClassID classId,
+        HRESULT hrStatus) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ClassUnloadStarted(
+        ClassID classId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ClassUnloadFinished(
+        ClassID classId,
+        HRESULT hrStatus) override;
+
+    virtual HRESULT STDMETHODCALLTYPE FunctionUnloadStarted(
+        FunctionID functionId) override;
+
+    //
+    // Jit events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE JITCompilationStarted(
+        FunctionID functionId,
+        BOOL fIsSafeToBlock) override;
+
+    virtual HRESULT STDMETHODCALLTYPE JITCompilationFinished(
+        FunctionID functionId,
+        HRESULT hrStatus,
+        BOOL fIsSafeToBlock) override;
+
+    virtual HRESULT STDMETHODCALLTYPE JITCachedFunctionSearchStarted(
+        FunctionID functionId,
+        BOOL *pbUseCachedFunction) override;
+
+    virtual HRESULT STDMETHODCALLTYPE JITCachedFunctionSearchFinished(
+        FunctionID functionId,
+        COR_PRF_JIT_CACHE result) override;
+
+    virtual HRESULT STDMETHODCALLTYPE JITFunctionPitched(
+        FunctionID functionId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE JITInlining(
+        FunctionID callerId,
+        FunctionID calleeId,
+        BOOL *pfShouldInline) override;
+
+    //
+    // Thread events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE ThreadCreated(
+        ThreadID threadId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ThreadDestroyed(
+        ThreadID threadId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ThreadAssignedToOSThread(
+        ThreadID managedThreadId,
+        DWORD osThreadId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ThreadNameChanged(
+        ThreadID threadId,
+        ULONG cchName,
+        _In_reads_opt_(cchName) WCHAR name[]) override;
+
+    //
+    // Remoting events.
+    //
+
+    // Client-side events.
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingClientInvocationStarted() override;
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingClientSendingMessage(
+        GUID *pCookie,
+        BOOL fIsAsync) override;
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingClientReceivingReply(
+        GUID *pCookie,
+        BOOL fIsAsync) override;
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingClientInvocationFinished() override;
+
+    // Server-side events.
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingServerReceivingMessage(
+        GUID *pCookie,
+        BOOL fIsAsync) override;
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingServerInvocationStarted() override;
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingServerInvocationReturned() override;
+
+    virtual HRESULT STDMETHODCALLTYPE RemotingServerSendingReply(
+        GUID *pCookie,
+        BOOL fIsAsync) override;
+
+    //
+    // Transition events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE UnmanagedToManagedTransition(
+        FunctionID functionId,
+        COR_PRF_TRANSITION_REASON reason) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ManagedToUnmanagedTransition(
+        FunctionID functionId,
+        COR_PRF_TRANSITION_REASON reason) override;
+
+    //
+    // Runtime suspension events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE RuntimeSuspendStarted(
+        COR_PRF_SUSPEND_REASON suspendReason) override;
+
+    virtual HRESULT STDMETHODCALLTYPE RuntimeSuspendFinished() override;
+
+    virtual HRESULT STDMETHODCALLTYPE RuntimeSuspendAborted() override;
+
+    virtual HRESULT STDMETHODCALLTYPE RuntimeResumeStarted() override;
+
+    virtual HRESULT STDMETHODCALLTYPE RuntimeResumeFinished() override;
+
+    virtual HRESULT STDMETHODCALLTYPE RuntimeThreadSuspended(
+        ThreadID threadId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE RuntimeThreadResumed(
+        ThreadID threadId) override;
+
+    //
+    // GC events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE MovedReferences(
+        ULONG cMovedObjectIDRanges,
+        ObjectID oldObjectIDRangeStart[],
+        ObjectID newObjectIDRangeStart[],
+        ULONG cObjectIDRangeLength[]) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ObjectAllocated(
+        ObjectID objectId,
+        ClassID classId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ObjectsAllocatedByClass(
+        ULONG cClassCount,
+        ClassID classIds[],
+        ULONG cObjects[]) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ObjectReferences(
+        ObjectID objectId,
+        ClassID classId,
+        ULONG cObjectRefs,
+        ObjectID objectRefIds[]) override;
+
+    virtual HRESULT STDMETHODCALLTYPE RootReferences(
+        ULONG cRootRefs,
+        ObjectID rootRefIds[]) override;
+
+    virtual HRESULT STDMETHODCALLTYPE GarbageCollectionStarted(
+        int cGenerations,
+        BOOL generationCollected[],
+        COR_PRF_GC_REASON reason) override;
+
+    virtual HRESULT STDMETHODCALLTYPE SurvivingReferences(
+        ULONG cSurvivingObjectIDRanges,
+        ObjectID objectIDRangeStart[],
+        ULONG cObjectIDRangeLength[]) override;
+
+    virtual HRESULT STDMETHODCALLTYPE GarbageCollectionFinished() override;
+
+    virtual HRESULT STDMETHODCALLTYPE FinalizeableObjectQueued(
+        DWORD finalizerFlags,
+        ObjectID objectId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE RootReferences2(
+        ULONG cRootRefs,
+        ObjectID rootRefIds[],
+        COR_PRF_GC_ROOT_KIND rootKinds[],
+        COR_PRF_GC_ROOT_FLAGS rootFlags[],
+        UINT_PTR rootIds[]) override;
+
+    virtual HRESULT STDMETHODCALLTYPE HandleCreated(
+        GCHandleID handleId,
+        ObjectID initialObjectId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE HandleDestroyed(
+        GCHandleID handleId) override;
+
+    //
+    // Exception events.
+    //
+
+    // Exception creation.
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionThrown(
+        ObjectID thrownObjectId) override;
+
+    // Search phase.
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionSearchFunctionEnter(
+        FunctionID functionId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionSearchFunctionLeave() override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionSearchFilterEnter(
+        FunctionID functionId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionSearchFilterLeave() override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionSearchCatcherFound(
+        FunctionID functionId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionOSHandlerEnter(
+        UINT_PTR __unused) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionOSHandlerLeave(
+        UINT_PTR __unused) override;
+
+    // Unwind phase.
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionUnwindFunctionEnter(
+        FunctionID functionId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionUnwindFunctionLeave() override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionUnwindFinallyEnter(
+        FunctionID functionId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionUnwindFinallyLeave() override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionCatcherEnter(
+        FunctionID functionId, ObjectID objectId) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionCatcherLeave() override;
+
+    //
+    // COM classic wrapper.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE COMClassicVTableCreated(
+        ClassID wrappedClassId,
+        REFGUID implementedIID,
+        void *pVTable,
+        ULONG cSlots) override;
+
+    virtual HRESULT STDMETHODCALLTYPE COMClassicVTableDestroyed(
+        ClassID wrappedClassId,
+        REFGUID implementedIID,
+        void *pVTable) override;
+
+    //
+    // Attach events.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE InitializeForAttach(
+        IUnknown *pCorProfilerInfoUnk,
+        void *pvClientData,
+        UINT cbClientData) override;
+
+    virtual HRESULT STDMETHODCALLTYPE ProfilerAttachComplete() override;
+
+    virtual HRESULT STDMETHODCALLTYPE ProfilerDetachSucceeded() override;
+
+    //
+    // DEPRECATED. These callbacks are no longer delivered.
+    //
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionCLRCatcherFound() override;
+
+    virtual HRESULT STDMETHODCALLTYPE ExceptionCLRCatcherExecute() override;
+
+private:
+    LONG m_cRef;
+
+    Log  m_logger;
+
+    BOOL m_initialized;
+    BOOL m_shutdowned;
+
+    LoggerConfig   m_loggerConfig;
+    TraceLogConfig m_traceLogConfig;
+    ProfilerConfig m_profConfig;
+    ProfilerInfo   m_info;
+
+    std::unique_ptr<ITraceLog> m_traceLog;
+
+    CommonTrace    m_commonTrace;
+    CpuTrace       m_cpuTrace;
+    ExecutionTrace m_executionTrace;
+    MemoryTrace    m_memoryTrace;
+
+    DWORD m_firstTickCount;
+};
+
+#endif // _PROFILER_H_
diff --git a/src/profilerinfo.cpp b/src/profilerinfo.cpp
new file mode 100644 (file)
index 0000000..e0ad714
--- /dev/null
@@ -0,0 +1,226 @@
+#include "profilerinfo.h"
+
+ProfilerInfo::ProfilerInfo() noexcept
+    : m_pProfilerInfo (nullptr)
+    , m_pProfilerInfo2(nullptr)
+    , m_pProfilerInfo3(nullptr)
+    , m_pProfilerInfo4(nullptr)
+    , m_pProfilerInfo5(nullptr)
+    , m_pProfilerInfo6(nullptr)
+    , m_pProfilerInfo7(nullptr)
+    , m_version(0)
+{
+}
+
+ProfilerInfo::~ProfilerInfo()
+{
+    if (m_pProfilerInfo != nullptr)
+        m_pProfilerInfo->Release();
+}
+
+HRESULT ProfilerInfo::Initialize(IUnknown *pICorProfilerInfoUnk) noexcept
+{
+    this->Reset(); // Ensure ProfilerInfo is in initial state.
+
+    HRESULT hr;
+
+    if (m_pProfilerInfo7 == nullptr)
+    {
+        hr = pICorProfilerInfoUnk->QueryInterface(
+            IID_ICorProfilerInfo7,
+            (void**)&m_pProfilerInfo7);
+        if (SUCCEEDED(hr)) {
+            _ASSERTE(m_version == 0);
+            m_version = 7;
+
+            m_pProfilerInfo6 = static_cast<ICorProfilerInfo6*>(
+                m_pProfilerInfo7);
+            m_pProfilerInfo5 = static_cast<ICorProfilerInfo5*>(
+                m_pProfilerInfo7);
+            m_pProfilerInfo4 = static_cast<ICorProfilerInfo4*>(
+                m_pProfilerInfo7);
+            m_pProfilerInfo3 = static_cast<ICorProfilerInfo3*>(
+                m_pProfilerInfo7);
+            m_pProfilerInfo2 = static_cast<ICorProfilerInfo2*>(
+                m_pProfilerInfo7);
+            m_pProfilerInfo  = static_cast<ICorProfilerInfo* >(
+                m_pProfilerInfo7);
+        }
+    }
+
+    if (m_pProfilerInfo6 == nullptr)
+    {
+        hr = pICorProfilerInfoUnk->QueryInterface(
+            IID_ICorProfilerInfo6,
+            (void**)&m_pProfilerInfo6);
+        if (SUCCEEDED(hr)) {
+            _ASSERTE(m_version == 0);
+            m_version = 6;
+
+            m_pProfilerInfo5 = static_cast<ICorProfilerInfo5*>(
+                m_pProfilerInfo6);
+            m_pProfilerInfo4 = static_cast<ICorProfilerInfo4*>(
+                m_pProfilerInfo6);
+            m_pProfilerInfo3 = static_cast<ICorProfilerInfo3*>(
+                m_pProfilerInfo6);
+            m_pProfilerInfo2 = static_cast<ICorProfilerInfo2*>(
+                m_pProfilerInfo6);
+            m_pProfilerInfo  = static_cast<ICorProfilerInfo* >(
+                m_pProfilerInfo6);
+        }
+    }
+
+    if (m_pProfilerInfo5 == nullptr)
+    {
+        hr = pICorProfilerInfoUnk->QueryInterface(
+            IID_ICorProfilerInfo5,
+            (void**)&m_pProfilerInfo5);
+        if (SUCCEEDED(hr)) {
+            _ASSERTE(m_version == 0);
+            m_version = 5;
+
+            m_pProfilerInfo4 = static_cast<ICorProfilerInfo4*>(
+                m_pProfilerInfo5);
+            m_pProfilerInfo3 = static_cast<ICorProfilerInfo3*>(
+                m_pProfilerInfo5);
+            m_pProfilerInfo2 = static_cast<ICorProfilerInfo2*>(
+                m_pProfilerInfo5);
+            m_pProfilerInfo  = static_cast<ICorProfilerInfo* >(
+                m_pProfilerInfo5);
+        }
+    }
+
+    if (m_pProfilerInfo4 == nullptr)
+    {
+        hr = pICorProfilerInfoUnk->QueryInterface(
+            IID_ICorProfilerInfo4,
+            (void**)&m_pProfilerInfo4);
+        if (SUCCEEDED(hr)) {
+            _ASSERTE(m_version == 0);
+            m_version = 4;
+
+            m_pProfilerInfo3 = static_cast<ICorProfilerInfo3*>(
+                m_pProfilerInfo4);
+            m_pProfilerInfo2 = static_cast<ICorProfilerInfo2*>(
+                m_pProfilerInfo4);
+            m_pProfilerInfo  = static_cast<ICorProfilerInfo* >(
+                m_pProfilerInfo4);
+        }
+    }
+
+    if (m_pProfilerInfo3 == nullptr)
+    {
+        hr = pICorProfilerInfoUnk->QueryInterface(
+            IID_ICorProfilerInfo3,
+            (void**)&m_pProfilerInfo3);
+        if (SUCCEEDED(hr)) {
+            _ASSERTE(m_version == 0);
+            m_version = 3;
+
+            m_pProfilerInfo2 = static_cast<ICorProfilerInfo2*>(
+                m_pProfilerInfo3);
+            m_pProfilerInfo  = static_cast<ICorProfilerInfo* >(
+                m_pProfilerInfo3);
+        }
+    }
+
+    if (m_pProfilerInfo2 == nullptr)
+    {
+        hr = pICorProfilerInfoUnk->QueryInterface(
+            IID_ICorProfilerInfo2,
+            (void**)&m_pProfilerInfo2);
+        if (SUCCEEDED(hr)) {
+            _ASSERTE(m_version == 0);
+            m_version = 2;
+
+            m_pProfilerInfo = static_cast<ICorProfilerInfo*>(
+                m_pProfilerInfo2);
+        }
+    }
+
+    if (m_pProfilerInfo == nullptr)
+    {
+        hr = pICorProfilerInfoUnk->QueryInterface(
+            IID_ICorProfilerInfo,
+            (void**)&m_pProfilerInfo);
+        if (SUCCEEDED(hr)) {
+            _ASSERTE(m_version == 0);
+            m_version = 1;
+        }
+    }
+
+    _ASSERTE(m_version < 7 || m_pProfilerInfo7 != nullptr);
+    _ASSERTE(m_version < 6 || m_pProfilerInfo6 != nullptr);
+    _ASSERTE(m_version < 5 || m_pProfilerInfo5 != nullptr);
+    _ASSERTE(m_version < 4 || m_pProfilerInfo4 != nullptr);
+    _ASSERTE(m_version < 3 || m_pProfilerInfo3 != nullptr);
+    _ASSERTE(m_version < 2 || m_pProfilerInfo2 != nullptr);
+    _ASSERTE(m_version < 1 || m_pProfilerInfo  != nullptr);
+
+    return hr;
+}
+
+void ProfilerInfo::Reset() noexcept
+{
+    if (m_version != 0)
+    {
+        _ASSERTE(m_pProfilerInfo != nullptr);
+        m_pProfilerInfo->Release();
+
+        m_pProfilerInfo  = nullptr;
+        m_pProfilerInfo2 = nullptr;
+        m_pProfilerInfo3 = nullptr;
+        m_pProfilerInfo4 = nullptr;
+        m_pProfilerInfo5 = nullptr;
+        m_pProfilerInfo6 = nullptr;
+        m_pProfilerInfo7 = nullptr;
+        m_version = 0;
+    }
+}
+
+unsigned int ProfilerInfo::version() const noexcept
+{
+    return m_version;
+}
+
+ICorProfilerInfo  *ProfilerInfo::v1() const noexcept
+{
+    _ASSERTE(m_version >= 1);
+    return m_pProfilerInfo;
+}
+
+ICorProfilerInfo2 *ProfilerInfo::v2() const noexcept
+{
+    _ASSERTE(m_version >= 2);
+    return m_pProfilerInfo2;
+}
+
+ICorProfilerInfo3 *ProfilerInfo::v3() const noexcept
+{
+    _ASSERTE(m_version >= 3);
+    return m_pProfilerInfo3;
+}
+
+ICorProfilerInfo4 *ProfilerInfo::v4() const noexcept
+{
+    _ASSERTE(m_version >= 4);
+    return m_pProfilerInfo4;
+}
+
+ICorProfilerInfo5 *ProfilerInfo::v5() const noexcept
+{
+    _ASSERTE(m_version >= 5);
+    return m_pProfilerInfo5;
+}
+
+ICorProfilerInfo6 *ProfilerInfo::v6() const noexcept
+{
+    _ASSERTE(m_version >= 6);
+    return m_pProfilerInfo6;
+}
+
+ICorProfilerInfo7 *ProfilerInfo::v7() const noexcept
+{
+    _ASSERTE(m_version >= 7);
+    return m_pProfilerInfo7;
+}
diff --git a/src/profilerinfo.h b/src/profilerinfo.h
new file mode 100644 (file)
index 0000000..7008b8c
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef _PROFILER_INFO_H_
+#define _PROFILER_INFO_H_
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+class ProfilerInfo final
+{
+public:
+    ProfilerInfo() noexcept;
+
+    ~ProfilerInfo();
+
+    // Initialize ProfilerInfo with specific pointer to Profiler Info interface.
+    HRESULT Initialize(IUnknown *pICorProfilerInfoUnk) noexcept;
+
+    // Reset ProfilerInfo to initial state.
+    void Reset() noexcept;
+
+    // Get version of the Profiler Info API. Zero value means that no API
+    // versions is supported.
+    unsigned int version() const noexcept;
+
+    //
+    // These methods provide access to a specific version of the Profiler Info
+    // interface. You should be sure that the requested version is supported.
+    // Requesting of unsupported interface version invokes undefined behavior.
+    //
+    ICorProfilerInfo  *v1() const noexcept;
+    ICorProfilerInfo2 *v2() const noexcept;
+    ICorProfilerInfo3 *v3() const noexcept;
+    ICorProfilerInfo4 *v4() const noexcept;
+    ICorProfilerInfo5 *v5() const noexcept;
+    ICorProfilerInfo6 *v6() const noexcept;
+    ICorProfilerInfo7 *v7() const noexcept;
+
+private:
+    // Pointers to the implementation of the ProfilerInfo interface(s).
+    ICorProfilerInfo  *m_pProfilerInfo;
+    ICorProfilerInfo2 *m_pProfilerInfo2;
+    ICorProfilerInfo3 *m_pProfilerInfo3;
+    ICorProfilerInfo4 *m_pProfilerInfo4;
+    ICorProfilerInfo5 *m_pProfilerInfo5;
+    ICorProfilerInfo6 *m_pProfilerInfo6;
+    ICorProfilerInfo7 *m_pProfilerInfo7;
+
+    // Version of the Profiler Info API.
+    unsigned int m_version;
+};
+
+#endif // _PROFILER_INFO_H_
diff --git a/src/profilermanager.cpp b/src/profilermanager.cpp
new file mode 100644 (file)
index 0000000..b90a6c2
--- /dev/null
@@ -0,0 +1,115 @@
+#include <assert.h>
+
+#include "profiler.h"
+#include "environmentconfigprovider.h"
+#include "profilermanager.h"
+
+// NOTE: currently only one instance of the Profiler can be registered in the
+// Profiler Manager. We use "hidden" global reference since this limitation
+// shouldn't be demonstrated in the class private section. We can use a static
+// global variable because the Profiler Manager instance is a singleton.
+static Profiler *g_pProfilerObject = nullptr;
+
+// static
+ProfilerManager &ProfilerManager::Instance() noexcept
+{
+    static ProfilerManager s_ProfilerManagerInstance;
+    return s_ProfilerManagerInstance;
+}
+
+ProfilerManager::ProfilerManager() noexcept
+{
+}
+
+ProfilerManager::~ProfilerManager()
+{
+    if (g_pProfilerObject != nullptr)
+    {
+        DllDetachShutdown();
+    }
+    // We should ensure that the DllDetachShutdown() method was called before
+    // singleton destruction.
+    assert(g_pProfilerObject == nullptr);
+}
+
+template <typename T>
+T ProfilerManager::FetchConfig(const Profiler *pProfiler)
+{
+    // Ensure method is called for the global profiler instance;
+    assert(g_pProfilerObject == pProfiler);
+
+    T config;
+
+    // Currently only environment is used as source of the configuration.
+    EnvironmentConfigProvider().FetchConfig(config);
+
+    config.Validate();
+
+    auto warnings = config.Verify();
+    if (!warnings.empty())
+    {
+        auto logLine = pProfiler->LOG().Warn();
+        logLine << "Some errors detected in " << config.Name() << ":";
+        for (const auto &warning : warnings)
+        {
+            logLine << "\n\t\t" << warning;
+        }
+    }
+
+    return config;
+}
+
+bool ProfilerManager::IsProfilerRegistered(
+    const Profiler *pProfiler) const noexcept
+{
+    return pProfiler != nullptr && g_pProfilerObject == pProfiler;
+}
+
+void ProfilerManager::RegisterProfiler(const Profiler *pProfiler)
+{
+    // NOTE: potential race condition should be avoided outside of this class.
+    if (g_pProfilerObject == nullptr)
+    {
+        g_pProfilerObject = const_cast<Profiler*>(pProfiler);
+    }
+    else
+    {
+        // Ensure method is called for the global profiler instance;
+        assert(g_pProfilerObject == pProfiler);
+    }
+}
+
+void ProfilerManager::UnregisterProfiler(const Profiler *pProfiler)
+{
+    if (g_pProfilerObject == pProfiler)
+    {
+        g_pProfilerObject = nullptr;
+    }
+}
+
+LoggerConfig ProfilerManager::FetchLoggerConfig(const Profiler *pProfiler)
+{
+    return FetchConfig<LoggerConfig>(pProfiler);
+}
+
+TraceLogConfig ProfilerManager::FetchTraceLogConfig(const Profiler *pProfiler)
+{
+    return FetchConfig<TraceLogConfig>(pProfiler);
+}
+
+ProfilerConfig ProfilerManager::FetchProfilerConfig(const Profiler *pProfiler)
+{
+    return FetchConfig<ProfilerConfig>(pProfiler);
+}
+
+void ProfilerManager::DllDetachShutdown() noexcept
+{
+    //
+    // Since this function is called when DLL ends up its lifetime, we don't
+    // worry about profiler's reference counter.
+    //
+    if (IsProfilerRegistered(g_pProfilerObject))
+    {
+        Profiler::RemoveObject(g_pProfilerObject);
+    }
+}
diff --git a/src/profilermanager.h b/src/profilermanager.h
new file mode 100644 (file)
index 0000000..573f798
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef _PROFILER_MANAGER_H_
+#define _PROFILER_MANAGER_H_
+
+#include "loggerconfig.h"
+#include "tracelogconfig.h"
+#include "profilerconfig.h"
+
+class Profiler; // Forward declaration instead of the header inclusion.
+
+class ProfilerManager
+{
+public:
+    // Get the instance of the singleton. It will be instantiated at first call.
+    static ProfilerManager &Instance() noexcept;
+
+private:
+    // Signleton can be instantiated only by the public Instance() static
+    // member function.
+    ProfilerManager() noexcept;
+
+    // Singleton can be destroyed only during process termination.
+    ~ProfilerManager();
+
+    template<typename T>
+    T FetchConfig(const Profiler *pProfiler);
+
+public:
+    // Check if the Profiler is registered in the Profiler Manager.
+    bool IsProfilerRegistered(const Profiler *pProfiler) const noexcept;
+
+    // Register the Profiler in the Profiler Manager.
+    void RegisterProfiler(const Profiler *pProfiler);
+
+    // Remove the Profiler from the Profiler Manager.
+    void UnregisterProfiler(const Profiler *pProfiler);
+
+    // Get logger configuration from the Global Area for the specified Profiler.
+    LoggerConfig FetchLoggerConfig(const Profiler *pProfiler);
+
+    // Get trace configuration from the Global Area for the specified Profiler.
+    TraceLogConfig FetchTraceLogConfig(const Profiler *pProfiler);
+
+    // Get configuration from the Global Area for the specified Profiler.
+    ProfilerConfig FetchProfilerConfig(const Profiler *pProfiler);
+
+    // This function is called when DLL is detached and we should perform
+    // cleanup of the Global Area.
+    void DllDetachShutdown() noexcept;
+
+    //
+    // Singleton is a noncopyable object.
+    //
+
+    ProfilerManager(const ProfilerManager&) = delete;
+
+    ProfilerManager &operator=(const ProfilerManager&) = delete;
+};
+
+#endif // _PROFILER_MANAGER_H_
diff --git a/src/storage/basestorage.h b/src/storage/basestorage.h
new file mode 100644 (file)
index 0000000..a7433db
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef _BASE_STORAGE_H_
+#define _BASE_STORAGE_H_
+
+#include <type_traits>
+#include <deque>
+
+#include "baseinfo.h"
+
+template<typename INFO>
+class BaseStorage
+{
+public:
+    typedef std::deque<INFO> Container;
+    typedef typename Container::iterator iterator;
+    typedef typename Container::const_iterator const_iterator;
+
+    static_assert(std::is_base_of<BaseInfo, INFO>::value,
+        "INFO not derived from BaseInfo");
+
+    bool HasValue(InternalID id) const noexcept
+    {
+        return id.id >= m_storage.size();
+    }
+
+    INFO &Get(InternalID id)
+    {
+        return const_cast<INFO&>(
+            const_cast<const BaseStorage<INFO>&>(*this).Get(id));
+    }
+
+    const INFO &Get(InternalID id) const
+    {
+        _ASSERTE(this->HasValue(id));
+        return m_storage[id.id];
+    }
+
+    INFO &Add()
+    {
+        m_storage.emplace_back();
+        INFO &info = m_storage.back();
+        info.internalId.id = m_storage.size() - 1;
+        return info;
+    }
+
+    iterator begin() noexcept
+    {
+        return m_storage.begin();
+    }
+
+    const_iterator begin() const noexcept
+    {
+        return m_storage.begin();
+    }
+
+    iterator end() noexcept
+    {
+        return m_storage.end();
+    }
+
+    const_iterator end() const noexcept
+    {
+        return m_storage.end();
+    }
+
+protected:
+    Container m_storage;
+};
+
+#endif // _BASE_STORAGE_H_
diff --git a/src/storage/classstorage.h b/src/storage/classstorage.h
new file mode 100644 (file)
index 0000000..4b171aa
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _CLASS_STORAGE_H_
+#define _CLASS_STORAGE_H_
+
+#include "mappedstorage.h"
+#include "classinfo.h"
+
+class ClassStorage : public MappedStorage<ClassID, ClassInfo>
+{};
+
+#endif // _CLASS_STORAGE_H_
diff --git a/src/storage/functionstorage.h b/src/storage/functionstorage.h
new file mode 100644 (file)
index 0000000..5b6b635
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _FUNCTION_STORAGE_H_
+#define _FUNCTION_STORAGE_H_
+
+#include "mappedstorage.h"
+#include "functioninfo.h"
+
+class FunctionStorage : public MappedStorage<FunctionID, FunctionInfo>
+{
+private:
+    using Base = MappedStorage<FunctionID, FunctionInfo>;
+
+public:
+    FunctionStorage(ExecutionTrace *pExecutionTrace)
+        : Base()
+        , m_pExecutionTrace(pExecutionTrace)
+    {}
+
+    std::pair<FunctionInfo&, bool>
+    Place(FunctionID id)
+    {
+        auto res = this->Base::Place(id);
+        res.first.executionTrace = m_pExecutionTrace;
+        return std::make_pair(std::ref(res.first), res.second);
+    }
+
+    // Add new function info without mapping to FunctionID. It is useful for
+    // internal pseudo-functions.
+    FunctionInfo &Add()
+    {
+        FunctionInfo &res = this->Base::Add();
+        res.executionTrace = m_pExecutionTrace;
+        return std::ref(res);
+    }
+
+protected:
+    ExecutionTrace *m_pExecutionTrace;
+};
+
+#endif // _FUNCTION_STORAGE_H_
diff --git a/src/storage/livestorage.h b/src/storage/livestorage.h
new file mode 100644 (file)
index 0000000..23d76be
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef _LIVE_STORAGE_H_
+#define _LIVE_STORAGE_H_
+
+#include <set>
+
+#include "mappedstorage.h"
+#include "iterator_range.h"
+
+template<typename ID, typename INFO>
+class LiveStorage : public MappedStorage<ID, INFO>
+{
+private:
+    using Base = MappedStorage<ID, INFO>;
+
+    struct less_info
+    {
+        bool operator()(const INFO &lhs, const INFO &rhs) const
+        {
+            return lhs.internalId.id < rhs.internalId.id;
+        }
+    };
+
+public:
+    typedef std::set<std::reference_wrapper<INFO>, less_info> LiveContainer;
+    typedef typename LiveContainer::iterator live_iterator;
+    typedef typename LiveContainer::const_iterator const_live_iterator;
+    typedef iterator_range<live_iterator> live_iterator_range;
+    typedef iterator_range<const_live_iterator> const_live_iterator_range;
+
+    std::pair<INFO&, bool>
+    Place(ID id)
+    {
+        auto res = this->Base::Place(id);
+        if (res.second)
+        {
+            m_liveStorage.insert(std::ref(res.first));
+        }
+        return std::make_pair(std::ref(res.first), res.second);
+    }
+
+    INFO &Unlink(ID id)
+    {
+        auto &res = this->Base::Unlink(id);
+        m_liveStorage.erase(std::ref(res));
+        return res;
+    }
+
+    live_iterator_range GetLiveRange()
+    {
+        return live_iterator_range(m_liveStorage.begin(), m_liveStorage.end());
+    }
+
+    const_live_iterator_range GetLiveRange() const
+    {
+        return const_live_iterator_range(
+            m_liveStorage.begin(), m_liveStorage.end());
+    }
+
+    LiveContainer GetLiveContainer() const
+    {
+        return m_liveStorage;
+    }
+
+protected:
+    LiveContainer m_liveStorage;
+};
+
+#endif // _LIVE_STORAGE_H_
diff --git a/src/storage/mappedstorage.h b/src/storage/mappedstorage.h
new file mode 100644 (file)
index 0000000..0851230
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef _MAPPED_STORAGE_H_
+#define _MAPPED_STORAGE_H_
+
+#include <unordered_map>
+#include <utility>
+#include <functional>
+
+#include "basestorage.h"
+#include "mappedinfo.h"
+
+template<typename ID, typename INFO>
+class MappedStorage : public BaseStorage<INFO>
+{
+public:
+    static_assert(std::is_base_of<MappedInfo<ID>, INFO>::value,
+        "INFO not derived from MappedInfo<ID>");
+
+    using BaseStorage<INFO>::HasValue;
+
+    bool HasValue(ID id) const
+    {
+        return m_toInternal.find(id) != m_toInternal.end();
+    }
+
+    using BaseStorage<INFO>::Get;
+
+    INFO &Get(ID id)
+    {
+        return const_cast<INFO&>(
+            const_cast<const MappedStorage<ID, INFO>&>(*this).Get(id));
+    }
+
+    const INFO &Get(ID id) const
+    {
+        auto it = m_toInternal.find(id);
+        _ASSERTE(it != m_toInternal.end());
+        return m_storage[it->second.id];
+    }
+
+    // Add and/or get info by ID. Second value in pair denoting whether
+    // the insertion took place.
+    std::pair<INFO&, bool> Place(ID id)
+    {
+        auto it = m_toInternal.find(id);
+        if (it != m_toInternal.end())
+        {
+            return std::make_pair(std::ref(m_storage[it->second.id]), false);
+        }
+        else
+        {
+            INFO &info = this->Add();
+            try
+            {
+                info.id = id;
+                m_toInternal[id] = info.internalId;
+            }
+            catch (...)
+            {
+                m_storage.pop_back(); // New value is always appended.
+                throw;
+            }
+            return std::make_pair(std::ref(info), true);
+        }
+    }
+
+    // Remap object accessible from iid to another ID. Returns old ID.
+    // Should not be called if storage doesn't have iid.
+    ID Link(ID id, InternalID iid)
+    {
+        _ASSERTE(this->HasValue(iid));
+        INFO &info = this->Get(iid);
+        ID old_id = info.id;
+        m_toInternal[id] = iid;
+        info.id = id;
+        return old_id;
+    }
+
+    // Remove ID from storage, so this ID can be used for another object later.
+    // Only ID is removed. Associated object stays accessible from internal ID.
+    // Function returns reference to this object.
+    // Should not be called if storage doesn't have ID.
+    INFO &Unlink(ID id)
+    {
+        auto it = m_toInternal.find(id);
+        _ASSERTE(it != m_toInternal.end());
+        INFO &info = m_storage[it->second.id];
+        m_toInternal.erase(it);
+        info.id = ID{};
+        return info;
+    }
+
+protected:
+    using BaseStorage<INFO>::m_storage;
+    std::unordered_map<ID, InternalID> m_toInternal;
+};
+
+#endif // _MAPPED_STORAGE_H_
diff --git a/src/storage/ringbuffer.h b/src/storage/ringbuffer.h
new file mode 100644 (file)
index 0000000..63ae738
--- /dev/null
@@ -0,0 +1,345 @@
+#ifndef _RING_BUFFER_H_
+#define _RING_BUFFER_H_
+
+#include <atomic>
+#include <limits>
+#include <stdexcept>
+#include <new>
+#include <utility>
+#include <iterator>
+
+#include <stdlib.h>
+#include <assert.h>
+
+template<typename T>
+class ring_buffer
+{
+public:
+    explicit ring_buffer(size_t capacity = 0)
+    {
+        initialize(capacity);
+    }
+
+    ring_buffer(const ring_buffer &rb)
+    {
+        initialize(rb.m_cap);
+        try
+        {
+            copy_from(rb);
+        }
+        catch (...)
+        {
+            ~ring_buffer();
+            throw;
+        }
+    }
+
+    ring_buffer(ring_buffer &&rb)
+    {
+        initialize();
+        swap(rb);
+    }
+
+    ~ring_buffer()
+    {
+        clear();
+        free(m_buf);
+    }
+
+    ring_buffer& operator=(const ring_buffer& other)
+    {
+        if (this == &other)
+            return *this;
+
+        ring_buffer tmp(other);
+        swap(tmp);
+        return *this;
+    }
+
+    ring_buffer& operator=(ring_buffer&& other)
+    {
+        swap(other);
+        return *this;
+    }
+
+    T &front() noexcept
+    {
+        assert(!empty());
+        return *m_begin;
+    }
+
+    const T &front() const noexcept
+    {
+        assert(!empty());
+        return const_cast<const T&>(*m_begin);
+    }
+
+    T &back() noexcept
+    {
+        assert(!empty());
+        return const_cast<T&>(const_cast<const ring_buffer<T>&>(*this).back());
+    }
+
+    const T &back() const noexcept
+    {
+        assert(!empty());
+
+        const T *end = m_end;
+        if (end == m_buf)
+            end = m_buf + m_cap;
+        --end;
+        return *end;
+    }
+
+    bool empty() const noexcept
+    {
+        return m_size == 0;
+    }
+
+    bool full() const noexcept
+    {
+        return m_size == m_cap;
+    }
+
+    size_t size() const noexcept
+    {
+        return m_size;
+    }
+
+    size_t max_size() const noexcept
+    {
+        static size_t max_size = std::numeric_limits<size_t>::max() / sizeof(T);
+        return max_size;
+    }
+
+    void reserve(size_t new_capacity)
+    {
+        if (new_capacity <= m_cap)
+            return;
+
+        ring_buffer tmp(new_capacity);
+        tmp.move_from(std::move(*this));
+        swap(tmp);
+    }
+
+    size_t capacity() const noexcept
+    {
+        return m_cap;
+    }
+
+    void clear() noexcept
+    {
+        size_t size = m_size;
+        while (size > 0)
+        {
+            m_begin->~T();
+            ++m_begin;
+            if (m_begin == m_buf + m_cap)
+                m_begin = m_buf;
+            --size;
+        }
+        m_size = 0;
+    }
+
+    void push_back(const T &item)
+    {
+        push_back_imp(item);
+    }
+
+    void push_back(T &&item)
+    {
+        push_back_imp(std::move(item));
+    }
+
+    void push_front(const T &item)
+    {
+        push_front_imp(item);
+    }
+
+    void push_front(T &&item)
+    {
+        push_front_imp(std::move(item));
+    }
+
+    void pop_back()
+    {
+        if (m_size == 0)
+            return;
+
+        if (m_end == m_buf)
+            m_end = m_buf + m_cap;
+        --m_end;
+        m_end->~T();
+        m_size--;
+    }
+
+    void pop_front()
+    {
+        if (m_size == 0)
+            return;
+
+        m_begin->~T();
+        ++m_begin;
+        if (m_begin == m_buf + m_cap)
+            m_begin = m_buf;
+        m_size--;
+    }
+
+    template <class InputIterator>
+    void append(InputIterator first, InputIterator last)
+    {
+        size_t c = std::distance(first, last);
+        if (c > m_cap - m_size)
+            throw std::out_of_range("ring_buffer capacity is exhausted");
+
+        size_t c1 = c;
+        size_t c2 = 0;
+        if (m_end > m_buf + m_cap - c) // m_end + c > m_buf + m_cap
+        {
+            c2 = m_end - (m_buf + m_cap - c); // (m_end + c) - (m_buf + m_cap);
+            c1 -= c2;
+        }
+        assert(c1 + c2 == std::distance(first, last));
+
+        while (c1-- > 0)
+        {
+            assert(first != last);
+            new (m_end++) T(*first++);
+        }
+
+        assert(m_end <= m_buf + m_cap);
+        if (m_end == m_buf + m_cap)
+            m_end = m_buf;
+
+        assert(c2 == 0 || m_end == m_buf); // c2 > 0 => m_end == m_buf
+        while (c2-- > 0)
+        {
+            assert(first != last);
+            new (m_end++) T(*first++);
+        }
+
+        assert(first == last);
+        m_size += c;
+    }
+
+    void swap(ring_buffer &rb) noexcept
+    {
+        std::swap(m_buf,   rb.m_buf);
+        std::swap(m_begin, rb.m_begin);
+        std::swap(m_end,   rb.m_end);
+        std::swap(m_cap,   rb.m_cap);
+        rb.m_size = m_size.exchange(rb.m_size);
+    }
+
+private:
+    void initialize() noexcept
+    {
+#ifdef _TARGET_AMD64_
+        //assert(m_size.is_lock_free()); // NOTE: With C++17 it can be checked
+                                       // staticaly.
+#endif // _TARGET_AMD64_
+        m_begin = m_end = m_buf = nullptr;
+        m_size = m_cap = 0;
+    }
+
+    void initialize(size_t capacity)
+    {
+        initialize();
+
+        if (capacity == 0)
+            return;
+        else if (capacity > max_size())
+            throw std::length_error("capacity exceeds the maximum size");
+
+        m_buf = reinterpret_cast<T*>(malloc(capacity * sizeof(T)));
+
+        if (m_buf == nullptr)
+            throw std::bad_alloc();
+
+        m_cap = capacity;
+        m_begin = m_end = m_buf;
+    }
+
+    void copy_from(const ring_buffer &rb)
+    {
+        assert(
+            m_begin == m_buf   &&
+            m_end   == m_buf   &&
+            m_size  == 0        &&
+            m_cap  >=  rb.m_cap
+        );
+
+        T *begin = rb.m_begin;
+        size_t size = 0;
+        while (size != rb.m_size)
+        {
+            new (m_end) T(*begin);
+            ++m_end;
+            ++begin;
+            if (begin == rb.m_buf + rb.m_cap)
+                begin = rb.m_buf;
+            ++size;
+        }
+        m_size = size;
+    }
+
+    void move_from(ring_buffer &&rb)
+    {
+        assert(
+            m_begin == m_buf   &&
+            m_end   == m_buf   &&
+            m_size  == 0        &&
+            m_cap  >=  rb.m_cap
+        );
+
+        T *begin = rb.m_begin;
+        size_t size = 0;
+        while (size != rb.m_size)
+        {
+            new (m_end) T(std::move(*begin));
+            ++m_end;
+            ++begin;
+            if (begin == rb.m_buf + rb.m_cap)
+                begin = rb.m_buf;
+            ++size;
+        }
+        m_size = size;
+    }
+
+    template <typename ValT>
+    void push_back_imp(ValT &&item)
+    {
+        if (full())
+            throw std::out_of_range("ring_buffer capacity is exhausted");
+
+        new (m_end) T(std::forward<ValT>(item));
+        ++m_end;
+        if (m_end == m_buf + m_cap)
+            m_end = m_buf;
+        m_size++;
+    }
+
+    template <typename ValT>
+    void push_front_impl(ValT &&item)
+    {
+        if (full())
+            throw std::out_of_range("ring_buffer capacity is exhausted");
+
+        T *begin = m_begin;
+        if (begin == m_buf)
+            begin = m_buf + m_cap;
+        --begin;
+        new (begin) T(std::forward<ValT>(item));
+        m_begin = begin;
+        m_size++;
+    }
+
+private:
+    T *m_buf;
+    T *m_begin;
+    T *m_end;
+    std::atomic_size_t m_size;
+    size_t m_cap;
+};
+
+#endif /* _RING_BUFFER_H_ */
diff --git a/src/storage/threadstorage.h b/src/storage/threadstorage.h
new file mode 100644 (file)
index 0000000..9ee8643
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _THREAD_STORAGE_H_
+#define _THREAD_STORAGE_H_
+
+#include "livestorage.h"
+#include "threadinfo.h"
+
+class ThreadStorage : public LiveStorage<ThreadID, ThreadInfo>
+{};
+
+#endif // _THREAD_STORAGE_H_
diff --git a/src/sync/binarysemaphore.h b/src/sync/binarysemaphore.h
new file mode 100644 (file)
index 0000000..7f5a86b
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef _BINARY_SEMAPHORE_H_
+#define _BINARY_SEMAPHORE_H_
+
+#include <mutex>
+#include <condition_variable>
+
+template <typename Mutex, typename CondVar>
+class basic_binary_semaphore {
+public:
+    basic_binary_semaphore();
+
+    explicit basic_binary_semaphore(bool init);
+
+    void notify();
+
+    void wait();
+
+    bool try_wait();
+
+private:
+    Mutex   m_mutex;
+    CondVar m_cv;
+    bool    m_val;
+};
+
+using binary_semaphore =
+    basic_binary_semaphore<std::mutex, std::condition_variable>;
+
+template <typename Mutex, typename CondVar>
+basic_binary_semaphore<Mutex, CondVar>::basic_binary_semaphore()
+    : m_val(false)
+{}
+
+template <typename Mutex, typename CondVar>
+basic_binary_semaphore<Mutex, CondVar>::basic_binary_semaphore(bool init)
+    : m_val(init)
+{}
+
+template <typename Mutex, typename CondVar>
+void basic_binary_semaphore<Mutex, CondVar>::notify()
+{
+    std::lock_guard<Mutex> lock(m_mutex);
+    m_val = true;;
+    m_cv.notify_one();
+}
+
+template <typename Mutex, typename CondVar>
+void basic_binary_semaphore<Mutex, CondVar>::wait()
+{
+    std::unique_lock<Mutex> lock(m_mutex);
+    while (!m_val)
+    {
+        m_cv.wait(lock);
+    }
+    m_val = false;
+}
+
+template <typename Mutex, typename CondVar>
+bool basic_binary_semaphore<Mutex, CondVar>::try_wait()
+{
+    std::lock_guard<Mutex> lock(m_mutex);
+    if (m_val)
+    {
+        m_val = false;
+        return true;
+    }
+    return false;
+}
+
+#endif // _BINARY_SEMAPHORE_H_
diff --git a/src/sync/shared_mutex.cpp b/src/sync/shared_mutex.cpp
new file mode 100644 (file)
index 0000000..1e0847d
--- /dev/null
@@ -0,0 +1,88 @@
+#include <system_error>
+
+#include <pthread.h>
+#include <errno.h>
+
+#include "shared_mutex.h"
+
+struct shared_mutex::impl
+{
+    pthread_rwlock_t rwlock;
+};
+
+shared_mutex::shared_mutex()
+    : pimpl(new impl())
+{
+    if (pthread_rwlock_init(&pimpl->rwlock, NULL))
+    {
+        throw std::system_error(errno, std::system_category(),
+            "can't create shared_mutex");
+    }
+}
+
+shared_mutex::~shared_mutex()
+{
+    pthread_rwlock_destroy(&pimpl->rwlock);
+}
+
+void shared_mutex::lock()
+{
+    if (pthread_rwlock_wrlock(&pimpl->rwlock))
+    {
+        throw std::system_error(errno, std::system_category(),
+            "can't lock shared_mutex");
+    }
+}
+
+bool shared_mutex::try_lock()
+{
+    int err = pthread_rwlock_trywrlock(&pimpl->rwlock);
+
+    if (err && err != EBUSY)
+    {
+        throw std::system_error(errno, std::system_category(),
+            "can't exclusively lock shared_mutex");
+    }
+
+    return err == 0;
+}
+
+void shared_mutex::unlock()
+{
+    if (pthread_rwlock_unlock(&pimpl->rwlock))
+    {
+        throw std::system_error(errno, std::system_category(),
+            "can't exclusively unlock shared_mutex");
+    }
+}
+
+void shared_mutex::lock_shared()
+{
+    if (pthread_rwlock_rdlock(&pimpl->rwlock))
+    {
+        throw std::system_error(errno, std::system_category(),
+            "can't shared lock shared_mutex");
+    }
+}
+
+bool shared_mutex::try_lock_shared()
+{
+    int err = pthread_rwlock_tryrdlock(&pimpl->rwlock);
+
+    if (err && err != EBUSY)
+    {
+        throw std::system_error(errno, std::system_category(),
+            "can't shared lock shared_mutex");
+    }
+
+    return err == 0;
+}
+
+void shared_mutex::unlock_shared()
+{
+    if (pthread_rwlock_unlock(&pimpl->rwlock))
+    {
+        throw std::system_error(errno, std::system_category(),
+            "can't shared unlock shared_mutex");
+    }
+}
diff --git a/src/sync/shared_mutex.h b/src/sync/shared_mutex.h
new file mode 100644 (file)
index 0000000..d8df9ab
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _SHARED_MUTEX_H_
+#define _SHARED_MUTEX_H_
+
+#include <memory>
+
+class shared_mutex
+{
+public:
+    shared_mutex();
+
+    shared_mutex(const shared_mutex&) = delete;
+
+    ~shared_mutex();
+
+    shared_mutex &operator=(const shared_mutex&) = delete;
+
+    void lock();
+
+    bool try_lock();
+
+    void unlock();
+
+    void lock_shared();
+
+    bool try_lock_shared();
+
+    void unlock_shared();
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
+#endif // _SHARED_MUTEX_H_
diff --git a/src/sync/sharedresource.h b/src/sync/sharedresource.h
new file mode 100644 (file)
index 0000000..1ab7d87
--- /dev/null
@@ -0,0 +1,183 @@
+#ifndef _SHARED_RESOURCE_H_
+#define _SHARED_RESOURCE_H_
+
+#include "shared_mutex.h"
+
+template<typename T, typename Mutex = shared_mutex>
+class SharedResource
+{
+private:
+    template<class SR>
+    class AccessorBase
+    {
+    public:
+        ~AccessorBase() = default;
+
+        AccessorBase(const AccessorBase&) = delete;
+
+        AccessorBase &operator=(const AccessorBase&) = delete;
+
+        AccessorBase(AccessorBase &&other) :
+            m_shared_resource(other.m_shared_resource)
+        {
+            other.m_shared_resource = nullptr;
+        }
+
+        AccessorBase &operator=(AccessorBase &&other)
+        {
+            if (&other != this)
+            {
+                m_shared_resource = other.m_shared_resource;
+                other.m_shared_resource = nullptr;
+            }
+            return *this;
+        }
+
+        bool isValid() const noexcept
+        {
+            return m_shared_resource != nullptr;
+        }
+
+    protected:
+        SR *m_shared_resource; // Mutable or constant pointer to SharedResource.
+
+        AccessorBase(SR *resource)
+            : m_shared_resource(resource)
+        {}
+    };
+
+    template<class SR>
+    class ExclusiveAccessor : public AccessorBase<SR>
+    {
+    public:
+        ~ExclusiveAccessor()
+        {
+            if (this->isValid())
+            {
+                this->m_shared_resource->m_mutex.unlock();
+            }
+        }
+
+        ExclusiveAccessor(ExclusiveAccessor &&other) = default;
+
+        ExclusiveAccessor &operator=(ExclusiveAccessor &&other) = default;
+
+    protected:
+        ExclusiveAccessor(SR *resource)
+            : AccessorBase<SR>(resource)
+        {
+            this->m_shared_resource->m_mutex.lock();
+        }
+    };
+
+    template<class SR>
+    class SharedAccessor : public AccessorBase<SR>
+    {
+    public:
+        ~SharedAccessor()
+        {
+            if (this->isValid())
+            {
+                this->m_shared_resource->m_mutex.unlock_shared();
+            }
+        }
+
+        SharedAccessor(SharedAccessor &&other) = default;
+
+        SharedAccessor &operator=(SharedAccessor &&other) = default;
+
+    protected:
+        SharedAccessor(SR *resource)
+            : AccessorBase<SR>(resource)
+        {
+            this->m_shared_resource->m_mutex.lock_shared();
+        }
+    };
+
+public:
+    template<class A>
+    class MutableAccessor : public A
+    {
+        friend class SharedResource<T, Mutex>;
+
+    public:
+        T *operator->()
+        {
+            return &this->m_shared_resource->m_resource;
+        }
+
+        T &operator*()
+        {
+            return this->m_shared_resource->m_resource;
+        }
+
+        using A::A; // Protected constructor.
+    };
+
+    template<class A>
+    class ConstAccessor : public A
+    {
+        friend class SharedResource<T, Mutex>;
+
+    public:
+        const T *operator->() const
+        {
+            return &this->m_shared_resource->m_resource;
+        }
+
+        const T &operator*() const
+        {
+            return this->m_shared_resource->m_resource;
+        }
+
+        using A::A; // Protected constructor.
+    };
+
+    template<typename ...Args>
+    SharedResource(Args&& ...args)
+        : m_resource(std::forward<Args>(args)...)
+    {}
+
+    ~SharedResource() = default;
+
+    SharedResource(const SharedResource&) = delete;
+
+    SharedResource(SharedResource&&) = delete;
+
+    SharedResource &operator=(const SharedResource&) = delete;
+
+    SharedResource &operator=(SharedResource&&) = delete;
+
+    // Should not be used in concurent environment.
+    T *get() const noexcept
+    {
+        return const_cast<T*>(&m_resource);
+    }
+
+    auto lock() ->
+        MutableAccessor<ExclusiveAccessor<SharedResource<T, Mutex>>>
+    {
+        // Implicit conversion to accessor with mutable exclusive lock.
+        return this;
+    }
+
+    auto lock_const() const ->
+        ConstAccessor<ExclusiveAccessor<const SharedResource<T, Mutex>>>
+    {
+        // Implicit conversion to accessor with constant exclusive lock.
+        return this;
+    }
+
+    auto lock_shared() const ->
+        ConstAccessor<SharedAccessor<const SharedResource<T, Mutex>>>
+    {
+        // Implicit conversion to accessor with constant shared lock.
+        return this;
+    }
+
+private:
+    T             m_resource;
+    mutable Mutex m_mutex;
+};
+
+#endif //_SHARED_RESOURCE_H_
diff --git a/src/trace/basetrace.cpp b/src/trace/basetrace.cpp
new file mode 100644 (file)
index 0000000..2406a8e
--- /dev/null
@@ -0,0 +1,29 @@
+#include "profiler.h"
+#include "profilerinfo.h"
+#include "basetrace.h"
+
+BaseTrace::BaseTrace(Profiler &profiler)
+    : m_disabled(true)
+    , m_profiler(profiler)
+    , m_info(profiler.GetProfilerInfo())
+{
+}
+
+BaseTrace::~BaseTrace()
+{
+}
+
+Log &BaseTrace::LOG() const noexcept
+{
+    return m_profiler.LOG();
+}
+
+ITraceLog &BaseTrace::TRACE() const noexcept
+{
+    return m_profiler.TRACE();
+}
+
+bool BaseTrace::IsEnabled() const noexcept
+{
+    return !m_disabled;
+}
diff --git a/src/trace/basetrace.h b/src/trace/basetrace.h
new file mode 100644 (file)
index 0000000..8107977
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _BASE_TRACE_H_
+#define _BASE_TRACE_H_
+
+class Profiler;
+
+class ProfilerInfo;
+
+class Log;
+
+class ITraceLog;
+
+class BaseTrace
+{
+protected:
+    BaseTrace(Profiler &profiler);
+
+    ~BaseTrace();
+
+    Log &LOG() const noexcept;
+
+    ITraceLog &TRACE() const noexcept;
+
+public:
+    bool IsEnabled() const noexcept;
+
+protected:
+    bool m_disabled;
+
+    Profiler &m_profiler;
+    const ProfilerInfo &m_info;
+};
+
+#endif // _BASE_TRACE_H_
diff --git a/src/trace/commontrace.cpp b/src/trace/commontrace.cpp
new file mode 100644 (file)
index 0000000..eab3713
--- /dev/null
@@ -0,0 +1,1189 @@
+#include <memory>
+#include <utility>
+#include <system_error>
+#include <exception>
+#include <stdexcept>
+
+#include <string.h>
+#include <signal.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include <winerror.h>
+
+#include "profiler.h"
+#include "intervalsplitter.h"
+#include "commontrace.h"
+
+#define CONTROL_SIGNAL_MIN (SIGRTMIN + 4)
+#define LOG_SIGNAL         (CONTROL_SIGNAL_MIN + 0)
+#define LOG_SIGNAL_STOP    (CONTROL_SIGNAL_MIN + 1)
+#define SAMPLE_SIGNAL      (CONTROL_SIGNAL_MIN + 2)
+#define SAMPLING_PAUSE     (CONTROL_SIGNAL_MIN + 3)
+#define SAMPLING_RESUME    (CONTROL_SIGNAL_MIN + 4)
+#define SAMPLING_EVENT     (CONTROL_SIGNAL_MIN + 5)
+#define SAMPLING_STOP      (CONTROL_SIGNAL_MIN + 6)
+#define CONTROL_SIGNAL_END (CONTROL_SIGNAL_MIN + 7)
+#define CONTROL_SIGNAL_MAX (CONTROL_SIGNAL_END - 1)
+
+// NOTE: currently only one instance of the CommonTrace can exist at each
+// moment, so global variable can be used.
+static CommonTrace *g_pCommonTraceObject = nullptr;
+
+static void SampleHandlerStub(
+    int code, siginfo_t *siginfo, void *context)
+{
+    if (code != SAMPLE_SIGNAL)
+    {
+        return;
+    }
+
+    CommonTrace *trace =
+        reinterpret_cast<CommonTrace*>(siginfo->si_value.sival_ptr);
+
+    if (trace != nullptr && trace->IsEnabled())
+    {
+        int terrno = errno;
+        trace->HandleSample(context);
+        errno = terrno;
+    }
+}
+
+static void SamplingPauseResumeHandlerStub(int code)
+{
+    bool shouldPause;
+
+    if (code == SAMPLING_PAUSE)
+    {
+        shouldPause = true;
+    }
+    else if (code == SAMPLING_RESUME)
+    {
+        shouldPause = false;
+    }
+    else
+    {
+        return;
+    }
+
+    if (g_pCommonTraceObject != nullptr && g_pCommonTraceObject->IsEnabled())
+    {
+        int terrno = errno;
+        g_pCommonTraceObject->HandleSamplingPauseResume(shouldPause);
+        errno = terrno;
+    }
+}
+
+static struct timespec MsToTS(unsigned long ms)
+{
+    return { ms / 1000, ms % 1000 * 1000000 };
+}
+
+CommonTrace::CommonTrace(Profiler &profiler)
+    : BaseTrace(profiler)
+    , m_tlsThreadInfoIndex(TLS_OUT_OF_INDEXES)
+    , m_threadStorage()
+    , m_classStorage()
+    , m_pauseAction()
+    , m_resumeAction()
+    , m_sampleAction()
+    , m_logThread()
+    , m_samplingThread()
+    , m_samplingSuspended(true)
+{
+    _ASSERTE(g_pCommonTraceObject == nullptr);
+    g_pCommonTraceObject = this;
+}
+
+CommonTrace::~CommonTrace()
+{
+    // NOTE: we are dealing with a partially destroyed m_profiler!
+    this->Shutdown();
+
+    if (m_tlsThreadInfoIndex != TLS_OUT_OF_INDEXES)
+    {
+        if (!TlsFree(m_tlsThreadInfoIndex))
+        {
+            m_profiler.HandleHresult(
+                "CommonTrace::~CommonTrace(): TlsFree()",
+                HRESULT_FROM_WIN32(GetLastError())
+            );
+        }
+    }
+
+    _ASSERTE(g_pCommonTraceObject == this);
+    g_pCommonTraceObject = nullptr;
+}
+
+void CommonTrace::ProcessConfig(ProfilerConfig &config)
+{
+    //
+    // Check activation condition.
+    //
+
+    if (config.ExecutionTraceEnabled || config.MemoryTraceEnabled)
+    {
+        m_disabled = false;
+    }
+    else
+    {
+        return;
+    }
+
+    //
+    // Performe runtime checks.
+    //
+
+    if (CONTROL_SIGNAL_MAX > SIGRTMAX)
+    {
+        throw std::runtime_error(
+            "CommonTrace::ProcessConfig(): Not enought real-time signals");
+    }
+
+    //
+    // Line Tracing.
+    //
+
+//#if !defined(_TARGET_ARM_) && !defined(_TARGET_X86_)
+    if (config.LineTraceEnabled)
+    {
+        config.LineTraceEnabled = false;
+        LOG().Warn() <<
+            "Line tracing currently is not supported at this platform";
+    }
+//#endif // _TARGET_ARM_ or _TARGET_X86_
+
+    //
+    // Initializing thread local storage.
+    //
+
+    m_tlsThreadInfoIndex = TlsAlloc();
+    if (m_tlsThreadInfoIndex == TLS_OUT_OF_INDEXES)
+    {
+        throw HresultException(
+            "CommonTrace::ProcessConfig(): TlsAlloc()",
+            HRESULT_FROM_WIN32(GetLastError())
+        );
+    }
+
+    //
+    // Setup signal handlers.
+    //
+
+    try
+    {
+        struct sigaction action;
+        memset(&action, 0, sizeof(struct sigaction));
+        action.sa_handler = SamplingPauseResumeHandlerStub;
+        sigemptyset(&action.sa_mask);
+        sigaddset(&action.sa_mask, SAMPLING_PAUSE);
+        sigaddset(&action.sa_mask, SAMPLING_RESUME);
+        action.sa_flags = SA_RESTART;
+
+        SigAction pauseAction  ( SAMPLING_PAUSE,  action );
+        SigAction resumeAction ( SAMPLING_RESUME, action );
+
+        m_pauseAction  = std::move(pauseAction);
+        m_resumeAction = std::move(resumeAction);
+    }
+    catch (const std::exception &e)
+    {
+        m_profiler.HandleException(e);
+        LOG().Warn() << "Tracing pause/resume functionality is disabled";
+    }
+
+    if (config.HighGranularityEnabled)
+    {
+        try
+        {
+            struct sigaction action;
+            memset(&action, 0, sizeof(struct sigaction));
+            action.sa_sigaction = SampleHandlerStub;
+            sigemptyset(&action.sa_mask);
+            action.sa_flags = SA_RESTART | SA_SIGINFO;
+            m_sampleAction = SigAction(SAMPLE_SIGNAL, action);
+        }
+        catch (const std::exception &e)
+        {
+            m_profiler.HandleException(e);
+            config.HighGranularityEnabled = false;
+            LOG().Warn() << "Hight granularity option is disabled";
+        }
+    }
+
+    //
+    // Starting service threads.
+    //
+
+    m_samplingSuspended = config.TracingSuspendedOnStart;
+
+    {
+        binary_semaphore threadInitializedSem;
+        m_logThread = std::thread(
+            &CommonTrace::LogThread, this,
+            &threadInitializedSem
+        );
+        threadInitializedSem.wait();
+
+        if (config.CollectionMethod == CollectionMethod::Sampling)
+        {
+            m_samplingThread = std::thread(
+                &CommonTrace::SamplingThread, this,
+                &threadInitializedSem
+            );
+            threadInitializedSem.wait();
+        }
+    }
+
+    //
+    // Event Mask calculation.
+    //
+
+    HRESULT hr;
+    DWORD events;
+    hr = m_info.v1()->GetEventMask(&events);
+    if (FAILED(hr))
+    {
+        throw HresultException(
+            "CommonTrace::ProcessConfig(): GetEventMask()", hr);
+    }
+
+    events = events
+        | COR_PRF_MONITOR_APPDOMAIN_LOADS
+        | COR_PRF_MONITOR_ASSEMBLY_LOADS
+        | COR_PRF_MONITOR_MODULE_LOADS
+        | COR_PRF_MONITOR_CLASS_LOADS
+        | COR_PRF_MONITOR_THREADS;
+
+    // This events are required for tracing of call stack dynamics.
+
+    if (config.LineTraceEnabled)
+    {
+        events |= COR_PRF_ENABLE_STACK_SNAPSHOT;
+    }
+
+    hr = m_info.v1()->SetEventMask(events);
+    if (FAILED(hr))
+    {
+        throw HresultException(
+            "CommonTrace::ProcessConfig(): SetEventMask()", hr);
+    }
+}
+
+void CommonTrace::Shutdown() noexcept
+{
+    m_disabled = true;
+
+    // Ensure service threads are joined before this object will be destroyed.
+    if (m_samplingThread.joinable())
+    {
+        this->SendStopSampling();
+        m_samplingThread.join();
+    }
+    if (m_logThread.joinable())
+    {
+        this->SendStopLog();
+        m_logThread.join();
+    }
+
+    // Restore signal handlers to defaults.
+    m_pauseAction  . Release();
+    m_resumeAction . Release();
+    m_sampleAction . Release();
+}
+
+__forceinline void CommonTrace::SendDoSample(ThreadInfo &thrInfo) noexcept
+{
+    union sigval val;
+    val.sival_ptr = this;
+    int ev = pthread_sigqueue(thrInfo.nativeHandle, SAMPLE_SIGNAL, val);
+    // It is OK if the limit of signals which may be queued has been reached.
+    if (ev && ev != EAGAIN)
+    {
+        m_profiler.HandleSysErr(
+            "CommonTrace::SendDoStackTraceSample(): pthread_sigqueue()", ev);
+    }
+}
+
+__forceinline void CommonTrace::SendDoLog(ThreadInfo &thrInfo) noexcept
+{
+    _ASSERTE(!m_disabled);
+    _ASSERTE(m_logThread.joinable());
+
+    union sigval val;
+    val.sival_ptr = &thrInfo;
+    int ev = pthread_sigqueue(m_logThread.native_handle(), LOG_SIGNAL, val);
+    // It is OK if the limit of signals which may be queued has been reached.
+    if (ev && ev != EAGAIN)
+    {
+        m_profiler.HandleSysErr(
+            "CommonTrace::SendDoLog(): pthread_sigqueue()", ev);
+    }
+}
+
+__forceinline void CommonTrace::SendStopLog() noexcept
+{
+    _ASSERTE(m_logThread.joinable());
+
+    int ev = pthread_kill(m_logThread.native_handle(), LOG_SIGNAL_STOP);
+    if (ev)
+    {
+        m_profiler.HandleSysErr(
+            "CommonTrace::SendStopLog(): pthread_kill()", ev);
+    }
+}
+
+__forceinline void CommonTrace::SendSamplingEvent(SamplingEvent event) noexcept
+{
+    _ASSERTE(!m_disabled);
+    _ASSERTE(m_samplingThread.joinable());
+
+    union sigval val;
+    val.sival_int = static_cast<int>(event);
+    int ev = pthread_sigqueue(
+        m_samplingThread.native_handle(), SAMPLING_EVENT, val);
+    // It is OK if the limit of signals which may be queued has been reached.
+    if (ev && ev != EAGAIN)
+    {
+        m_profiler.HandleSysErr(
+            "CommonTrace::SendSamplingEvent(): pthread_sigqueue()", ev);
+    }
+}
+
+__forceinline void CommonTrace::SendStopSampling() noexcept
+{
+    _ASSERTE(m_samplingThread.joinable());
+
+    int ev = pthread_kill(m_samplingThread.native_handle(), SAMPLING_STOP);
+    if (ev)
+    {
+        m_profiler.HandleSysErr(
+            "CommonTrace::SendStopSampling(): pthread_kill()", ev);
+    }
+}
+
+void CommonTrace::LogThread(binary_semaphore *pInitialized) noexcept
+{
+    try
+    {
+        //
+        // Initialization.
+        //
+
+        int ev;
+        sigset_t set;
+        try
+        {
+            sigemptyset(&set);
+            sigaddset(&set, LOG_SIGNAL);
+            sigaddset(&set, LOG_SIGNAL_STOP);
+            ev = pthread_sigmask(SIG_BLOCK, &set, NULL);
+            if (ev)
+            {
+                throw std::system_error(ev, std::system_category(),
+                    "CommonTrace::LogThread(): pthread_sigmask()");
+            }
+
+            pInitialized->notify();
+            // NOTE: semaphore can be destroyed after notification.
+            pInitialized = nullptr;
+        }
+        catch (const std::exception &e)
+        {
+            pInitialized->notify();
+            m_profiler.HandleException(e);
+            return;
+        }
+
+        //
+        // Working loop.
+        //
+
+        for (;;)
+        {
+            siginfo_t siginfo;
+            sigwaitinfo(&set, &siginfo);
+            if (siginfo.si_signo == LOG_SIGNAL_STOP)
+            {
+                break;
+            }
+            else if (siginfo.si_signo != LOG_SIGNAL)
+            {
+                continue;
+            }
+
+            _ASSERTE(siginfo.si_signo == LOG_SIGNAL);
+
+            ThreadInfo *pThreadInfo =
+                reinterpret_cast<ThreadInfo*>(siginfo.si_value.sival_ptr);
+            for (
+                // Local copy of volatile data.
+                size_t count = pThreadInfo->eventChannel.GetEventSummaryCount();
+                count > 0; --count)
+            {
+                const EventSummary &summary =
+                    pThreadInfo->eventChannel.GetCurrentEventSummary();
+                TRACE().DumpSample(pThreadInfo->internalId, summary);
+                pThreadInfo->eventChannel.NextEventSummary();
+            }
+        }
+    }
+    catch (const std::exception &e)
+    {
+        m_profiler.HandleException(e);
+    }
+}
+
+void CommonTrace::SamplingThread(binary_semaphore *pInitialized) noexcept
+{
+    try
+    {
+        //
+        // Initialization.
+        //
+
+        int ev;
+        sigset_t set;
+        try
+        {
+            sigemptyset(&set);
+            sigaddset(&set, SAMPLING_EVENT);
+            sigaddset(&set, SAMPLING_STOP);
+            ev = pthread_sigmask(SIG_BLOCK, &set, NULL);
+            if (ev)
+            {
+                throw std::system_error(ev, std::system_category(),
+                    "CommonTrace::SamplingThread(): pthread_sigmask()");
+            }
+
+            pInitialized->notify();
+            // NOTE: semaphore can be destroyed after notification.
+            pInitialized = nullptr;
+        }
+        catch (const std::exception &e)
+        {
+            pInitialized->notify();
+            m_profiler.HandleException(e);
+            return;
+        }
+
+        //
+        // Working loop.
+        //
+
+        IntervalSplitter splitter(m_profiler.GetConfig().SamplingTimeoutMs);
+        ThreadStorage::LiveContainer liveThreads;
+        ThreadStorage::LiveContainer::iterator  itThrInfo = liveThreads.begin();
+        ThreadStorage::LiveContainer::iterator endThrInfo = liveThreads.end();
+
+        for (;;)
+        {
+            siginfo_t siginfo;
+            int rv;
+            if (!m_samplingSuspended)
+            {
+                struct timespec ts;
+
+                if (itThrInfo == endThrInfo)
+                {
+                    {
+                        auto storage_lock = this->GetThreadStorage();
+                        liveThreads = storage_lock->GetLiveContainer();
+                    }
+                    itThrInfo  = liveThreads.begin();
+                    endThrInfo = liveThreads.end();
+                    splitter.Reset(liveThreads.size());
+                }
+
+                if (!liveThreads.empty())
+                {
+                    auto storage_lock = this->GetThreadStorage();
+                    ThreadInfo &thrInfo = *itThrInfo++;
+
+                    // We update all live threads if they are attached to OS
+                    // threads.
+                    if (thrInfo.id != 0 && thrInfo.nativeHandle != 0)
+                    {
+                        thrInfo.genTicks++; // OK with unsigned overflows.
+                        if (m_profiler.GetConfig().HighGranularityEnabled)
+                        {
+                            this->SendDoSample(thrInfo);
+                        }
+                    }
+
+                    ts = MsToTS(splitter.GetNext());
+                }
+                else
+                {
+                    ts = MsToTS(m_profiler.GetConfig().SamplingTimeoutMs);
+                }
+
+                // NOTE: Sleep() function has better precision so we use it
+                // for short pauses.
+                if (ts.tv_sec == 0)
+                {
+                    Sleep(ts.tv_nsec / 1000000);
+                    ts.tv_nsec = 0;
+                }
+                rv = sigtimedwait(&set, &siginfo, &ts);
+            }
+            else
+            {
+                rv = sigwaitinfo(&set, &siginfo);
+            }
+
+            if (rv == -1 && errno == EAGAIN)
+            {
+                continue;
+            }
+            else if (rv == SAMPLING_EVENT)
+            {
+                SamplingEvent event = static_cast<SamplingEvent>(
+                    siginfo.si_value.sival_int);
+                switch (event)
+                {
+                case SamplingEvent::SAMPLING_EVENT_PAUSE:
+                    if (m_samplingSuspended == false)
+                    {
+                        TRACE().DumpProfilerTracingPause(
+                            m_profiler.GetTickCountFromInit());
+                    }
+                    m_samplingSuspended = true;
+                    break;
+
+                case SamplingEvent::SAMPLING_EVENT_RESUME:
+                    if (m_samplingSuspended == true)
+                    {
+                        TRACE().DumpProfilerTracingResume(
+                            m_profiler.GetTickCountFromInit());
+                        // Should restart threads round.
+                        itThrInfo = endThrInfo;
+                    }
+                    m_samplingSuspended = false;
+                    break;
+                }
+
+            }
+            else if (rv == SAMPLING_STOP)
+            {
+                break; // End of loop!
+            }
+            else
+            {
+                m_profiler.HandleSysErr(
+                    "CommonTrace::SamplingThread(): sigtimedwait()", errno);
+            }
+        }
+    }
+    catch (const std::exception &e)
+    {
+        m_profiler.HandleException(e);
+    }
+}
+
+__forceinline void CommonTrace::DoSampleWithAction(
+    ThreadInfo &thrInfo,
+    SamplingAction action,
+    SamplingSharedState &state) noexcept
+{
+    _ASSERTE(!thrInfo.interruptible);
+
+    ExecutionTrace &executionTrace = m_profiler.GetExecutionTrace();
+    MemoryTrace    &memoryTrace    = m_profiler.GetMemoryTrace();
+
+    state.genTicks = thrInfo.genTicks; // Local copy of volatile data.
+    bool needSample = executionTrace . NeedSample(thrInfo, state) ||
+                      memoryTrace    . NeedSample(thrInfo, state);
+
+    if (needSample)
+    {
+        executionTrace . PrepareSample(thrInfo, state);
+        // memoryTrace    . PrepareSample(thrInfo, state);
+    }
+
+    if (action)
+    {
+        action(thrInfo, state);
+    }
+
+    if (needSample)
+    {
+        state.isSampleSucceeds = thrInfo.eventChannel.Sample(
+            m_profiler.GetTickCountFromInit(),
+            // OK with unsigned overflows in ticks.
+            state.genTicks - thrInfo.fixTicks);
+        if (state.isSampleSucceeds)
+        {
+            this->SendDoLog(thrInfo);
+        }
+        executionTrace . AfterSample(thrInfo, state);
+        // memoryTrace    . AfterSample(thrInfo, state);
+        thrInfo.fixTicks = state.genTicks;
+    }
+}
+
+__forceinline void CommonTrace::DoSampleFromHandler(
+    ThreadInfo &thrInfo, void *context) noexcept
+{
+    _ASSERTE(thrInfo.interruptible);
+
+    SamplingSharedState state = {};
+    state.context = context;
+
+    ExecutionTrace &executionTrace = m_profiler.GetExecutionTrace();
+    MemoryTrace    &memoryTrace    = m_profiler.GetMemoryTrace();
+
+    state.genTicks = thrInfo.genTicks; // Local copy of volatile data.
+    bool needSample = executionTrace . NeedSample(thrInfo, state) ||
+                      memoryTrace    . NeedSample(thrInfo, state);
+
+    if (needSample)
+    {
+        executionTrace . PrepareSample(thrInfo, state);
+        // memoryTrace    . PrepareSample(thrInfo, state);
+
+        state.isSampleSucceeds = thrInfo.eventChannel.Sample(
+            m_profiler.GetTickCountFromInit(),
+            // OK with unsigned overflows in ticks.
+            state.genTicks - thrInfo.fixTicks,
+            // NOTE: we can't reallocate memory from signal handler.
+            ChanCanRealloc::NO);
+        if (state.isSampleSucceeds)
+        {
+            this->SendDoLog(thrInfo);
+        }
+
+        executionTrace . AfterSample(thrInfo, state);
+        // memoryTrace    . AfterSample(thrInfo, state);
+        thrInfo.fixTicks = state.genTicks;
+    }
+}
+
+ThreadInfo *CommonTrace::GetThreadInfo() noexcept
+{
+    try {
+        //
+        // Try to get thread info from the local storage.
+        //
+
+        ThreadInfo *threadInfo = reinterpret_cast<ThreadInfo*>(
+            TlsGetValue(m_tlsThreadInfoIndex));
+
+        if (threadInfo == nullptr)
+        {
+            DWORD lastError = GetLastError();
+            if (lastError != ERROR_SUCCESS)
+            {
+                m_profiler.HandleHresult(
+                    "CommonTrace::GetThreadInfo(): TlsGetValue()",
+                    HRESULT_FROM_WIN32(lastError)
+                );
+            }
+        }
+
+        HRESULT hr;
+
+        //
+        // Fast check if current thread is changed.
+        //
+
+        ThreadID threadId = 0;
+        hr = m_info.v1()->GetCurrentThreadID(&threadId);
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "CommonTrace::GetThreadInfo(): GetCurrentThreadID()", hr);
+        }
+
+        if (threadInfo == nullptr || threadInfo->id != threadId)
+        {
+            //
+            // We should update thread info.
+            //
+
+            // Get or create thread info for current thread ID.
+            ThreadInfo *oldThreadInfo = threadInfo;
+            auto storage_lock = m_threadStorage.lock();
+            threadInfo = &storage_lock->Place(threadId).first;
+
+            // Get current OS thread ID.
+            DWORD osThreadId = 0;
+            hr = m_info.v1()->GetThreadInfo(threadId, &osThreadId);
+            // This is OK if we can't obtain osThreadId in some special cases.
+            if (FAILED(hr) && hr != CORPROF_E_UNSUPPORTED_CALL_SEQUENCE)
+            {
+                m_profiler.HandleHresult(
+                    "CommonTrace::GetThreadInfo(): GetThreadInfo()", hr);
+            }
+
+            // Check if OS thread ID changed and update it.
+            if (oldThreadInfo != nullptr &&
+                oldThreadInfo->osThreadId == osThreadId)
+            {
+                oldThreadInfo->osThreadId   = 0;
+                oldThreadInfo->nativeHandle = 0;
+            }
+            threadInfo->osThreadId   = osThreadId;
+            threadInfo->nativeHandle = pthread_self();
+
+            //
+            // Save new thead info to the local storage.
+            //
+
+            if (!TlsSetValue(m_tlsThreadInfoIndex, threadInfo))
+            {
+                m_profiler.HandleHresult(
+                    "CommonTrace::GetThreadInfo(): TlsSetValue()",
+                    HRESULT_FROM_WIN32(GetLastError())
+                );
+            }
+        }
+
+        return threadInfo;
+    }
+    catch (const std::exception &e)
+    {
+        m_profiler.HandleException(e);
+        return nullptr;
+    }
+}
+
+ThreadInfo *CommonTrace::GetThreadInfoR() const noexcept
+{
+    //
+    // Try to get thread info from the local storage.
+    //
+
+    ThreadInfo *threadInfo = reinterpret_cast<ThreadInfo*>(
+        TlsGetValue(m_tlsThreadInfoIndex));
+
+#ifdef _TARGET_AMD64_
+    if (threadInfo == nullptr)
+    {
+        return nullptr;
+    }
+
+    //
+    // Fast check if current thread is changed.
+    //
+
+    HRESULT hr;
+    ThreadID threadId = 0;
+    hr = m_info.v1()->GetCurrentThreadID(&threadId);
+    if (FAILED(hr) || threadInfo->id != threadId)
+    {
+        return nullptr;
+    }
+#endif // _TARGET_AMD64_
+
+    return threadInfo;
+}
+
+void CommonTrace::InterruptSampling(
+    SamplingSharedState &state,
+    SamplingAction      beforeAction,
+    SamplingAction      action,
+    SamplingAction      afterAction) noexcept
+{
+    ThreadInfo *pThreadInfo = m_profiler.GetCommonTrace().GetThreadInfo();
+    if (pThreadInfo != nullptr)
+    {
+        pThreadInfo->interruptible = false;
+
+        this->DoSampleWithAction(*pThreadInfo, beforeAction, state);
+
+        if (action)
+        {
+            action(*pThreadInfo, state);
+        }
+
+        this->DoSampleWithAction(*pThreadInfo, afterAction, state);
+
+        pThreadInfo->interruptible = true;
+    }
+}
+
+__forceinline void CommonTrace::HandleSample(void *context) noexcept
+{
+    _ASSERTE(!m_disabled);
+    ThreadInfo *pThreadInfo = this->GetThreadInfoR();
+    if (pThreadInfo && pThreadInfo->interruptible)
+    {
+        DoSampleFromHandler(*pThreadInfo, context);
+    }
+}
+
+__forceinline void CommonTrace::HandleSamplingPauseResume(
+    bool shouldPause) noexcept
+{
+    _ASSERTE(!m_disabled);
+    if (m_profiler.GetConfig().CollectionMethod == CollectionMethod::Sampling)
+    {
+        this->SendSamplingEvent(
+            shouldPause ? SamplingEvent::SAMPLING_EVENT_PAUSE :
+                          SamplingEvent::SAMPLING_EVENT_RESUME
+        );
+    }
+    else if (m_profiler.GetConfig().CollectionMethod ==
+        CollectionMethod::Instrumentation)
+    {
+        m_samplingSuspended = shouldPause;
+    }
+}
+
+bool CommonTrace::IsSamplingSuspended() const noexcept
+{
+    return m_samplingSuspended;
+}
+
+HRESULT CommonTrace::AppDomainCreationFinished(
+    AppDomainID appDomainId,
+    HRESULT hrStatus) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        ULONG     size = 0;
+        ProcessID processId = 0;
+
+        hr = m_info.v1()->GetAppDomainInfo(
+            appDomainId, 0, &size, nullptr, &processId);
+
+        std::unique_ptr<WCHAR[]> name = nullptr;
+        if (SUCCEEDED(hr))
+        {
+            name.reset(new (std::nothrow) WCHAR[size]);
+            if (name)
+            {
+                hr = m_info.v1()->GetAppDomainInfo(
+                    appDomainId, size, nullptr, name.get(), nullptr);
+            }
+        }
+
+        TRACE().DumpAppDomainCreationFinished(
+            appDomainId, name.get(), processId, hrStatus);
+
+        // Do it after dump.
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "CommonTrace::AppDomainCreationFinished()", hr);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::AssemblyLoadFinished(
+    AssemblyID assemblyId,
+    HRESULT hrStatus) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        ULONG      size = 0;
+        AssemblyID appDomainId = 0;
+        ModuleID   moduleId = 0;
+
+        hr = m_info.v1()->GetAssemblyInfo(
+            assemblyId, 0, &size, nullptr, &appDomainId, &moduleId);
+
+        std::unique_ptr<WCHAR[]> name = nullptr;
+        if (SUCCEEDED(hr))
+        {
+            name.reset(new (std::nothrow) WCHAR[size]);
+            if (name)
+            {
+                hr = m_info.v1()->GetAssemblyInfo(
+                    assemblyId, size, nullptr, name.get(), nullptr, nullptr);
+            }
+        }
+
+        TRACE().DumpAssemblyLoadFinished(
+            assemblyId, name.get(), appDomainId, moduleId, hrStatus);
+
+        // Do it after dump.
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "CommonTrace::AssemblyLoadFinished(): GetAssemblyInfo()", hr);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ModuleLoadFinished(
+    ModuleID moduleId,
+    HRESULT hrStatus) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        ULONG      size = 0;
+        LPCBYTE    baseLoadAddress = 0;
+        AssemblyID assemblyId = 0;
+
+        hr = m_info.v1()->GetModuleInfo(
+            moduleId, &baseLoadAddress, 0, &size, nullptr, &assemblyId);
+
+        std::unique_ptr<WCHAR[]> name = nullptr;
+        if (SUCCEEDED(hr))
+        {
+            name.reset(new (std::nothrow) WCHAR[size]);
+            if (name)
+            {
+                hr = m_info.v1()->GetModuleInfo(
+                    moduleId, nullptr, size, nullptr, name.get(), nullptr);
+            }
+        }
+
+        TRACE().DumpModuleLoadFinished(
+            moduleId, baseLoadAddress, name.get(), assemblyId, hrStatus);
+
+        // Do it after dump.
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "CommonTrace::ModuleLoadFinished(): GetModuleInfo()", hr);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ModuleAttachedToAssembly(
+    ModuleID moduleId,
+    AssemblyID assemblyId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        TRACE().DumpModuleAttachedToAssembly(moduleId, assemblyId);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ClassLoadStarted(
+    ClassID classId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        m_classStorage.lock()->Place(classId);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ClassLoadFinished(
+    ClassID classId,
+    HRESULT hrStatus) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    if (m_info.v1()->IsArrayClass(classId, nullptr, nullptr, nullptr) == S_OK)
+    {
+        LOG().Warn() << "Array class in ClassLoadFinished()";
+    }
+
+    HRESULT hr = S_OK;
+    try
+    {
+        auto storage_lock = m_classStorage.lock();
+        ClassInfo &classInfo = storage_lock->Get(classId);
+        hr = classInfo.Initialize(m_profiler, *storage_lock);
+
+        TRACE().DumpClassLoadFinished(classInfo, hrStatus);
+        if (!classInfo.isNamePrinted)
+        {
+            TRACE().DumpClassName(classInfo);
+            classInfo.isNamePrinted = true;
+        }
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ClassUnloadStarted(
+    ClassID classId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        m_classStorage.lock()->Unlink(classId);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ThreadCreated(
+    ThreadID threadId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        InternalID threadIid =
+            m_threadStorage.lock()->Place(threadId).first.internalId;
+        TRACE().DumpThreadCreated(threadId, threadIid);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ThreadDestroyed(
+    ThreadID threadId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        InternalID threadIid;
+        {
+            auto storage_lock = m_threadStorage.lock();
+            ThreadInfo &thrInfo = storage_lock->Unlink(threadId);
+            thrInfo.osThreadId = 0;
+            threadIid = thrInfo.internalId;
+        }
+        TRACE().DumpThreadDestroyed(threadIid);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT CommonTrace::ThreadAssignedToOSThread(
+    ThreadID managedThreadId,
+    DWORD osThreadId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        InternalID threadIid;
+        {
+            auto storage_lock = m_threadStorage.lock();
+            ThreadInfo &thrInfo = storage_lock->Get(managedThreadId);
+
+            if (thrInfo.osThreadId != osThreadId)
+            {
+                // Get current OS thread ID.
+                DWORD currentOsThreadId = 0;
+                hr = m_info.v1()->GetThreadInfo(
+                    managedThreadId, &currentOsThreadId);
+                // Check if we can setup OS thread ID.
+                if (SUCCEEDED(hr) && currentOsThreadId == osThreadId)
+                {
+                    thrInfo.nativeHandle = pthread_self();
+                }
+                else
+                {
+                    // This will be updated by GetThreadInfo() later.
+                    thrInfo.nativeHandle = 0;
+                    if (FAILED(hr))
+                    {
+                        m_profiler.HandleHresult(
+                            "CommonTrace::ThreadAssignedToOSThread(): "
+                            "GetThreadInfo()", hr
+                        );
+                    }
+                }
+                thrInfo.osThreadId = osThreadId;
+            }
+            threadIid = thrInfo.internalId;
+        }
+        TRACE().DumpThreadAssignedToOSThread(threadIid, osThreadId);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+auto CommonTrace::GetThreadStorage() ->
+    decltype(m_threadStorage.lock())
+{
+    return m_threadStorage.lock();
+}
+
+auto CommonTrace::GetThreadStorage() const ->
+    decltype(m_threadStorage.lock_shared())
+{
+    return m_threadStorage.lock_shared();
+}
+
+auto CommonTrace::GetClassStorage() ->
+    decltype(m_classStorage.lock())
+{
+    return m_classStorage.lock();
+}
+
+auto CommonTrace::GetClassStorage() const ->
+    decltype(m_classStorage.lock_shared())
+{
+    return m_classStorage.lock_shared();
+}
diff --git a/src/trace/commontrace.h b/src/trace/commontrace.h
new file mode 100644 (file)
index 0000000..9320e58
--- /dev/null
@@ -0,0 +1,154 @@
+#ifndef _COMMON_TRACE_H_
+#define _COMMON_TRACE_H_
+
+#include <thread>
+
+#ifdef _TARGET_AMD64_
+#include <future>
+#endif // _TARGET_AMD64_
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "basetrace.h"
+
+#include "sharedresource.h"
+#include "threadstorage.h"
+#include "classstorage.h"
+#include "sigaction.h"
+#include "binarysemaphore.h"
+
+class CommonTrace final : public BaseTrace
+{
+public:
+    CommonTrace(Profiler &profiler);
+
+    ~CommonTrace();
+
+    void ProcessConfig(ProfilerConfig &config);
+
+    void Shutdown() noexcept;
+
+    struct SamplingSharedState
+    {
+        ULONG genTicks;
+        bool  isIpRestored;
+        bool  isSampleSucceeds;
+        void  *context;
+        bool  stackWillBeChanged;
+    };
+
+    typedef std::function<void(ThreadInfo&, SamplingSharedState&)>
+        SamplingAction;
+
+private:
+    enum class SamplingEvent
+    {
+        SAMPLING_EVENT_PAUSE,
+        SAMPLING_EVENT_RESUME,
+    };
+
+    void SendDoSample(ThreadInfo &thrInfo) noexcept;
+
+    void SendDoLog(ThreadInfo &thrInfo) noexcept;
+
+    void SendStopLog() noexcept;
+
+    void SendSamplingEvent(SamplingEvent event) noexcept;
+
+    void SendStopSampling() noexcept;
+
+    void LogThread(binary_semaphore *pInitialized) noexcept;
+
+    void SamplingThread(binary_semaphore *pInitialized) noexcept;
+
+    void DoSampleWithAction(
+        ThreadInfo &thrInfo,
+        SamplingAction action,
+        SamplingSharedState &state) noexcept;
+
+    void DoSampleFromHandler(
+        ThreadInfo &thrInfo, void *context) noexcept;
+
+public:
+    ThreadInfo *GetThreadInfo() noexcept;
+
+    // Simple and safety version of GetThreadInfo() that can be used in signal
+    // handlers.
+    ThreadInfo *GetThreadInfoR() const noexcept;
+
+    void InterruptSampling(
+        SamplingSharedState &state,
+        SamplingAction      beforeAction = {},
+        SamplingAction      action       = {},
+        SamplingAction      afterAction  = {}) noexcept;
+
+    void HandleSample(void *context) noexcept;
+
+    void HandleSamplingPauseResume(bool shouldPause) noexcept;
+
+    bool IsSamplingSuspended() const noexcept;
+
+    HRESULT AppDomainCreationFinished(
+        AppDomainID appDomainId,
+        HRESULT hrStatus) noexcept;
+
+    HRESULT AssemblyLoadFinished(
+        AssemblyID assemblyId,
+        HRESULT hrStatus) noexcept;
+
+    HRESULT ModuleLoadFinished(
+        ModuleID moduleId,
+        HRESULT hrStatus) noexcept;
+
+    HRESULT ModuleAttachedToAssembly(
+        ModuleID moduleId,
+        AssemblyID assemblyId) noexcept;
+
+    HRESULT ClassLoadStarted(
+        ClassID classId) noexcept;
+
+    HRESULT ClassLoadFinished(
+        ClassID classId,
+        HRESULT hrStatus) noexcept;
+
+    HRESULT ClassUnloadStarted(
+        ClassID classId) noexcept;
+
+    HRESULT ThreadCreated(
+        ThreadID threadId) noexcept;
+
+    HRESULT ThreadDestroyed(
+        ThreadID threadId) noexcept;
+
+    HRESULT ThreadAssignedToOSThread(
+        ThreadID managedThreadId,
+        DWORD osThreadId) noexcept;
+
+private:
+    int m_tlsThreadInfoIndex;
+
+    SharedResource<ThreadStorage> m_threadStorage;
+    SharedResource<ClassStorage>  m_classStorage;
+
+    SigAction m_pauseAction;
+    SigAction m_resumeAction;
+    SigAction m_sampleAction;
+
+    std::thread m_logThread;
+    std::thread m_samplingThread;
+
+    bool m_samplingSuspended;
+
+public:
+    auto GetThreadStorage() -> decltype(m_threadStorage.lock());
+
+    auto GetThreadStorage() const -> decltype(m_threadStorage.lock_shared());
+
+    auto GetClassStorage() -> decltype(m_classStorage.lock());
+
+    auto GetClassStorage() const -> decltype(m_classStorage.lock_shared());
+};
+
+#endif // _COMMON_TRACE_H_
diff --git a/src/trace/cputrace.cpp b/src/trace/cputrace.cpp
new file mode 100644 (file)
index 0000000..b923961
--- /dev/null
@@ -0,0 +1,148 @@
+#include <system_error>
+#include <exception>
+
+#include <errno.h>
+
+#include "profiler.h"
+#include "cputrace.h"
+
+CpuTrace::CpuTrace(Profiler &profiler)
+    : BaseTrace(profiler)
+    , m_logThread()
+    , lastUserTime(0)
+{
+}
+
+CpuTrace::~CpuTrace()
+{
+    // NOTE: we are dealing with a partially destroyed m_profiler!
+    this->Shutdown();
+}
+
+void CpuTrace::ProcessConfig(ProfilerConfig &config)
+{
+    //
+    // Check activation condition.
+    //
+
+    if (config.CpuTraceProcessEnabled || config.CpuTraceThreadEnabled)
+    {
+        m_disabled = false;
+    }
+    else
+    {
+        return;
+    }
+
+    //
+    // Starting service threads.
+    //
+
+    m_logThread = std::thread(&CpuTrace::LogThread, this);
+}
+
+void CpuTrace::Shutdown() noexcept
+{
+    m_disabled = true;
+    if (m_logThread.joinable())
+    {
+        m_logThread.join();
+    }
+}
+
+// static
+DWORD64 CpuTrace::GetClockTime(clockid_t clk_id)
+{
+    struct timespec ts;
+    if (clock_gettime(clk_id, &ts))
+    {
+        throw std::system_error(errno, std::system_category(),
+            "CpuTrace::GetClockTime(): clock_gettime()");
+    }
+    return (DWORD64)ts.tv_sec * 1000000 + ts.tv_nsec / 1000; // (us)
+}
+
+void CpuTrace::LogProcessTime()
+{
+    DWORD64 userTimeUs64 = CpuTrace::GetClockTime(CLOCK_PROCESS_CPUTIME_ID);
+
+    TRACE().DumpProcessTimes(
+        m_profiler.GetTickCountFromInit(),
+        userTimeUs64 - lastUserTime
+    );
+
+    lastUserTime = userTimeUs64;
+}
+
+void CpuTrace::LogThreadTime(ThreadInfo &thrInfo)
+{
+    clockid_t cid;
+    int err = pthread_getcpuclockid(thrInfo.nativeHandle, &cid);
+    if (err && err != ESRCH)
+    {
+        throw std::system_error(err, std::system_category(),
+            "CpuTrace::LogThreadTimes(): pthread_getcpuclockid()");
+    }
+    else if (err == ESRCH)
+    {
+        return;
+    }
+
+    DWORD64 userTimeUs64 = CpuTrace::GetClockTime(cid);
+
+    TRACE().DumpThreadTimes(
+        thrInfo.internalId,
+        m_profiler.GetTickCountFromInit(),
+        userTimeUs64 - thrInfo.lastUserTime
+    );
+
+    thrInfo.lastUserTime = userTimeUs64;
+}
+
+void CpuTrace::LogThread() noexcept
+{
+    try
+    {
+        lastUserTime = CpuTrace::GetClockTime(CLOCK_PROCESS_CPUTIME_ID);
+
+        while(m_disabled == false)
+        {
+            Sleep(m_profiler.GetConfig().CpuTraceTimeoutMs);
+            if (m_disabled)
+            {
+                break;
+            }
+            else if (m_profiler.GetCommonTrace().IsSamplingSuspended())
+            {
+                continue;
+            }
+
+            if (m_profiler.GetConfig().CpuTraceProcessEnabled)
+            {
+                this->LogProcessTime();
+            }
+
+            if (m_profiler.GetConfig().CpuTraceThreadEnabled)
+            {
+                auto storage_lock =
+                    m_profiler.GetCommonTrace().GetThreadStorage();
+                for (ThreadInfo &thrInfo : storage_lock->GetLiveRange())
+                {
+                    if (m_disabled == true)
+                        break;
+
+                    // We update all live threads if they are attached to OS
+                    // threads.
+                    if (thrInfo.id != 0 && thrInfo.nativeHandle != 0)
+                    {
+                        this->LogThreadTime(thrInfo);
+                    }
+                }
+            }
+        }
+    }
+    catch (const std::exception &e)
+    {
+        m_profiler.HandleException(e);
+    }
+}
diff --git a/src/trace/cputrace.h b/src/trace/cputrace.h
new file mode 100644 (file)
index 0000000..2e28d34
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _CPU_TRACE_H_
+#define _CPU_TRACE_H_
+
+#include <thread>
+
+#include <time.h>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "basetrace.h"
+
+#include "threadinfo.h"
+
+class CpuTrace final : public BaseTrace
+{
+public:
+    CpuTrace(Profiler &profiler);
+
+    ~CpuTrace();
+
+    void ProcessConfig(ProfilerConfig &config);
+
+    void Shutdown() noexcept;
+
+private:
+    static DWORD64 GetClockTime(clockid_t clk_id);
+
+    void LogProcessTime();
+
+    void LogThreadTime(ThreadInfo &thrInfo);
+
+    void LogThread() noexcept;
+
+private:
+    std::thread m_logThread;
+
+    DWORD64     lastUserTime;
+};
+
+#endif // _CPU_TRACE_H_
diff --git a/src/trace/eventchannel.cpp b/src/trace/eventchannel.cpp
new file mode 100644 (file)
index 0000000..b13b903
--- /dev/null
@@ -0,0 +1,210 @@
+#include <assert.h>
+
+#include "eventchannel.h"
+
+#define EVENT_CHANNEL_START_CAP 128 // Should be power of 2.
+
+using Stack = EventSummary::Stack;
+
+EventSummary::EventSummary(Stack::size_type stackSize)
+    : ticks(0)
+    , count(0)
+    , matchPrefixSize(stackSize)
+    , stackSize(stackSize)
+    , ipIsChanged(false)
+    , ip(0)
+    , newFrames()
+{}
+
+bool EventSummary::HasStackSample() const noexcept
+{
+    return count > 0
+        || matchPrefixSize != stackSize
+        || ipIsChanged
+        || newFrames.size() > 0;
+}
+
+bool EventSummary::HasAllocSample() const noexcept
+{
+    return allocTable.size() > 0;
+}
+
+EventChannel::EventChannel()
+    : m_stack()
+    , m_currentState()
+    , m_buffer(EVENT_CHANNEL_START_CAP)
+    , m_mutex()
+    , m_bufferCapacityIncreaseIsPlanned(false)
+{}
+
+void EventChannel::IncreaseBufferCapacity()
+{
+    m_buffer.reserve(m_buffer.capacity() * 2);
+    m_bufferCapacityIncreaseIsPlanned = false;
+}
+
+bool EventChannel::EnsureBufferCapacity(ChanCanRealloc canRealloc)
+{
+    bool isBufferNoSpace = m_buffer.size() == m_buffer.capacity();
+    Stack::difference_type needStackSize =
+        m_stack.size() - m_currentState.matchPrefixSize;
+    bool isStackNoSpace = m_currentState.newFrames.capacity() < needStackSize;
+    switch (canRealloc)
+    {
+    case ChanCanRealloc::NO:
+        if (isBufferNoSpace)
+        {
+            this->PlanToIncreaseBufferCapacity();
+        }
+        assert(!isStackNoSpace);
+        return !isBufferNoSpace;
+
+    case ChanCanRealloc::YES:
+        if (isBufferNoSpace || m_bufferCapacityIncreaseIsPlanned)
+        {
+            std::lock_guard<decltype(m_mutex)> lock(m_mutex);
+            this->IncreaseBufferCapacity();
+        }
+        if (isStackNoSpace)
+        {
+            m_currentState.newFrames.reserve(needStackSize);
+        }
+        assert(m_buffer.capacity() != m_buffer.size());
+        assert(m_currentState.newFrames.capacity() >= needStackSize);
+        return true;
+    }
+}
+
+void EventChannel::Push(const FunctionInfo &funcInfo) noexcept
+{
+    assert(m_currentState.matchPrefixSize <= m_stack.size());
+    m_stack.push_back(Frame{&funcInfo, 0});
+
+    // XXX: exception in this call will terminate process!
+    this->EnsureBufferCapacity(); // Perform planned reallocation.
+}
+
+void EventChannel::Pop() noexcept
+{
+    assert(!m_stack.empty());
+    assert(m_currentState.matchPrefixSize <= m_stack.size());
+    m_stack.pop_back();
+    if (m_stack.size() < m_currentState.matchPrefixSize)
+    {
+        m_currentState.matchPrefixSize = m_stack.size();
+        m_currentState.ipIsChanged = false;
+    }
+
+    // XXX: exception in this call will terminate process!
+    this->EnsureBufferCapacity(); // Perform planned reallocation.
+}
+
+void EventChannel::ChIP(UINT_PTR ip, size_t idxFromTop) noexcept
+{
+    assert(idxFromTop < m_stack.size());
+    assert(m_currentState.matchPrefixSize <= m_stack.size());
+    assert(
+        m_stack.size() - idxFromTop >= m_currentState.matchPrefixSize
+    );
+
+    Frame &frame = const_cast<Frame&>(this->GetFrameFromTop(idxFromTop));
+    size_t frameIdx = m_stack.size() - idxFromTop - 1;
+    assert(&m_stack[frameIdx] == &frame);
+    if (frame.ip != ip)
+    {
+        if (frameIdx + 1 == m_currentState.matchPrefixSize)
+        {
+            m_currentState.ipIsChanged = true;
+        }
+        frame.ip = ip;
+    }
+
+    // XXX: exception in this call will terminate process!
+    this->EnsureBufferCapacity(); // Perform planned reallocation.
+}
+
+void EventChannel::Allocation(
+    ClassInfo &classInfo, SIZE_T size, UINT_PTR ip) noexcept
+{
+    AllocInfo &allocInfo =
+        m_currentState.allocTable[classInfo.internalId.id][ip];
+    allocInfo.allocCount++;
+    allocInfo.memSize += size;
+}
+
+bool EventChannel::Sample(
+    DWORD ticks, ULONG count, ChanCanRealloc canRealloc) noexcept
+{
+    assert(m_currentState.matchPrefixSize <= m_stack.size());
+    assert(m_currentState.matchPrefixSize <= m_currentState.stackSize);
+    assert(!m_currentState.ipIsChanged || m_currentState.matchPrefixSize > 0);
+
+    // XXX: exception in this call will terminate process!
+    if (!this->EnsureBufferCapacity(canRealloc))
+    {
+        // No space for new sample.
+        return false;
+    }
+
+    m_currentState.ticks = ticks;
+    m_currentState.count = count;
+
+    if (m_currentState.ipIsChanged)
+    {
+        m_currentState.ip = m_stack[m_currentState.matchPrefixSize - 1].ip;
+    }
+
+    assert(m_currentState.newFrames.size() == 0);
+    m_currentState.newFrames.assign(
+        m_stack.cbegin() + m_currentState.matchPrefixSize, m_stack.cend());
+
+    m_buffer.push_back(std::move(m_currentState));
+    m_currentState = EventSummary(m_stack.size());
+
+    return true;
+}
+
+void EventChannel::PlanToIncreaseBufferCapacity() noexcept
+{
+    m_bufferCapacityIncreaseIsPlanned = true;
+}
+
+Stack::size_type EventChannel::GetStackSize() const noexcept
+{
+    return m_stack.size();
+}
+
+bool EventChannel::HasStackSample() const noexcept
+{
+    return m_currentState.HasStackSample() ||
+        m_stack.size() > m_currentState.matchPrefixSize;
+}
+
+bool EventChannel::HasAllocSample() const noexcept
+{
+    return m_currentState.HasAllocSample();
+}
+
+const Frame &EventChannel::GetFrameFromTop(
+    Stack::size_type idxFromTop) const noexcept
+{
+    assert(idxFromTop < m_stack.size());
+    return m_stack.rbegin()[idxFromTop];
+}
+
+size_t EventChannel::GetEventSummaryCount() const noexcept
+{
+    return m_buffer.size();
+}
+
+const EventSummary &EventChannel::GetCurrentEventSummary() noexcept
+{
+    m_mutex.lock();
+    return m_buffer.front();
+}
+
+void EventChannel::NextEventSummary() noexcept
+{
+    m_buffer.pop_front();
+    m_mutex.unlock();
+}
diff --git a/src/trace/eventchannel.h b/src/trace/eventchannel.h
new file mode 100644 (file)
index 0000000..12aadf4
--- /dev/null
@@ -0,0 +1,129 @@
+#ifndef _EVENT_CHANNEL_H_
+#define _EVENT_CHANNEL_H_
+
+#include <utility>
+#include <vector>
+#include <map>
+#include <mutex>
+#include <atomic>
+
+#include "functioninfo.h"
+#include "classinfo.h"
+#include "ringbuffer.h"
+
+struct Frame
+{
+    const FunctionInfo *pFuncInfo;
+    UINT_PTR ip;
+};
+
+struct AllocInfo
+{
+    SIZE_T allocCount = 0;
+    SIZE_T memSize    = 0;
+};
+
+typedef std::map<ULONG, std::map<UINT_PTR, AllocInfo>> AllocTable;
+
+struct EventSummary
+{
+    typedef std::vector<Frame> Stack;
+
+    explicit EventSummary(Stack::size_type stackSize = 0);
+
+    //
+    // Sample
+    //
+
+    DWORD                  ticks;
+    ULONG                  count;
+
+    //
+    // Stack
+    //
+
+    Stack::difference_type matchPrefixSize;
+    Stack::size_type       stackSize;
+    bool                   ipIsChanged;
+    UINT_PTR               ip;
+    Stack                  newFrames;
+
+    bool HasStackSample() const noexcept;
+
+    //
+    // Allocations
+    //
+
+    AllocTable             allocTable;
+
+    bool HasAllocSample() const noexcept;
+};
+
+enum class ChanCanRealloc
+{
+    NO,
+    YES
+};
+
+class EventChannel
+{
+public:
+    typedef EventSummary::Stack Stack;
+
+    EventChannel();
+
+private:
+    void IncreaseBufferCapacity();
+
+    bool EnsureBufferCapacity(ChanCanRealloc canRealloc = ChanCanRealloc::YES);
+
+public:
+    //
+    // Writer methods.
+    //
+
+    void Push(const FunctionInfo &funcInfo) noexcept;
+
+    void Pop() noexcept;
+
+    void ChIP(UINT_PTR ip, size_t idxFromTop = 0) noexcept;
+
+    void Allocation(
+        ClassInfo &classInfo, SIZE_T size, UINT_PTR ip = 0) noexcept;
+
+    bool Sample(
+        DWORD ticks, ULONG count,
+        ChanCanRealloc canRealloc = ChanCanRealloc::YES) noexcept;
+
+    void PlanToIncreaseBufferCapacity() noexcept;
+
+    Stack::size_type GetStackSize() const noexcept;
+
+    const Frame &GetFrameFromTop(
+        Stack::size_type idxFromTop = 0) const noexcept;
+
+    bool HasStackSample() const noexcept;
+
+    bool HasAllocSample() const noexcept;
+
+    //
+    // Reader methods.
+    //
+
+    size_t GetEventSummaryCount() const noexcept;
+
+    // Reference only valid until next call to NextEventSummary().
+    const EventSummary &GetCurrentEventSummary() noexcept;
+
+    void NextEventSummary() noexcept;
+
+private:
+    Stack m_stack;
+    EventSummary m_currentState;
+
+    ring_buffer<EventSummary> m_buffer;
+    std::mutex m_mutex;
+    bool m_bufferCapacityIncreaseIsPlanned;
+};
+
+#endif // _EVENT_CHANNEL_H_
diff --git a/src/trace/executiontrace.cpp b/src/trace/executiontrace.cpp
new file mode 100644 (file)
index 0000000..13d7743
--- /dev/null
@@ -0,0 +1,656 @@
+#include <exception>
+#include <tuple>
+
+#include "profiler.h"
+#include "executiontrace.h"
+
+EXTERN_C UINT_PTR __stdcall FunctionIDMapStub(
+    FunctionID funcId,
+    void *clientData,
+    BOOL *pbHookFunction)
+{
+    return reinterpret_cast<ExecutionTrace*>(clientData)->
+        FunctionIDMap(funcId, pbHookFunction);
+}
+
+EXTERN_C __stdcall void EnterNaked3(FunctionIDOrClientID functionIDOrClientID);
+EXTERN_C __stdcall void LeaveNaked3(FunctionIDOrClientID functionIDOrClientID);
+EXTERN_C __stdcall void TailcallNaked3(FunctionIDOrClientID functionIDOrClientID);
+
+#ifdef _TARGET_ARM_
+EXTERN_C UINT_PTR getPrevPC();
+#endif // _TARGET_ARM_
+
+EXTERN_C void __stdcall EnterStub(FunctionIDOrClientID functionIDOrClientID)
+{
+    UINT_PTR ip = 0;
+
+#ifdef _TARGET_ARM_
+    ip = getPrevPC();
+#endif // _TARGET_ARM_
+
+    FunctionInfo *funcInfo = reinterpret_cast<FunctionInfo*>(
+        functionIDOrClientID.clientID);
+    funcInfo->executionTrace->Enter(*funcInfo, ip);
+}
+
+EXTERN_C void __stdcall LeaveStub(FunctionIDOrClientID functionIDOrClientID)
+{
+    FunctionInfo *funcInfo = reinterpret_cast<FunctionInfo*>(
+        functionIDOrClientID.clientID);
+    funcInfo->executionTrace->Leave(*funcInfo);
+}
+
+EXTERN_C void __stdcall TailcallStub(FunctionIDOrClientID functionIDOrClientID)
+{
+    FunctionInfo *funcInfo = reinterpret_cast<FunctionInfo*>(
+        functionIDOrClientID.clientID);
+    funcInfo->executionTrace->Tailcall(*funcInfo);
+}
+
+ExecutionTrace::ExecutionTrace(Profiler &profiler)
+    : BaseTrace(profiler)
+    , m_functionStorage(this)
+    , m_pUnmanagedFunctionInfo(nullptr)
+    , m_pJitFunctionInfo(nullptr)
+{
+    auto storage_lock = m_functionStorage.lock();
+
+    m_pUnmanagedFunctionInfo = &storage_lock->Add();
+    m_pJitFunctionInfo       = &storage_lock->Add();
+
+    m_pUnmanagedFunctionInfo->name     = W("<UNMANAGED>");
+    m_pUnmanagedFunctionInfo->fullName = m_pUnmanagedFunctionInfo->name;
+    m_pJitFunctionInfo->name           = W("<JIT>");
+    m_pJitFunctionInfo->fullName       = m_pJitFunctionInfo->name;
+}
+
+ExecutionTrace::~ExecutionTrace()
+{
+    // NOTE: we are dealing with a partially destroyed m_profiler!
+    this->Shutdown();
+}
+
+void ExecutionTrace::ProcessConfig(ProfilerConfig &config)
+{
+    //
+    // Check activation condition.
+    //
+
+    if (config.ExecutionTraceEnabled || config.MemoryTraceEnabled)
+    {
+        m_disabled = false;
+    }
+    else
+    {
+        return;
+    }
+
+    //
+    // Announce names of the special functions.
+    //
+
+    TRACE().DumpJITFunctionName(*m_pUnmanagedFunctionInfo);
+    TRACE().DumpJITFunctionName(*m_pJitFunctionInfo);
+
+    //
+    // Event Mask calculation.
+    //
+
+    HRESULT hr;
+    DWORD events;
+    hr = m_info.v1()->GetEventMask(&events);
+    if (FAILED(hr))
+    {
+        throw HresultException(
+            "ExecutionTrace::ProcessConfig(): GetEventMask()", hr);
+    }
+
+    // This events are common for execution tracing.
+    events = events
+        | COR_PRF_MONITOR_JIT_COMPILATION
+        | COR_PRF_MONITOR_CACHE_SEARCHES
+        | COR_PRF_MONITOR_FUNCTION_UNLOADS;
+
+    if (config.CollectionMethod == CollectionMethod::Instrumentation ||
+        config.CollectionMethod == CollectionMethod::Sampling)
+    {
+        if (m_info.version() < 3)
+        {
+            LOG().Warn() <<
+                "ICorProfilerInfo3 is required for current configuration";
+            goto next_stage;
+        }
+
+        m_info.v3()->SetFunctionIDMapper2(FunctionIDMapStub, this);
+
+        hr = m_info.v3()->SetEnterLeaveFunctionHooks3(
+            EnterNaked3, LeaveNaked3, TailcallNaked3);
+        if (FAILED(hr))
+        {
+            m_profiler.HandleHresult(
+                "ExecutionTrace::ProcessConfig(): "
+                "SetEnterLeaveFunctionHooks3()", hr
+            );
+            goto next_stage;
+        }
+
+        // This events are required for tracing of call stack dynamics.
+        events = events
+            | COR_PRF_MONITOR_ENTERLEAVE
+            | COR_PRF_MONITOR_CODE_TRANSITIONS
+            | COR_PRF_MONITOR_EXCEPTIONS;
+    }
+
+next_stage:
+    //
+    // Set Event Mask.
+    //
+
+    hr = m_info.v1()->SetEventMask(events);
+    if (FAILED(hr))
+    {
+        throw HresultException(
+            "ExecutionTrace::ProcessConfig(): SetEventMask()", hr);
+    }
+}
+
+void ExecutionTrace::Shutdown() noexcept
+{
+    m_disabled = true;
+}
+
+bool ExecutionTrace::IsPseudoFunction(
+    const FunctionInfo &funcInfo) const noexcept
+{
+    _ASSERTE(m_pUnmanagedFunctionInfo != nullptr);
+    _ASSERTE(m_pUnmanagedFunctionInfo->internalId.id == 0);
+    _ASSERTE(m_pJitFunctionInfo != nullptr);
+    _ASSERTE(m_pJitFunctionInfo->internalId.id == 1);
+
+    return funcInfo.internalId.id >= 0 && funcInfo.internalId.id <= 1;
+}
+
+UINT_PTR ExecutionTrace::GetCurrentManagedIP(
+    ThreadInfo &thrInfo, CONTEXT *winContext) noexcept
+{
+    struct Snapshot {
+        FunctionID funcId;
+        UINT_PTR   pc;
+
+        static HRESULT __stdcall Callback(
+            FunctionID funcId,
+            UINT_PTR ip,
+            COR_PRF_FRAME_INFO frameInfo,
+            ULONG32 contextSize,
+            BYTE context[],
+            void *clientData)
+        {
+            Snapshot *data = reinterpret_cast<Snapshot*>(clientData);
+
+            if (funcId == data->funcId)
+            {
+                // We have HIT !
+                data->pc = ip;
+                return E_FAIL; // != S_OK
+            }
+
+            return S_OK;
+        }
+    };
+
+    const FunctionInfo &funcInfo =
+        *thrInfo.eventChannel.GetFrameFromTop().pFuncInfo;
+    if (this->IsPseudoFunction(funcInfo))
+    {
+        return 0;
+    }
+
+    // XXX: prevent deadlock at DoStackSnapshot() call.
+    if (std::uncaught_exception())
+    {
+        return 0;
+    }
+
+    _ASSERTE(m_info.version() >= 2);
+    Snapshot data = {funcInfo.id, 0};
+    m_info.v2()->DoStackSnapshot(thrInfo.id, Snapshot::Callback,
+        COR_PRF_SNAPSHOT_DEFAULT, &data,
+        reinterpret_cast<BYTE*>(winContext), sizeof(winContext));
+
+    return data.pc;
+}
+
+void ExecutionTrace::RestoreManagedIP(
+    ThreadInfo &thrInfo, CONTEXT *winContext) noexcept
+{
+    struct Snapshot {
+        ExecutionTrace &execTrace;
+        EventChannel   &channel;
+        size_t         idxFromTop;
+        size_t         maxIdxFromTop;
+        size_t         maxUndIdxFromTop;
+
+        static HRESULT __stdcall Callback(
+            FunctionID funcId,
+            UINT_PTR ip,
+            COR_PRF_FRAME_INFO frameInfo,
+            ULONG32 contextSize,
+            BYTE context[],
+            void *clientData)
+        {
+            Snapshot *data = reinterpret_cast<Snapshot*>(clientData);
+
+            ExecutionTrace &execTrace    = data->execTrace;
+            EventChannel   &channel      = data->channel;
+            size_t         &idxFromTop   = data->idxFromTop; // Reference!
+            size_t         maxIdxFromTop = data->maxIdxFromTop;
+            size_t         &maxUndIdxFromTop =
+                data->maxUndIdxFromTop; // Reference!
+
+            _ASSERTE(maxIdxFromTop < channel.GetStackSize());
+            _ASSERTE(idxFromTop <= maxIdxFromTop);
+
+            // Skip pseudo-functions.
+            const Frame *pFrame = &channel.GetFrameFromTop(idxFromTop);
+            while (execTrace.IsPseudoFunction(*pFrame->pFuncInfo))
+            {
+                if (++idxFromTop > maxIdxFromTop)
+                {
+                    return E_FAIL; // Stop unwinding.
+                }
+                pFrame = &channel.GetFrameFromTop(idxFromTop);
+            }
+
+            if (funcId == pFrame->pFuncInfo->id)
+            {
+                // We have HIT !
+                if (ip != 0 || idxFromTop == 0)
+                {
+                    channel.ChIP(ip, idxFromTop++);
+                }
+                else
+                {
+                    maxUndIdxFromTop = idxFromTop++;
+                }
+            }
+
+            // Continue for next function on stack or stop unwinding.
+            return idxFromTop <= maxIdxFromTop ? S_OK : E_FAIL;
+        }
+    };
+
+    // XXX: prevent deadlock at DoStackSnapshot() call.
+    if (std::uncaught_exception())
+    {
+        return;
+    }
+
+    _ASSERTE(m_info.version() >= 2);
+    Snapshot data = {
+        *this,
+        thrInfo.eventChannel,
+        0,
+        thrInfo.maxRestoreIpIdx,
+        0,
+    };
+    HRESULT hr;
+    hr = m_info.v2()->DoStackSnapshot(thrInfo.id, Snapshot::Callback,
+        COR_PRF_SNAPSHOT_DEFAULT, &data,
+        reinterpret_cast<BYTE*>(winContext), sizeof(winContext));
+    if (FAILED(hr) && hr != CORPROF_E_STACKSNAPSHOT_ABORTED)
+    {
+        thrInfo.eventChannel.ChIP(0);
+    }
+    else if (data.idxFromTop > data.maxIdxFromTop)
+    {
+        thrInfo.maxRestoreIpIdx = data.maxUndIdxFromTop;
+    }
+}
+
+bool ExecutionTrace::NeedSample(
+    ThreadInfo &thrInfo, SamplingSharedState &state) const noexcept
+{
+    if (m_disabled || !m_profiler.GetConfig().ExecutionTraceEnabled)
+        return false;
+
+    return (thrInfo.fixTicks != state.genTicks) ||
+        (
+            thrInfo.eventChannel.HasStackSample() &&
+            (m_profiler.GetConfig().CollectionMethod ==
+                CollectionMethod::Instrumentation &&
+            !m_profiler.GetCommonTrace().IsSamplingSuspended())
+        );
+}
+
+HRESULT ContextToStackSnapshotContext(
+    const void *context, CONTEXT *winContext) noexcept;
+
+void ExecutionTrace::PrepareSample(
+    ThreadInfo &thrInfo, SamplingSharedState &state) noexcept
+{
+    if (m_profiler.GetConfig().LineTraceEnabled &&
+        thrInfo.eventChannel.GetStackSize() > 0)
+    {
+        if (state.context)
+        {
+            CONTEXT winContext;
+            if (SUCCEEDED(ContextToStackSnapshotContext(
+                state.context, &winContext)))
+            {
+                this->RestoreManagedIP(thrInfo, &winContext);
+            }
+            else
+            {
+                thrInfo.eventChannel.ChIP(0);
+            }
+        }
+        else
+        {
+            this->RestoreManagedIP(thrInfo);
+        }
+        state.isIpRestored = true;
+    }
+}
+
+void ExecutionTrace::AfterSample(
+    ThreadInfo &thrInfo, SamplingSharedState &state) noexcept
+{
+    if (state.isSampleSucceeds)
+    {
+        thrInfo.maxRestoreIpIdx = 0;
+    }
+}
+
+HRESULT ContextToStackSnapshotContext(
+    const void *context, CONTEXT *winContext) noexcept;
+
+void ExecutionTrace::UpdateCallStackPush(const FunctionInfo &funcInfo) noexcept
+{
+    SamplingSharedState state = {};
+    state.stackWillBeChanged = true;
+    m_profiler.GetCommonTrace().InterruptSampling(
+        state,
+        {},
+        [&funcInfo](ThreadInfo &thrInfo, SamplingSharedState &state)
+        {
+            EventChannel &channel = thrInfo.eventChannel;
+            if (channel.GetStackSize() > 0)
+            {
+                ++thrInfo.maxRestoreIpIdx;
+            }
+            channel.Push(funcInfo);
+            state.isIpRestored = false;
+            state.stackWillBeChanged = false;
+        }
+    );
+}
+
+void ExecutionTrace::UpdateCallStackPush(
+    const FunctionInfo &funcInfo, UINT_PTR prevIP) noexcept
+{
+    SamplingSharedState state = {};
+    state.stackWillBeChanged = true;
+    m_profiler.GetCommonTrace().InterruptSampling(
+        state,
+        [&funcInfo, prevIP](ThreadInfo &thrInfo, SamplingSharedState &state)
+        {
+            EventChannel &channel = thrInfo.eventChannel;
+            if (channel.GetStackSize() > 0)
+            {
+                channel.ChIP(prevIP);
+            }
+        },
+        [&funcInfo, prevIP](ThreadInfo &thrInfo, SamplingSharedState &state)
+        {
+            EventChannel &channel = thrInfo.eventChannel;
+            if (channel.GetStackSize() > 0)
+            {
+                if (prevIP == 0 || thrInfo.maxRestoreIpIdx > 0)
+                {
+                    ++thrInfo.maxRestoreIpIdx;
+                }
+            }
+            channel.Push(funcInfo);
+            state.isIpRestored = false;
+            state.stackWillBeChanged = false;
+        }
+    );
+}
+
+void ExecutionTrace::UpdateCallStackPop() noexcept
+{
+    SamplingSharedState state = {};
+    state.stackWillBeChanged = true;
+    m_profiler.GetCommonTrace().InterruptSampling(
+        state,
+        {},
+        [](ThreadInfo &thrInfo, SamplingSharedState &state)
+        {
+            thrInfo.eventChannel.Pop();
+            if (thrInfo.maxRestoreIpIdx > 0)
+            {
+                --thrInfo.maxRestoreIpIdx;
+            }
+            state.isIpRestored = false;
+            state.stackWillBeChanged = false;
+        }
+    );
+}
+
+UINT_PTR ExecutionTrace::FunctionIDMap(
+    FunctionID funcId,
+    BOOL *pbHookFunction) noexcept
+{
+    LOG().Trace() << "FunctionIDMap()";
+
+    try
+    {
+        FunctionInfo *pFuncInfo =
+            &m_functionStorage.lock()->Place(funcId).first;
+        pFuncInfo->executionTrace = this;
+        *pbHookFunction = true;
+        // This pointer should be stable during all lifetime of the Profiler.
+        // It is important feature guaranteed by the BaseStorage class.
+        return reinterpret_cast<UINT_PTR>(pFuncInfo);
+    }
+    catch (const std::exception &e)
+    {
+        m_profiler.HandleException(e);
+        *pbHookFunction = false;
+        return reinterpret_cast<UINT_PTR>(nullptr);
+    }
+}
+
+__forceinline void ExecutionTrace::Enter(
+    const FunctionInfo &funcInfo, UINT_PTR prevIP) noexcept
+{
+    LOG().Trace() << "EnterStub()";
+    if (m_profiler.GetConfig().LineTraceEnabled)
+    {
+        this->UpdateCallStackPush(funcInfo, prevIP);
+    }
+    else
+    {
+        this->UpdateCallStackPush(funcInfo);
+    }
+}
+
+__forceinline void ExecutionTrace::Leave(const FunctionInfo &funcInfo) noexcept
+{
+    LOG().Trace() << "LeaveStub()";
+    this->UpdateCallStackPop();
+}
+
+__forceinline void ExecutionTrace::Tailcall(
+    const FunctionInfo &funcInfo) noexcept
+{
+    LOG().Trace() << "TailcallStub()";
+    this->UpdateCallStackPop();
+}
+
+HRESULT ExecutionTrace::JITStarted(
+    FunctionID functionId) noexcept
+{
+    HRESULT hr = S_OK;
+    try
+    {
+        this->UpdateCallStackPush(*m_pJitFunctionInfo);
+        m_functionStorage.lock()->Place(functionId);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT ExecutionTrace::JITFinished(
+    FunctionID functionId,
+    std::function<JITDumpFunction> dumpFunction) noexcept
+{
+    HRESULT hr = S_OK;
+    try
+    {
+        auto storage_lock = m_functionStorage.lock();
+        FunctionInfo &funcInfo = storage_lock->Get(functionId);
+
+        {
+            auto storage_lock = m_profiler.GetCommonTrace().GetClassStorage();
+            hr = funcInfo.Initialize(m_profiler, *storage_lock);
+        }
+
+        dumpFunction(funcInfo);
+        if (!funcInfo.isNamePrinted)
+        {
+            TRACE().DumpJITFunctionName(funcInfo);
+            funcInfo.isNamePrinted = true;
+        }
+
+        this->UpdateCallStackPop();
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT ExecutionTrace::FunctionUnloadStarted(
+    FunctionID functionId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        m_functionStorage.lock()->Unlink(functionId);
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
+
+HRESULT ExecutionTrace::JITCompilationStarted(
+    FunctionID functionId,
+    BOOL fIsSafeToBlock) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    return this->JITStarted(functionId);
+}
+
+HRESULT ExecutionTrace::JITCompilationFinished(
+    FunctionID functionId,
+    HRESULT hrStatus, BOOL fIsSafeToBlock) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    return this->JITFinished(
+        functionId,
+        [this, hrStatus](const FunctionInfo &funcInfo)
+        {
+            TRACE().DumpJITCompilationFinished(funcInfo, hrStatus);
+        }
+    );
+}
+
+HRESULT ExecutionTrace::JITCachedFunctionSearchStarted(
+    FunctionID functionId,
+    BOOL *pbUseCachedFunction) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    *pbUseCachedFunction = TRUE;
+    return this->JITStarted(functionId);
+}
+
+HRESULT ExecutionTrace::JITCachedFunctionSearchFinished(
+    FunctionID functionId,
+    COR_PRF_JIT_CACHE result) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    if (result != COR_PRF_CACHED_FUNCTION_FOUND)
+    {
+        return S_OK;
+    }
+
+    return this->JITFinished(
+        functionId,
+        [this](const FunctionInfo &funcInfo)
+        {
+            TRACE().DumpJITCachedFunctionSearchFinished(funcInfo);
+        }
+    );
+}
+
+HRESULT ExecutionTrace::UnmanagedToManagedTransition(
+    FunctionID functionId,
+    COR_PRF_TRANSITION_REASON reason) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    if (reason == COR_PRF_TRANSITION_RETURN)
+    {
+        this->UpdateCallStackPop();
+    }
+
+    return S_OK;
+}
+
+HRESULT ExecutionTrace::ManagedToUnmanagedTransition(
+    FunctionID functionId,
+    COR_PRF_TRANSITION_REASON reason) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    if (reason == COR_PRF_TRANSITION_CALL)
+    {
+        this->UpdateCallStackPush(*m_pUnmanagedFunctionInfo);
+    }
+
+    return S_OK;
+}
+
+HRESULT ExecutionTrace::ExceptionUnwindFunctionLeave() noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    this->UpdateCallStackPop();
+
+    return S_OK;
+}
diff --git a/src/trace/executiontrace.h b/src/trace/executiontrace.h
new file mode 100644 (file)
index 0000000..1a69cda
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef _EXECUTION_TRACE_H_
+#define _EXECUTION_TRACE_H_
+
+#include <functional>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "basetrace.h"
+
+#include "sharedresource.h"
+#include "threadinfo.h"
+#include "functionstorage.h"
+
+// #include "shared_iterator_range.h"
+
+class ExecutionTrace final : public BaseTrace
+{
+private:
+    using SamplingSharedState = CommonTrace::SamplingSharedState;
+
+public:
+    ExecutionTrace(Profiler &profiler);
+
+    ~ExecutionTrace();
+
+    void ProcessConfig(ProfilerConfig &config);
+
+    bool IsPseudoFunction(const FunctionInfo &funcInfo) const noexcept;
+
+    void Shutdown() noexcept;
+
+    UINT_PTR GetCurrentManagedIP(
+        ThreadInfo &thrInfo, CONTEXT *winContext = nullptr) noexcept;
+
+    void RestoreManagedIP(
+        ThreadInfo &thrInfo, CONTEXT *winContext = nullptr) noexcept;
+
+    bool NeedSample(
+        ThreadInfo &thrInfo, SamplingSharedState &state) const noexcept;
+
+    void PrepareSample(
+        ThreadInfo &thrInfo, SamplingSharedState &state) noexcept;
+
+    void AfterSample(
+        ThreadInfo &thrInfo, SamplingSharedState &state) noexcept;
+
+private:
+    //
+    // Various useful instance methods.
+    //
+
+    void UpdateCallStackPush(const FunctionInfo &funcInfo) noexcept;
+
+    void UpdateCallStackPush(
+        const FunctionInfo &funcInfo, UINT_PTR prevIP) noexcept;
+
+    void UpdateCallStackPop() noexcept;
+
+public:
+    //
+    // Function Hooks and mapper function.
+    // Used by stub function so have to be public.
+    //
+
+    UINT_PTR FunctionIDMap(
+        FunctionID funcId,
+        BOOL *pbHookFunction) noexcept;
+
+    void Enter(const FunctionInfo &funcInfo, UINT_PTR prevIP) noexcept;
+
+    void Leave(const FunctionInfo &funcInfo) noexcept;
+
+    void Tailcall(const FunctionInfo &funcInfo) noexcept;
+
+private:
+    //
+    // Events helpers.
+    //
+
+    typedef void JITDumpFunction(const FunctionInfo &funcInfo);
+
+    HRESULT JITStarted(
+        FunctionID functionId) noexcept;
+
+    HRESULT JITFinished(
+        FunctionID functionId,
+        std::function<JITDumpFunction> dumpFunction) noexcept;
+public:
+    //
+    // Events.
+    //
+
+    HRESULT FunctionUnloadStarted(
+        FunctionID functionId) noexcept;
+
+    HRESULT JITCompilationStarted(
+        FunctionID functionId,
+        BOOL fIsSafeToBlock) noexcept;
+
+    HRESULT JITCompilationFinished(
+        FunctionID functionId,
+        HRESULT hrStatus, BOOL fIsSafeToBlock) noexcept;
+
+    HRESULT JITCachedFunctionSearchStarted(
+        FunctionID functionId,
+        BOOL *pbUseCachedFunction) noexcept;
+
+    HRESULT JITCachedFunctionSearchFinished(
+        FunctionID functionId,
+        COR_PRF_JIT_CACHE result) noexcept;
+
+    HRESULT UnmanagedToManagedTransition(
+        FunctionID functionId,
+        COR_PRF_TRANSITION_REASON reason) noexcept;
+
+    HRESULT ManagedToUnmanagedTransition(
+        FunctionID functionId,
+        COR_PRF_TRANSITION_REASON reason) noexcept;
+
+    HRESULT ExceptionUnwindFunctionLeave() noexcept;
+
+private:
+    SharedResource<FunctionStorage> m_functionStorage;
+
+    FunctionInfo *m_pUnmanagedFunctionInfo;
+    FunctionInfo *m_pJitFunctionInfo;
+};
+
+#endif // _EXECUTION_TRACE_H_
diff --git a/src/trace/memorytrace.cpp b/src/trace/memorytrace.cpp
new file mode 100644 (file)
index 0000000..a8e7a1a
--- /dev/null
@@ -0,0 +1,152 @@
+#include "profiler.h"
+#include "memorytrace.h"
+
+MemoryTrace::MemoryTrace(Profiler &profiler)
+    : BaseTrace(profiler)
+{
+}
+
+MemoryTrace::~MemoryTrace()
+{
+    // NOTE: we are dealing with a partially destroyed m_profiler!
+    this->Shutdown();
+}
+
+void MemoryTrace::ProcessConfig(ProfilerConfig &config)
+{
+    //
+    // Check activation condition.
+    //
+
+    if (config.MemoryTraceEnabled)
+    {
+        m_disabled = false;
+    }
+    else
+    {
+        return;
+    }
+
+    //
+    // Event Mask calculation.
+    //
+
+    HRESULT hr;
+    DWORD events;
+    hr = m_info.v1()->GetEventMask(&events);
+    if (FAILED(hr))
+    {
+        throw HresultException(
+            "MemoryTrace::ProcessConfig(): GetEventMask()", hr
+        );
+    }
+
+    // This events are common for memory tracing.
+    events = events
+        | COR_PRF_ENABLE_OBJECT_ALLOCATED
+        | COR_PRF_MONITOR_OBJECT_ALLOCATED;
+
+    //
+    // Set Event Mask.
+    //
+
+    hr = m_info.v1()->SetEventMask(events);
+    if (FAILED(hr))
+    {
+        throw HresultException(
+            "MemoryTrace::ProcessConfig(): SetEventMask()", hr);
+    }
+}
+
+void MemoryTrace::Shutdown() noexcept
+{
+    m_disabled = true;
+}
+
+bool MemoryTrace::NeedSample(
+    ThreadInfo &thrInfo, SamplingSharedState &state) const noexcept
+{
+    if (m_disabled)
+        return false;
+
+    return thrInfo.eventChannel.HasAllocSample() &&
+        (
+            (thrInfo.fixTicks != state.genTicks) ||
+            (m_profiler.GetConfig().CollectionMethod ==
+                CollectionMethod::Instrumentation) ||
+            (m_profiler.GetConfig().StackTrackingEnabled &&
+                state.stackWillBeChanged)
+        );
+}
+
+HRESULT MemoryTrace::ObjectAllocated(
+    ObjectID objectId,
+    ClassID classId) noexcept
+{
+    if (m_disabled)
+        return S_OK;
+
+    if (m_profiler.GetCommonTrace().IsSamplingSuspended())
+        return S_OK;
+
+    HRESULT hr = S_OK;
+    try
+    {
+        auto storage_lock = m_profiler.GetCommonTrace().GetClassStorage();
+        ClassInfo &classInfo = storage_lock->Place(classId).first;
+        classInfo.Initialize(m_profiler, *storage_lock);
+        if (!classInfo.isNamePrinted)
+        {
+            TRACE().DumpClassName(classInfo);
+            classInfo.isNamePrinted = true;
+        }
+
+        SIZE_T objectSize = 0;
+        if (m_info.version() >= 4)
+        {
+            hr = m_info.v4()->GetObjectSize2(objectId, &objectSize);
+        }
+        else
+        {
+            ULONG size = 0;
+            hr = m_info.v1()->GetObjectSize(objectId, &size);
+            objectSize = size;
+        }
+        if (FAILED(hr))
+        {
+            throw HresultException(
+                "MemoryTrace::ObjectAllocated(): GetObjectSize()", hr);
+        }
+
+        UINT_PTR ip = 0;
+        SamplingSharedState state = {};
+        m_profiler.GetCommonTrace().InterruptSampling(
+            state,
+            [this, &classInfo, &objectSize, &ip]
+            (ThreadInfo &thrInfo, SamplingSharedState &state)
+            {
+                EventChannel &channel = thrInfo.eventChannel;
+                if (m_profiler.GetConfig().LineTraceEnabled &&
+                    channel.GetStackSize() > 0)
+                {
+                    if (state.isIpRestored)
+                    {
+                        ip = channel.GetFrameFromTop().ip;
+                    }
+                    else
+                    {
+                        ip = m_profiler.GetExecutionTrace().
+                            GetCurrentManagedIP(thrInfo);
+                    }
+                }
+                channel.Allocation(classInfo, objectSize, ip);
+            }
+        );
+    }
+    catch (const std::exception &e)
+    {
+        hr = m_profiler.HandleException(e);
+    }
+
+    return hr;
+}
diff --git a/src/trace/memorytrace.h b/src/trace/memorytrace.h
new file mode 100644 (file)
index 0000000..2582a58
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef _MEMORY_TRACE_H_
+#define _MEMORY_TRACE_H_
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "basetrace.h"
+
+class MemoryTrace : public BaseTrace
+{
+private:
+    using SamplingSharedState = CommonTrace::SamplingSharedState;
+
+public:
+    MemoryTrace(Profiler &profiler);
+
+    ~MemoryTrace();
+
+    void ProcessConfig(ProfilerConfig &config);
+
+    void Shutdown() noexcept;
+
+    bool NeedSample(
+        ThreadInfo &thrInfo, SamplingSharedState &state) const noexcept;
+
+    // void PrepareSample(
+    //     ThreadInfo &thrInfo, SamplingSharedState &state) noexcept;
+
+    // void AfterSample(
+    //     ThreadInfo &thrInfo, SamplingSharedState &state) noexcept;
+
+private:
+
+public:
+    //
+    // Events.
+    //
+
+    HRESULT ObjectAllocated(
+        ObjectID objectId,
+        ClassID classId) noexcept;
+};
+
+#endif // _MEMORY_TRACE_H_
diff --git a/src/tracelog.cpp b/src/tracelog.cpp
new file mode 100644 (file)
index 0000000..1c45ec2
--- /dev/null
@@ -0,0 +1,371 @@
+#include <system_error>
+#include <utility>
+#include <mutex>
+
+#include <errno.h>
+
+#include <pal.h>
+
+#include "commonconfigconversions.h"
+#include "profilerconfigconversions.h"
+#include "tracelog.h"
+
+class TraceLog final : public ITraceLog
+{
+public:
+    TraceLog(StdOutStream_t)
+        : m_pStream(PAL_stdout)
+        , m_bIsOwner(false)
+    {}
+
+    TraceLog(StdErrStream_t)
+        : m_pStream(PAL_stderr)
+        , m_bIsOwner(false)
+    {}
+
+    TraceLog(FileStream_t, const std::string &filename)
+    {
+        m_pStream = PAL_fopen(filename.c_str(), "w");
+        if (m_pStream == nullptr)
+        {
+            throw std::system_error(errno, std::system_category(),
+                "can't create TraceLog object");
+        }
+        m_bIsOwner = true;
+    }
+
+    virtual ~TraceLog()
+    {
+        if (m_bIsOwner)
+            PAL_fclose(m_pStream);
+    }
+
+    virtual void DumpStartTime(
+        const SYSTEMTIME &systime) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "prf stm %04hu-%02hu-%02hu %02hu:%02hu:%02hu.%03hu\n",
+            systime.wYear, systime.wMonth, systime.wDay,
+            systime.wHour, systime.wMinute, systime.wSecond,
+            systime.wMilliseconds
+        );
+    }
+
+    virtual void DumpProfilerConfig(
+        const ProfilerConfig &config) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "prf cfg CollectionMethod %s\n",
+            convert<LPCSTR>(config.CollectionMethod)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg SamplingTimeoutMs %lu\n",
+            config.SamplingTimeoutMs
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg HighGranularityEnabled %s\n",
+            convert<LPCSTR>(config.HighGranularityEnabled)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg TracingSuspendedOnStart %s\n",
+            convert<LPCSTR>(config.TracingSuspendedOnStart)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg LineTraceEnabled %s\n",
+            convert<LPCSTR>(config.LineTraceEnabled)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg CpuTraceProcessEnabled %s\n",
+            convert<LPCSTR>(config.CpuTraceProcessEnabled)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg CpuTraceThreadEnabled %s\n",
+            convert<LPCSTR>(config.CpuTraceThreadEnabled)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg CpuTraceTimeoutMs %lu\n",
+            config.CpuTraceTimeoutMs
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg ExecutionTraceEnabled %s\n",
+            convert<LPCSTR>(config.ExecutionTraceEnabled)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg MemoryTraceEnabled %s\n",
+            convert<LPCSTR>(config.MemoryTraceEnabled)
+        );
+        PAL_fprintf(
+            m_pStream, "prf cfg StackTrackingEnabled %s\n",
+            convert<LPCSTR>(config.StackTrackingEnabled)
+        );
+    }
+
+    virtual void DumpProfilerTracingPause(
+        DWORD       ticks) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(m_pStream, "prf tps %d\n", ticks);
+    }
+
+    virtual void DumpProfilerTracingResume(
+        DWORD       ticks) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(m_pStream, "prf trs %d\n", ticks);
+    }
+
+    virtual void DumpProcessTimes(
+        DWORD       ticksFromStart,
+        DWORD64     userTime) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(m_pStream, "prc cpu %d %I64u\n",
+            ticksFromStart, userTime);
+    }
+
+    virtual void DumpAppDomainCreationFinished(
+        AppDomainID appDomainId,
+        LPCWCH      appDomainName,
+        ProcessID   processId,
+        HRESULT     hrStatus) override
+    {
+        if (appDomainName == nullptr)
+            appDomainName = W("UNKNOWN");
+
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "apd crf 0x%p 0x%p 0x%08x \"%S\"\n",
+            appDomainId, processId, hrStatus, appDomainName
+        );
+    }
+
+    virtual void DumpAssemblyLoadFinished(
+        AssemblyID  assemblyId,
+        LPCWCH      assemblyName,
+        AppDomainID appDomainId,
+        ModuleID    moduleId,
+        HRESULT     hrStatus) override
+    {
+        if (assemblyName == nullptr)
+            assemblyName = W("UNKNOWN");
+
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "asm ldf 0x%p 0x%p 0x%p 0x%08x \"%S\"\n",
+            assemblyId, appDomainId, moduleId, hrStatus, assemblyName
+        );
+    }
+
+    virtual void DumpModuleLoadFinished(
+        ModuleID    moduleId,
+        LPCBYTE     baseLoadAddress,
+        LPCWCH      moduleName,
+        AssemblyID  assemblyId,
+        HRESULT     hrStatus) override
+    {
+        if (moduleName == nullptr)
+            moduleName = W("UNKNOWN");
+
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "mod ldf 0x%p 0x%p 0x%p 0x%08x \"%S\"\n",
+            moduleId, baseLoadAddress, assemblyId, hrStatus, moduleName
+        );
+    }
+
+    virtual void DumpModuleAttachedToAssembly(
+        ModuleID    moduleId,
+        AssemblyID  assemblyId) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "mod ata 0x%p 0x%p\n", moduleId, assemblyId
+        );
+    }
+
+    virtual void DumpClassLoadFinished(
+        const ClassInfo &info,
+        HRESULT     hrStatus) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "cls ldf 0x%p 0x%08x 0x%p 0x%08x 0x%08x\n",
+            info.id, info.internalId.id, info.moduleId, info.classToken,
+            hrStatus
+        );
+    }
+
+    virtual void DumpClassName(
+        const ClassInfo &info) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "cls nam 0x%08x \"%S\"\n",
+            info.internalId.id, info.fullName.c_str()
+        );
+    }
+
+    virtual void DumpJITCompilationFinished(
+        const FunctionInfo &info,
+        HRESULT     hrStatus) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "fun cmf 0x%p 0x%08x 0x%p 0x%p 0x%08x 0x%08x",
+            info.id, info.internalId.id, info.classId, info.moduleId,
+            info.funcToken, hrStatus
+        );
+        DumpFunctionInfo(info);
+        PAL_fprintf(m_pStream, "\n");
+    }
+
+    virtual void DumpJITCachedFunctionSearchFinished(
+        const FunctionInfo &info) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "fun csf 0x%p 0x%08x 0x%p 0x%p 0x%08x",
+            info.id, info.internalId.id, info.classId, info.moduleId,
+            info.funcToken
+        );
+        DumpFunctionInfo(info);
+        PAL_fprintf(m_pStream, "\n");
+    }
+
+    virtual void DumpJITFunctionName(
+        const FunctionInfo &info) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "fun nam 0x%08x \"%S\" \"%S\" \"%S\"\n",
+            info.internalId.id, info.fullName.c_str(),
+            info.returnType.c_str(), info.signature.c_str()
+        );
+    }
+
+    virtual void DumpThreadCreated(
+        ThreadID    threadId,
+        InternalID  threadIid) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(m_pStream, "thr crt 0x%p 0x%08x\n", threadId, threadIid.id);
+    }
+
+    virtual void DumpThreadDestroyed(
+        InternalID  threadIid) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(m_pStream, "thr dst 0x%08x\n", threadIid.id);
+    }
+
+    virtual void DumpThreadAssignedToOSThread(
+        InternalID  managedThreadIid,
+        DWORD       osThreadId) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(
+            m_pStream, "thr aos 0x%08x %d\n", managedThreadIid.id, osThreadId
+        );
+    }
+
+    virtual void DumpThreadTimes(
+        InternalID  threadIid,
+        DWORD       ticksFromStart,
+        DWORD64     userTime) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+        PAL_fprintf(m_pStream, "thr cpu 0x%08x %d %I64u\n",
+            threadIid.id, ticksFromStart, userTime);
+    }
+
+    virtual void DumpSample(
+        InternalID           threadIid,
+        const EventSummary   &summary) override
+    {
+        std::lock_guard<std::mutex> streamLock(m_mStream);
+
+        if (summary.HasStackSample())
+        {
+            PAL_fprintf(
+                m_pStream, "sam str 0x%08x %d %lu",
+                threadIid.id, summary.ticks, summary.count
+            );
+            PAL_fprintf(m_pStream, " %d:%d",
+                summary.matchPrefixSize, summary.stackSize);
+            if (summary.ipIsChanged)
+            {
+                PAL_fprintf(m_pStream, summary.ip != 0 ? ":%p" : ":?", summary.ip);
+            }
+            for (const auto &frame : summary.newFrames)
+            {
+                PAL_fprintf(m_pStream, frame.ip != 0 ? " 0x%x:%p" : " 0x%x",
+                    frame.pFuncInfo->internalId.id, frame.ip);
+            }
+            PAL_fprintf(m_pStream, "\n");
+        }
+
+        if (summary.HasAllocSample())
+        {
+            PAL_fprintf(
+                m_pStream, "sam mem 0x%08x %d", threadIid.id, summary.ticks
+            );
+            for (const auto &classIdIpAllocInfo : summary.allocTable)
+            {
+                for (const auto &IpAllocInfo : classIdIpAllocInfo.second)
+                {
+                    PAL_fprintf(
+                        m_pStream,
+                        IpAllocInfo.first != 0 ?
+                            " 0x%x:%Iu:%Iu:%p" : " 0x%x:%Iu:%Iu",
+                        classIdIpAllocInfo.first,
+                        IpAllocInfo.second.allocCount,
+                        IpAllocInfo.second.memSize,
+                        IpAllocInfo.first
+                    );
+                }
+            }
+            PAL_fprintf(m_pStream, "\n");
+        }
+    }
+
+private:
+    PAL_FILE  *m_pStream;
+    std::mutex m_mStream;
+    bool       m_bIsOwner;
+
+    void DumpFunctionInfo(const FunctionInfo &info)
+    {
+        for (const auto &ci : info.codeInfo)
+        {
+            PAL_fprintf(m_pStream, " 0x%p:0x%x",
+                ci.startAddress, ci.size);
+        }
+
+        for (const auto &m : info.ILToNativeMapping)
+        {
+            PAL_fprintf(m_pStream, " 0x%x:0x%x:0x%x",
+                m.ilOffset, m.nativeStartOffset, m.nativeEndOffset);
+        }
+    }
+};
+
+// static
+ITraceLog *ITraceLog::Create(StdOutStream_t StdOutStream)
+{
+    return new TraceLog(StdOutStream);
+}
+
+// static
+ITraceLog *ITraceLog::Create(StdErrStream_t StdErrStream)
+{
+    return new TraceLog(StdErrStream);
+}
+
+// static
+ITraceLog *ITraceLog::Create(
+    FileStream_t FileStream, const std::string &filename)
+{
+    return new TraceLog(FileStream, filename);
+}
diff --git a/src/tracelog.h b/src/tracelog.h
new file mode 100644 (file)
index 0000000..079882c
--- /dev/null
@@ -0,0 +1,125 @@
+#ifndef _TRACE_LOG_H_
+#define _TRACE_LOG_H_
+
+#include <windows.h>
+
+#include <cor.h>
+#include <corhdr.h>
+#include <corprof.h>
+
+#include "profilerconfig.h"
+#include "functioninfo.h"
+#include "classinfo.h"
+#include "eventchannel.h"
+
+class ITraceLog
+{
+protected:
+    class StdOutStream_t {};
+
+    class StdErrStream_t {};
+
+    class FileStream_t {};
+
+public:
+    static StdOutStream_t StdOutStream;
+
+    static StdErrStream_t StdErrStream;
+
+    static FileStream_t   FileStream;
+
+    ITraceLog() = default;
+
+    ITraceLog(const ITraceLog&) = delete;
+
+    ITraceLog &operator=(const ITraceLog&) = delete;
+
+    virtual ~ITraceLog() = default;
+
+    static ITraceLog *Create(StdOutStream_t);
+
+    static ITraceLog *Create(StdErrStream_t);
+
+    static ITraceLog *Create(FileStream_t, const std::string &filename);
+
+    // TODO: different methods to dump information.
+
+    virtual void DumpStartTime(
+        const SYSTEMTIME &systime) = 0;
+
+    virtual void DumpProfilerConfig(
+        const ProfilerConfig &config) = 0;
+
+    virtual void DumpProfilerTracingPause(
+        DWORD       ticks) = 0;
+
+    virtual void DumpProfilerTracingResume(
+        DWORD       ticks) = 0;
+
+    virtual void DumpProcessTimes(
+        DWORD       ticksFromStart,
+        DWORD64     userTime) = 0;
+
+    virtual void DumpAppDomainCreationFinished(
+        AppDomainID appDomainId,
+        LPCWCH      appDomainName,
+        ProcessID   processId,
+        HRESULT     hrStatus) = 0;
+
+    virtual void DumpAssemblyLoadFinished(
+        AssemblyID  assemblyId,
+        LPCWCH      assemblyName,
+        AppDomainID appDomainId,
+        ModuleID    moduleId,
+        HRESULT     hrStatus) = 0;
+
+    virtual void DumpModuleLoadFinished(
+        ModuleID    moduleId,
+        LPCBYTE     baseLoadAddress,
+        LPCWCH      moduleName,
+        AssemblyID  assemblyId,
+        HRESULT     hrStatus) = 0;
+
+    virtual void DumpModuleAttachedToAssembly(
+        ModuleID    moduleId,
+        AssemblyID  assemblyId) = 0;
+
+    virtual void DumpClassLoadFinished(
+        const ClassInfo &info,
+        HRESULT     hrStatus) = 0;
+
+    virtual void DumpClassName(
+        const ClassInfo &info) = 0;
+
+    virtual void DumpJITCompilationFinished(
+        const FunctionInfo &info,
+        HRESULT     hrStatus) = 0;
+
+    virtual void DumpJITCachedFunctionSearchFinished(
+        const FunctionInfo &info) = 0;
+
+    virtual void DumpJITFunctionName(
+        const FunctionInfo &info) = 0;
+
+    virtual void DumpThreadCreated(
+        ThreadID    threadId,
+        InternalID  threadIid) = 0;
+
+    virtual void DumpThreadDestroyed(
+        InternalID  threadIid) = 0;
+
+    virtual void DumpThreadAssignedToOSThread(
+        InternalID  managedThreadIid,
+        DWORD       osThreadId) = 0;
+
+    virtual void DumpThreadTimes(
+        InternalID  threadIid,
+        DWORD       ticksFromStart,
+        DWORD64     userTime) = 0;
+
+    virtual void DumpSample(
+        InternalID           threadIid,
+        const EventSummary   &summary) = 0;
+};
+
+#endif // _TRACE_LOG_H_