From c813ea655dbb92713739391dc1b52f40d7c80ec9 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Thu, 28 Sep 2017 21:04:28 +0300 Subject: [PATCH] #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 :( --- easy_profiler_core/CMakeLists.txt | 88 ++++++++++++--- easy_profiler_core/profile_manager.cpp | 87 ++++++++++++--- easy_profiler_core/profile_manager.h | 145 +++++++++++++++---------- 3 files changed, 232 insertions(+), 88 deletions(-) diff --git a/easy_profiler_core/CMakeLists.txt b/easy_profiler_core/CMakeLists.txt index a4a42f4..54f1475 100644 --- a/easy_profiler_core/CMakeLists.txt +++ b/easy_profiler_core/CMakeLists.txt @@ -2,20 +2,35 @@ 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)") set(EASY_OPTION_PROFILE_SELF_BLOCKS_ON OFF CACHE BOOL "Storage expand default status (profiler::ON or profiler::OFF)") set(EASY_OPTION_LOG OFF CACHE BOOL "Print errors to stderr") 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.") +set(BUILD_SHARED_LIBS ON CACHE BOOL "Build easy_profiler as shared library.") if (WIN32) - 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) -set(BUILD_WITH_CHRONO_STEADY_CLOCK OFF CACHE BOOL "Use std::chrono::steady_clock as a timer" ) + 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") +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 $ $ # /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 $<$:-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") diff --git a/easy_profiler_core/profile_manager.cpp b/easy_profiler_core/profile_manager.cpp index ebcd55c..158636d 100644 --- a/easy_profiler_core/profile_manager.cpp +++ b/easy_profiler_core/profile_manager.cpp @@ -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(t.blocks.closedList.size()) + static_cast(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(); diff --git a/easy_profiler_core/profile_manager.h b/easy_profiler_core/profile_manager.h index 96d1dad..078cea4 100644 --- a/easy_profiler_core/profile_manager.h +++ b/easy_profiler_core/profile_manager.h @@ -3,40 +3,40 @@ Lightweight profiler library for c++ Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin Licensed under either of - * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) at your option. The MIT License - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished - to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. The Apache License, Version 2.0 (the "License"); - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. **/ @@ -368,29 +368,28 @@ EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val) template 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_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(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);