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:
parent
86f2ff0c71
commit
c813ea655d
@ -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")
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user