0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-27 08:41:02 +08:00

#53 [Core] Fixed problem with removing ThreadStorage for alive threads. But Linux would have another problem: potential memory leak or potential crash at your choise :)) because these wonderful pthreads does not allow to check thread state :(

This commit is contained in:
Victor Zarubkin 2017-09-28 21:04:28 +03:00
parent 86f2ff0c71
commit c813ea655d
3 changed files with 232 additions and 88 deletions

View File

@ -2,6 +2,11 @@ message(STATUS "")
message(STATUS "EASY_PROFILER.Core version = ${EASY_PRODUCT_VERSION_STRING}")
message(STATUS "")
###########################################################
# EasyProfiler options:
set(EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT "Enable new threads registration when collecting context switch events")
set(EASY_DEFAULT_PORT 28077 CACHE STRING "Default listening port")
set(EASY_OPTION_LISTEN OFF CACHE BOOL "Enable automatic startListen on startup")
set(EASY_OPTION_PROFILE_SELF OFF CACHE BOOL "Enable self profiling (measure time for internal storage expand)")
@ -10,12 +15,22 @@ set(EASY_OPTION_LOG OFF CACHE BOOL "Print errors to stde
set(EASY_OPTION_PREDEFINED_COLORS ON CACHE BOOL "Use predefined set of colors (see profiler_colors.h). If you want to use your own colors palette you can turn this option OFF")
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build easy_profiler as shared library.")
if (WIN32)
set(EASY_OPTION_IMPLICIT_THREAD_REGISTRATION ON CACHE BOOL ${EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT})
set(EASY_OPTION_EVENT_TRACING ON CACHE BOOL "Enable event tracing by default")
set(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING ON CACHE BOOL "Set low priority for event tracing thread")
endif (WIN32)
else ()
set(EASY_OPTION_IMPLICIT_THREAD_REGISTRATION OFF CACHE BOOL ${EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT})
set(EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS OFF CACHE BOOL "Enable easy_profiler to remove empty unguarded threads. This fixes potential memory leak on Unix systems, but may lead to an application crash! This is used if your compiler does not support C++11 thread_local feature or EASY_OPTION_IMPLICIT_THREAD_REGISTER is ON")
endif ()
set(BUILD_WITH_CHRONO_STEADY_CLOCK OFF CACHE BOOL "Use std::chrono::steady_clock as a timer" )
set(BUILD_WITH_CHRONO_HIGH_RESOLUTION_CLOCK OFF CACHE BOOL "Use std::chrono::high_resolution_clock as a timer")
# End EasyProfiler options.
###########################################################
#####################################################################
# Print EasyProfiler options status:
message(STATUS "-------- EASY_PROFILER OPTIONS: --------")
if (BUILD_WITH_CHRONO_STEADY_CLOCK)
message(STATUS " Use std::chrono::steady_clock as a timer")
@ -33,20 +48,39 @@ message(STATUS " Default listening port = ${EASY_DEFAULT_PORT}")
message(STATUS " Auto-start listening = ${EASY_OPTION_LISTEN}")
message(STATUS " Profile self = ${EASY_OPTION_PROFILE_SELF}")
message(STATUS " Profile self blocks initial status = ${EASY_OPTION_PROFILE_SELF_BLOCKS_ON}")
message(STATUS " Implicit thread registration = ${EASY_OPTION_IMPLICIT_THREAD_REGISTRATION}")
if (EASY_OPTION_IMPLICIT_THREAD_REGISTRATION)
message(STATUS " WARNING! Implicit thread registration for Unix systems can lead to memory leak")
message(STATUS " because there is no possibility to check if thread is alive and remove dead threads.")
endif ()
if (WIN32)
message(STATUS " Event tracing = ${EASY_OPTION_EVENT_TRACING}")
if (EASY_OPTION_LOW_PRIORITY_EVENT_TRACING)
message(STATUS " Event tracing has low priority = Yes")
message(STATUS " Event tracing has low priority = ${EASY_OPTION_LOW_PRIORITY_EVENT_TRACING}")
else ()
message(STATUS " WARNING! Implicit thread registration for Unix systems can lead to memory leak")
message(STATUS " because there is no possibility to check if thread is alive and remove dead threads.")
message(STATUS " Removing empty unguarded threads = ${EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS}")
if (EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS)
message(STATUS " WARNING! Removing empty unguarded threads may lead to an application crash!")
message(STATUS " But fixes potential memory leak on Unix systems.")
else ()
message(STATUS " Event tracing has low priority = No")
endif (EASY_OPTION_LOW_PRIORITY_EVENT_TRACING)
endif (WIN32)
message(STATUS " WARNING! There is a possibility of memory leak without removing empty unguarded threads.")
endif ()
message(STATUS " If implicit thread registration is OFF and your compiler supports C++11 thread_local feature")
message(STATUS " then You can ignore this warning. But beware if it's not so!")
endif ()
message(STATUS " Log messages = ${EASY_OPTION_LOG}")
message(STATUS " Use EasyProfiler colors palette = ${EASY_OPTION_PREDEFINED_COLORS}")
message(STATUS " Shared library: ${BUILD_SHARED_LIBS}")
message(STATUS "------ END EASY_PROFILER OPTIONS -------")
message(STATUS "")
# End printing EasyProfiler options status.
#####################################################################
#################################################
# Add source files:
set(CPP_FILES
block.cpp
profile_manager.cpp
@ -83,6 +117,11 @@ set(SOURCES
)
add_library(easy_profiler ${SOURCES} resources.rc)
# End adding source files.
#################################################
target_include_directories(easy_profiler PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include> # <prefix>/include
@ -102,18 +141,31 @@ target_compile_definitions(easy_profiler PUBLIC
-DBUILD_WITH_EASY_PROFILER=1
)
#####################################################################
# Add EasyProfiler options definitions:
easy_define_target_option(easy_profiler BUILD_WITH_CHRONO_STEADY_CLOCK EASY_CHRONO_STEADY_CLOCK)
easy_define_target_option(easy_profiler BUILD_WITH_CHRONO_HIGH_RESOLUTION_CLOCK EASY_CHRONO_HIGHRES_CLOCK)
easy_define_target_option(easy_profiler EASY_OPTION_LISTEN EASY_OPTION_START_LISTEN_ON_STARTUP)
easy_define_target_option(easy_profiler EASY_OPTION_PROFILE_SELF EASY_OPTION_MEASURE_STORAGE_EXPAND)
easy_define_target_option(easy_profiler EASY_OPTION_PROFILE_SELF_BLOCKS_ON EASY_OPTION_STORAGE_EXPAND_BLOCKS_ON)
easy_define_target_option(easy_profiler EASY_OPTION_IMPLICIT_THREAD_REGISTRATION EASY_OPTION_IMPLICIT_THREAD_REGISTRATION)
if (WIN32)
easy_define_target_option(easy_profiler EASY_OPTION_EVENT_TRACING EASY_OPTION_EVENT_TRACING_ENABLED)
easy_define_target_option(easy_profiler EASY_OPTION_LOW_PRIORITY_EVENT_TRACING EASY_OPTION_LOW_PRIORITY_EVENT_TRACING)
else ()
easy_define_target_option(easy_profiler EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS)
endif ()
easy_define_target_option(easy_profiler EASY_OPTION_LOG EASY_OPTION_LOG_ENABLED)
easy_define_target_option(easy_profiler EASY_OPTION_PREDEFINED_COLORS EASY_OPTION_BUILTIN_COLORS)
# End adding EasyProfiler options definitions.
#####################################################################
###############################################################################
# Add platform specific compile options:
if (UNIX)
target_compile_options(easy_profiler PRIVATE -Wall -Wno-long-long -Wno-reorder -Wno-braced-scalar-init -pedantic)
target_link_libraries(easy_profiler pthread)
@ -128,29 +180,29 @@ endif ()
if (MSVC)
target_compile_options(easy_profiler PRIVATE /WX)
endif()
endif ()
if (APPLE)
target_compile_options(easy_profiler PUBLIC -std=gnu++11)
else ()
if (CMAKE_VERSION VERSION_LESS "3.1")
if (NOT MSVC)
target_compile_options(easy_profiler PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-std=gnu++11>)
endif ()
else()
else ()
if (NOT MSVC)
target_compile_options(easy_profiler PUBLIC -std=gnu++11)
endif ()
set_target_properties(easy_profiler PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON)
endif ()
endif ()
# End adding platform specific compile options.
###############################################################################
####
# Installation
#########################################################################################
# Installation:
set(config_install_dir "lib/cmake/${PROJECT_NAME}")
set(include_install_dir "include")

View File

@ -225,6 +225,10 @@ EASY_THREAD_LOCAL static uint32_t THIS_THREAD_N_FRAMES = 0;
EASY_THREAD_LOCAL static bool THIS_THREAD_FRAME_T_RESET_MAX = false;
EASY_THREAD_LOCAL static bool THIS_THREAD_FRAME_T_RESET_AVG = false;
#ifdef EASY_THREAD_LOCAL_CPP11
thread_local static profiler::ThreadGuard THIS_THREAD_GUARD; // thread guard for monitoring thread life time
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef BUILD_WITH_EASY_PROFILER
@ -686,7 +690,7 @@ void ThreadStorage::storeBlock(const profiler::Block& block)
if (expanded) beginTime = getCurrentTime();
#endif
char* data = (char*)blocks.closedList.allocate(size);
void* data = blocks.closedList.allocate(size);
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
if (expanded) endTime = getCurrentTime();
@ -898,7 +902,7 @@ bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, cons
}
else if (THIS_THREAD == nullptr)
{
THIS_THREAD = &threadStorage(getCurrentThreadId());
registerThread();
}
#if EASY_ENABLE_BLOCK_STATUS != 0
@ -928,7 +932,7 @@ bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, cons
}
else if (THIS_THREAD == nullptr)
{
THIS_THREAD = &threadStorage(getCurrentThreadId());
registerThread();
}
#if EASY_ENABLE_BLOCK_STATUS != 0
@ -951,7 +955,7 @@ void ProfileManager::storeBlockForce(const profiler::BaseBlockDescriptor* _desc,
return;
if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId());
registerThread();
#if EASY_ENABLE_BLOCK_STATUS != 0
if (!THIS_THREAD->allowChildren && !(_desc->m_status & FORCE_ON_FLAG))
@ -972,7 +976,7 @@ void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc
return;
if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId());
registerThread();
#if EASY_ENABLE_BLOCK_STATUS != 0
if (!THIS_THREAD->allowChildren && !(_desc->m_status & FORCE_ON_FLAG))
@ -997,7 +1001,7 @@ void ProfileManager::storeBlockForce2(ThreadStorage& _registeredThread, const pr
void ProfileManager::beginBlock(Block& _block)
{
if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId());
registerThread();
if (++THIS_THREAD_STACK_SIZE > 1)
{
@ -1075,7 +1079,7 @@ void ProfileManager::beginBlock(Block& _block)
void ProfileManager::beginNonScopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
{
if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId());
registerThread();
NonscopedBlock& b = THIS_THREAD->nonscopedBlocks.push(_desc, _runtimeName, false);
beginBlock(b);
@ -1149,11 +1153,25 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processi
{
ThreadStorage* ts = nullptr;
if (_process_id == m_processId)
{
// Implicit thread registration.
// If thread owned by current process then create new ThreadStorage if there is no one
#if defined(EASY_OPTION_IMPLICIT_THREAD_REGISTRATION) && EASY_OPTION_IMPLICIT_THREAD_REGISTRATION != 0
ts = _lockSpin ? &threadStorage(_thread_id) : &_threadStorage(_thread_id);
# ifndef _WIN32
# if defined(EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS) && EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS != 0
# pragma message "Warning: Implicit thread registration may cause application crash because there is no possibility to check thread state (dead or alive) in Unix systems."
# else
# pragma message "Warning: Implicit thread registration may lead to memory leak because there is no possibility to check thread state (dead or alive) in Unix systems."
# endif
# endif
#endif
}
else
{
// If thread owned by another process OR _process_id IS UNKNOWN then do not create ThreadStorage for this
ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
}
if (ts == nullptr || ts->sync.openedList.empty())
return;
@ -1327,8 +1345,7 @@ char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
return 0;
#ifdef _WIN32
// Check thread for Windows
// Check thread state for Windows
DWORD exitCode = 0;
auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)_registeredThread.id);
@ -1345,13 +1362,20 @@ char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
CloseHandle(hThread);
return 0;
#else
// Check thread state for Linux and MacOS/iOS
return 0;//pthread_kill(_registeredThread.pthread_id, 0) != 0;
// This would drop the application if pthread already died
//return pthread_kill(_registeredThread.pthread_id, 0) != 0 ? 1 : 0;
// There is no function to check external pthread state in Linux! :((
#ifndef EASY_THREAD_LOCAL_CPP11
#pragma message "Warning: Your compiler does not support thread_local C++11 feature. Please use EASY_THREAD_SCOPE as much as possible. Otherwise, there is a possibility of memory leak if there are a lot of rapidly created and destroyed threads."
#endif
return 0;
#endif
}
//////////////////////////////////////////////////////////////////////////
@ -1466,10 +1490,19 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bo
{
auto& t = it->second;
uint32_t num = static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
const char expired = ProfileManager::checkThreadExpired(t);
const char expired = checkThreadExpired(t);
if (num == 0 && (expired != 0 || !t.guarded)) {
// Remove thread if it contains no profiled information and has been finished or is not guarded.
#if !defined(_WIN32) && defined(EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS) && EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS != 0
#pragma message "Warning: Removing !guarded thread may cause an application crash, but fixes potential memory leak on Unix systems."
if (num == 0 && (expired != 0
|| !t.guarded)) // Removing !guarded thread may cause an application crash if a thread would start to write blocks after ThreadStorage remove.
// TODO: Find solution to check thread state on Unix systems or to nullify THIS_THREAD pointer for removed ThreadStorage
#else
if (num == 0 && expired != 0)
#endif
{
// Remove thread if it contains no profiled information and has been finished (or is not guarded --deprecated).
profiler::thread_id_t id = it->first;
if (!mainThreadExpired && m_mainThreadId.compare_exchange_weak(id, 0, std::memory_order_release, std::memory_order_acquire))
mainThreadExpired = true;
@ -1477,7 +1510,8 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bo
continue;
}
if (expired == 1) {
if (expired == 1)
{
EASY_FORCE_EVENT3(t, endtime, "ThreadExpired", EASY_COLOR_THREAD_END);
++num;
}
@ -1605,6 +1639,16 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
return blocksNumber;
}
void ProfileManager::registerThread()
{
THIS_THREAD = &threadStorage(getCurrentThreadId());
#ifdef EASY_THREAD_LOCAL_CPP11
THIS_THREAD->guarded = true;
THIS_THREAD_GUARD.m_id = THIS_THREAD->id;
#endif
}
const char* ProfileManager::registerThread(const char* name, ThreadGuard& threadGuard)
{
if (THIS_THREAD == nullptr)
@ -1621,9 +1665,17 @@ const char* ProfileManager::registerThread(const char* name, ThreadGuard& thread
profiler::thread_id_t id = 0;
THIS_THREAD_IS_MAIN = m_mainThreadId.compare_exchange_weak(id, THIS_THREAD->id, std::memory_order_release, std::memory_order_acquire);
}
#ifdef EASY_THREAD_LOCAL_CPP11
THIS_THREAD_GUARD.m_id = THIS_THREAD->id;
}
threadGuard; // this is just to prevent from warning about unused variable
#else
}
threadGuard.m_id = THIS_THREAD->id;
#endif
return THIS_THREAD->name.c_str();
}
@ -1643,6 +1695,11 @@ const char* ProfileManager::registerThread(const char* name)
profiler::thread_id_t id = 0;
THIS_THREAD_IS_MAIN = m_mainThreadId.compare_exchange_weak(id, THIS_THREAD->id, std::memory_order_release, std::memory_order_acquire);
}
#ifdef EASY_THREAD_LOCAL_CPP11
THIS_THREAD->guarded = true;
THIS_THREAD_GUARD.m_id = THIS_THREAD->id;
#endif
}
return THIS_THREAD->name.c_str();

View File

@ -368,29 +368,28 @@ EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val)
template <uint16_t N>
class chunk_allocator
{
struct chunk { EASY_ALIGNED(int8_t, data[N], EASY_ALIGNMENT_SIZE); chunk* prev = nullptr; };
struct chunk { EASY_ALIGNED(char, data[N], EASY_ALIGNMENT_SIZE); chunk* prev = nullptr; };
struct chunk_list
{
chunk* last = nullptr;
chunk* last;
chunk_list() : last(nullptr)
{
static_assert(sizeof(char) == 1, "easy_profiler logic error: sizeof(char) != 1 for this platform! Please, contact easy_profiler authors to resolve your problem.");
emplace_back();
}
~chunk_list()
{
clear();
do free_last(); while (last != nullptr);
}
void clear()
void clear_all_except_last()
{
do {
auto p = last;
last = last->prev;
EASY_FREE(p);
} while (last != nullptr);
}
chunk& back()
{
return *last;
while (last->prev != nullptr)
free_last();
zero_last_chunk_size();
}
void emplace_back()
@ -398,12 +397,7 @@ class chunk_allocator
auto prev = last;
last = ::new (EASY_MALLOC(sizeof(chunk), EASY_ALIGNMENT_SIZE)) chunk();
last->prev = prev;
// Although there is no need for unaligned access stuff b/c a new chunk will
// usually be at least 8 byte aligned (and we only need 2 byte alignment),
// this is the only way I have been able to get rid of the GCC strict-aliasing warning
// without using std::memset. It's an extra line, but is just as fast as *(uint16_t*)last->data = 0;
char* const data = (char*)&last->data;
*(uint16_t*)data = 0;
zero_last_chunk_size();
}
/** Invert current chunks list to enable to iterate over chunks list in direct order.
@ -423,12 +417,33 @@ class chunk_allocator
last->prev = next;
}
private:
chunk_list(const chunk_list&) = delete;
chunk_list(chunk_list&&) = delete;
void free_last()
{
auto p = last;
last = last->prev;
EASY_FREE(p);
}
void zero_last_chunk_size()
{
// Although there is no need for unaligned access stuff b/c a new chunk will
// usually be at least 8 byte aligned (and we only need 2 byte alignment),
// this is the only way I have been able to get rid of the GCC strict-aliasing warning
// without using std::memset. It's an extra line, but is just as fast as *(uint16_t*)last->data = 0;
char* const data = last->data;
*(uint16_t*)data = (uint16_t)0;
}
};
//typedef std::list<chunk> chunk_list;
// Used in serialize(): workaround for no constexpr support in MSVC 2013.
static const int_fast32_t MAX_CHUNK_OFFSET = N-sizeof(uint16_t);
static const int_fast32_t MAX_CHUNK_OFFSET = N - sizeof(uint16_t);
static const uint16_t N_MINUS_ONE = N - 1;
chunk_list m_chunks; ///< List of chunks.
uint32_t m_size; ///< Number of elements stored(# of times allocate() has been called.)
@ -438,7 +453,6 @@ public:
chunk_allocator() : m_size(0), m_chunkOffset(0)
{
m_chunks.emplace_back();
}
/** Allocate n bytes.
@ -454,7 +468,7 @@ public:
{
// Temp to avoid extra load due to this* aliasing.
uint16_t chunkOffset = m_chunkOffset;
char* data = (char*)m_chunks.back().data + chunkOffset;
char* data = m_chunks.last->data + chunkOffset;
chunkOffset += n + sizeof(uint16_t);
m_chunkOffset = chunkOffset;
@ -463,7 +477,7 @@ public:
// If there is enough space for at least another payload size,
// set it to zero.
if (chunkOffset < N-1)
if (chunkOffset < N_MINUS_ONE)
unaligned_zero16(data + n);
return data;
@ -472,7 +486,7 @@ public:
m_chunkOffset = n + sizeof(uint16_t);
m_chunks.emplace_back();
char* data = (char*)&m_chunks.back().data[0];
char* data = m_chunks.last->data;
unaligned_store16(data, n);
data += sizeof(uint16_t);
@ -503,8 +517,7 @@ public:
{
m_size = 0;
m_chunkOffset = 0;
m_chunks.clear();
m_chunks.emplace_back();
m_chunks.clear_all_except_last(); // There is always at least one chunk
}
/** Serialize data to stream.
@ -529,10 +542,10 @@ public:
chunk* current = m_chunks.last;
do {
const char* data = (char*)current->data;
const char* data = current->data;
int_fast32_t chunkOffset = 0; // signed int so overflow is not checked.
uint16_t payloadSize = unaligned_load16<uint16_t>(data);
while ((chunkOffset < MAX_CHUNK_OFFSET) & (payloadSize != 0)) {
while (chunkOffset < MAX_CHUNK_OFFSET && payloadSize != 0) {
const uint16_t chunkSize = sizeof(uint16_t) + payloadSize;
_outputStream.write(data, chunkSize);
data += chunkSize;
@ -546,6 +559,11 @@ public:
clear();
}
private:
chunk_allocator(const chunk_allocator&) = delete;
chunk_allocator(chunk_allocator&&) = delete;
}; // END of class chunk_allocator.
//////////////////////////////////////////////////////////////////////////
@ -652,6 +670,11 @@ public:
m_overflow.pop_back();
}
private:
StackBuffer(const StackBuffer&) = delete;
StackBuffer(StackBuffer&&) = delete;
}; // END of class StackBuffer.
//////////////////////////////////////////////////////////////////////////
@ -670,6 +693,11 @@ struct BlocksList
usedMemorySize = 0;
}
private:
BlocksList(const BlocksList&) = delete;
BlocksList(BlocksList&&) = delete;
}; // END of struct BlocksList.
//////////////////////////////////////////////////////////////////////////
@ -715,6 +743,11 @@ struct ThreadStorage
ThreadStorage();
private:
ThreadStorage(const ThreadStorage&) = delete;
ThreadStorage(ThreadStorage&&) = delete;
}; // END of struct ThreadStorage.
//////////////////////////////////////////////////////////////////////////
@ -822,13 +855,15 @@ public:
private:
void registerThread();
void beginFrame();
void endFrame();
void enableEventTracer();
void disableEventTracer();
char checkThreadExpired(ThreadStorage& _registeredThread);
static char checkThreadExpired(ThreadStorage& _registeredThread);
void storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp);
void storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp);