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,20 +2,35 @@ message(STATUS "")
message(STATUS "EASY_PROFILER.Core version = ${EASY_PRODUCT_VERSION_STRING}") message(STATUS "EASY_PROFILER.Core version = ${EASY_PRODUCT_VERSION_STRING}")
message(STATUS "") 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_DEFAULT_PORT 28077 CACHE STRING "Default listening port")
set(EASY_OPTION_LISTEN OFF CACHE BOOL "Enable automatic startListen on startup") 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 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_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_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(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) if (WIN32)
set(EASY_OPTION_EVENT_TRACING ON CACHE BOOL "Enable event tracing by default") set(EASY_OPTION_IMPLICIT_THREAD_REGISTRATION ON CACHE BOOL ${EASY_OPTION_IMPLICIT_THREAD_REGISTER_TEXT})
set(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING ON CACHE BOOL "Set low priority for event tracing thread") set(EASY_OPTION_EVENT_TRACING ON CACHE BOOL "Enable event tracing by default")
endif (WIN32) set(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING ON CACHE BOOL "Set low priority for event tracing thread")
set(BUILD_WITH_CHRONO_STEADY_CLOCK OFF CACHE BOOL "Use std::chrono::steady_clock as a timer" ) 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") 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: --------") message(STATUS "-------- EASY_PROFILER OPTIONS: --------")
if (BUILD_WITH_CHRONO_STEADY_CLOCK) if (BUILD_WITH_CHRONO_STEADY_CLOCK)
message(STATUS " Use std::chrono::steady_clock as a timer") 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 " Auto-start listening = ${EASY_OPTION_LISTEN}")
message(STATUS " Profile self = ${EASY_OPTION_PROFILE_SELF}") message(STATUS " Profile self = ${EASY_OPTION_PROFILE_SELF}")
message(STATUS " Profile self blocks initial status = ${EASY_OPTION_PROFILE_SELF_BLOCKS_ON}") 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) if (WIN32)
message(STATUS " Event tracing = ${EASY_OPTION_EVENT_TRACING}") message(STATUS " Event tracing = ${EASY_OPTION_EVENT_TRACING}")
if (EASY_OPTION_LOW_PRIORITY_EVENT_TRACING) message(STATUS " Event tracing has low priority = ${EASY_OPTION_LOW_PRIORITY_EVENT_TRACING}")
message(STATUS " Event tracing has low priority = Yes") 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 () else ()
message(STATUS " Event tracing has low priority = No") message(STATUS " WARNING! There is a possibility of memory leak without removing empty unguarded threads.")
endif (EASY_OPTION_LOW_PRIORITY_EVENT_TRACING) endif ()
endif (WIN32) 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 " Log messages = ${EASY_OPTION_LOG}")
message(STATUS " Use EasyProfiler colors palette = ${EASY_OPTION_PREDEFINED_COLORS}") message(STATUS " Use EasyProfiler colors palette = ${EASY_OPTION_PREDEFINED_COLORS}")
message(STATUS " Shared library: ${BUILD_SHARED_LIBS}") message(STATUS " Shared library: ${BUILD_SHARED_LIBS}")
message(STATUS "------ END EASY_PROFILER OPTIONS -------") message(STATUS "------ END EASY_PROFILER OPTIONS -------")
message(STATUS "") message(STATUS "")
# End printing EasyProfiler options status.
#####################################################################
#################################################
# Add source files:
set(CPP_FILES set(CPP_FILES
block.cpp block.cpp
profile_manager.cpp profile_manager.cpp
@ -83,6 +117,11 @@ set(SOURCES
) )
add_library(easy_profiler ${SOURCES} resources.rc) add_library(easy_profiler ${SOURCES} resources.rc)
# End adding source files.
#################################################
target_include_directories(easy_profiler PUBLIC target_include_directories(easy_profiler PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include> # <prefix>/include $<INSTALL_INTERFACE:include> # <prefix>/include
@ -102,18 +141,31 @@ target_compile_definitions(easy_profiler PUBLIC
-DBUILD_WITH_EASY_PROFILER=1 -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_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 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_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 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_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) 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_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) 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 () endif ()
easy_define_target_option(easy_profiler EASY_OPTION_LOG EASY_OPTION_LOG_ENABLED) 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) 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) if (UNIX)
target_compile_options(easy_profiler PRIVATE -Wall -Wno-long-long -Wno-reorder -Wno-braced-scalar-init -pedantic) target_compile_options(easy_profiler PRIVATE -Wall -Wno-long-long -Wno-reorder -Wno-braced-scalar-init -pedantic)
target_link_libraries(easy_profiler pthread) target_link_libraries(easy_profiler pthread)
@ -128,29 +180,29 @@ endif ()
if (MSVC) if (MSVC)
target_compile_options(easy_profiler PRIVATE /WX) target_compile_options(easy_profiler PRIVATE /WX)
endif() endif ()
if (APPLE) if (APPLE)
target_compile_options(easy_profiler PUBLIC -std=gnu++11) target_compile_options(easy_profiler PUBLIC -std=gnu++11)
else () else ()
if (CMAKE_VERSION VERSION_LESS "3.1") if (CMAKE_VERSION VERSION_LESS "3.1")
if (NOT MSVC) if (NOT MSVC)
target_compile_options(easy_profiler PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-std=gnu++11>) target_compile_options(easy_profiler PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-std=gnu++11>)
endif () endif ()
else() else ()
if (NOT MSVC) if (NOT MSVC)
target_compile_options(easy_profiler PUBLIC -std=gnu++11) target_compile_options(easy_profiler PUBLIC -std=gnu++11)
endif () endif ()
set_target_properties(easy_profiler PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON) set_target_properties(easy_profiler PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON)
endif () endif ()
endif () endif ()
# End adding platform specific compile options.
###############################################################################
####
# Installation
#########################################################################################
# Installation:
set(config_install_dir "lib/cmake/${PROJECT_NAME}") set(config_install_dir "lib/cmake/${PROJECT_NAME}")
set(include_install_dir "include") 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_MAX = false;
EASY_THREAD_LOCAL static bool THIS_THREAD_FRAME_T_RESET_AVG = 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 #ifdef BUILD_WITH_EASY_PROFILER
@ -686,7 +690,7 @@ void ThreadStorage::storeBlock(const profiler::Block& block)
if (expanded) beginTime = getCurrentTime(); if (expanded) beginTime = getCurrentTime();
#endif #endif
char* data = (char*)blocks.closedList.allocate(size); void* data = blocks.closedList.allocate(size);
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 #if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
if (expanded) endTime = getCurrentTime(); if (expanded) endTime = getCurrentTime();
@ -898,7 +902,7 @@ bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, cons
} }
else if (THIS_THREAD == nullptr) else if (THIS_THREAD == nullptr)
{ {
THIS_THREAD = &threadStorage(getCurrentThreadId()); registerThread();
} }
#if EASY_ENABLE_BLOCK_STATUS != 0 #if EASY_ENABLE_BLOCK_STATUS != 0
@ -928,7 +932,7 @@ bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, cons
} }
else if (THIS_THREAD == nullptr) else if (THIS_THREAD == nullptr)
{ {
THIS_THREAD = &threadStorage(getCurrentThreadId()); registerThread();
} }
#if EASY_ENABLE_BLOCK_STATUS != 0 #if EASY_ENABLE_BLOCK_STATUS != 0
@ -951,7 +955,7 @@ void ProfileManager::storeBlockForce(const profiler::BaseBlockDescriptor* _desc,
return; return;
if (THIS_THREAD == nullptr) if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId()); registerThread();
#if EASY_ENABLE_BLOCK_STATUS != 0 #if EASY_ENABLE_BLOCK_STATUS != 0
if (!THIS_THREAD->allowChildren && !(_desc->m_status & FORCE_ON_FLAG)) if (!THIS_THREAD->allowChildren && !(_desc->m_status & FORCE_ON_FLAG))
@ -972,7 +976,7 @@ void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc
return; return;
if (THIS_THREAD == nullptr) if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId()); registerThread();
#if EASY_ENABLE_BLOCK_STATUS != 0 #if EASY_ENABLE_BLOCK_STATUS != 0
if (!THIS_THREAD->allowChildren && !(_desc->m_status & FORCE_ON_FLAG)) 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) void ProfileManager::beginBlock(Block& _block)
{ {
if (THIS_THREAD == nullptr) if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId()); registerThread();
if (++THIS_THREAD_STACK_SIZE > 1) 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) void ProfileManager::beginNonScopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
{ {
if (THIS_THREAD == nullptr) if (THIS_THREAD == nullptr)
THIS_THREAD = &threadStorage(getCurrentThreadId()); registerThread();
NonscopedBlock& b = THIS_THREAD->nonscopedBlocks.push(_desc, _runtimeName, false); NonscopedBlock& b = THIS_THREAD->nonscopedBlocks.push(_desc, _runtimeName, false);
beginBlock(b); beginBlock(b);
@ -1149,11 +1153,25 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processi
{ {
ThreadStorage* ts = nullptr; ThreadStorage* ts = nullptr;
if (_process_id == m_processId) if (_process_id == m_processId)
{
// Implicit thread registration.
// If thread owned by current process then create new ThreadStorage if there is no one // 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); 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 else
{
// If thread owned by another process OR _process_id IS UNKNOWN then do not create ThreadStorage for this // 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); ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
}
if (ts == nullptr || ts->sync.openedList.empty()) if (ts == nullptr || ts->sync.openedList.empty())
return; return;
@ -1327,8 +1345,7 @@ char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
return 0; return 0;
#ifdef _WIN32 #ifdef _WIN32
// Check thread state for Windows
// Check thread for Windows
DWORD exitCode = 0; DWORD exitCode = 0;
auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)_registeredThread.id); auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)_registeredThread.id);
@ -1345,13 +1362,20 @@ char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
CloseHandle(hThread); CloseHandle(hThread);
return 0; return 0;
#else #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 #endif
return 0;
#endif
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -1466,10 +1490,19 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bo
{ {
auto& t = it->second; auto& t = it->second;
uint32_t num = static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size()); 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 !defined(_WIN32) && defined(EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS) && EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS != 0
if (num == 0 && (expired != 0 || !t.guarded)) { #pragma message "Warning: Removing !guarded thread may cause an application crash, but fixes potential memory leak on Unix systems."
// Remove thread if it contains no profiled information and has been finished or is not guarded. 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; profiler::thread_id_t id = it->first;
if (!mainThreadExpired && m_mainThreadId.compare_exchange_weak(id, 0, std::memory_order_release, std::memory_order_acquire)) if (!mainThreadExpired && m_mainThreadId.compare_exchange_weak(id, 0, std::memory_order_release, std::memory_order_acquire))
mainThreadExpired = true; mainThreadExpired = true;
@ -1477,7 +1510,8 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bo
continue; continue;
} }
if (expired == 1) { if (expired == 1)
{
EASY_FORCE_EVENT3(t, endtime, "ThreadExpired", EASY_COLOR_THREAD_END); EASY_FORCE_EVENT3(t, endtime, "ThreadExpired", EASY_COLOR_THREAD_END);
++num; ++num;
} }
@ -1605,6 +1639,16 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
return blocksNumber; 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) const char* ProfileManager::registerThread(const char* name, ThreadGuard& threadGuard)
{ {
if (THIS_THREAD == nullptr) if (THIS_THREAD == nullptr)
@ -1621,9 +1665,17 @@ const char* ProfileManager::registerThread(const char* name, ThreadGuard& thread
profiler::thread_id_t id = 0; 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); 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; threadGuard.m_id = THIS_THREAD->id;
#endif
return THIS_THREAD->name.c_str(); return THIS_THREAD->name.c_str();
} }
@ -1643,6 +1695,11 @@ const char* ProfileManager::registerThread(const char* name)
profiler::thread_id_t id = 0; 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); 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(); return THIS_THREAD->name.c_str();

View File

@ -3,40 +3,40 @@ Lightweight profiler library for c++
Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of 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) * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option. at your option.
The MIT License The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 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 of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions: to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 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 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 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, 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 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE. USE OR OTHER DEALINGS IN THE SOFTWARE.
The Apache License, Version 2.0 (the "License"); The Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with 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 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 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
**/ **/
@ -368,29 +368,28 @@ EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val)
template <uint16_t N> template <uint16_t N>
class chunk_allocator 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 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() ~chunk_list()
{ {
clear(); do free_last(); while (last != nullptr);
} }
void clear() void clear_all_except_last()
{ {
do { while (last->prev != nullptr)
auto p = last; free_last();
last = last->prev; zero_last_chunk_size();
EASY_FREE(p);
} while (last != nullptr);
}
chunk& back()
{
return *last;
} }
void emplace_back() void emplace_back()
@ -398,12 +397,7 @@ class chunk_allocator
auto prev = last; auto prev = last;
last = ::new (EASY_MALLOC(sizeof(chunk), EASY_ALIGNMENT_SIZE)) chunk(); last = ::new (EASY_MALLOC(sizeof(chunk), EASY_ALIGNMENT_SIZE)) chunk();
last->prev = prev; last->prev = prev;
// Although there is no need for unaligned access stuff b/c a new chunk will zero_last_chunk_size();
// 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;
} }
/** Invert current chunks list to enable to iterate over chunks list in direct order. /** Invert current chunks list to enable to iterate over chunks list in direct order.
@ -423,12 +417,33 @@ class chunk_allocator
last->prev = next; 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. // 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. chunk_list m_chunks; ///< List of chunks.
uint32_t m_size; ///< Number of elements stored(# of times allocate() has been called.) 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) chunk_allocator() : m_size(0), m_chunkOffset(0)
{ {
m_chunks.emplace_back();
} }
/** Allocate n bytes. /** Allocate n bytes.
@ -454,7 +468,7 @@ public:
{ {
// Temp to avoid extra load due to this* aliasing. // Temp to avoid extra load due to this* aliasing.
uint16_t chunkOffset = m_chunkOffset; 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); chunkOffset += n + sizeof(uint16_t);
m_chunkOffset = chunkOffset; m_chunkOffset = chunkOffset;
@ -463,7 +477,7 @@ public:
// If there is enough space for at least another payload size, // If there is enough space for at least another payload size,
// set it to zero. // set it to zero.
if (chunkOffset < N-1) if (chunkOffset < N_MINUS_ONE)
unaligned_zero16(data + n); unaligned_zero16(data + n);
return data; return data;
@ -472,7 +486,7 @@ public:
m_chunkOffset = n + sizeof(uint16_t); m_chunkOffset = n + sizeof(uint16_t);
m_chunks.emplace_back(); m_chunks.emplace_back();
char* data = (char*)&m_chunks.back().data[0]; char* data = m_chunks.last->data;
unaligned_store16(data, n); unaligned_store16(data, n);
data += sizeof(uint16_t); data += sizeof(uint16_t);
@ -503,8 +517,7 @@ public:
{ {
m_size = 0; m_size = 0;
m_chunkOffset = 0; m_chunkOffset = 0;
m_chunks.clear(); m_chunks.clear_all_except_last(); // There is always at least one chunk
m_chunks.emplace_back();
} }
/** Serialize data to stream. /** Serialize data to stream.
@ -529,10 +542,10 @@ public:
chunk* current = m_chunks.last; chunk* current = m_chunks.last;
do { do {
const char* data = (char*)current->data; const char* data = current->data;
int_fast32_t chunkOffset = 0; // signed int so overflow is not checked. int_fast32_t chunkOffset = 0; // signed int so overflow is not checked.
uint16_t payloadSize = unaligned_load16<uint16_t>(data); 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; const uint16_t chunkSize = sizeof(uint16_t) + payloadSize;
_outputStream.write(data, chunkSize); _outputStream.write(data, chunkSize);
data += chunkSize; data += chunkSize;
@ -546,6 +559,11 @@ public:
clear(); clear();
} }
private:
chunk_allocator(const chunk_allocator&) = delete;
chunk_allocator(chunk_allocator&&) = delete;
}; // END of class chunk_allocator. }; // END of class chunk_allocator.
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -652,6 +670,11 @@ public:
m_overflow.pop_back(); m_overflow.pop_back();
} }
private:
StackBuffer(const StackBuffer&) = delete;
StackBuffer(StackBuffer&&) = delete;
}; // END of class StackBuffer. }; // END of class StackBuffer.
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -670,6 +693,11 @@ struct BlocksList
usedMemorySize = 0; usedMemorySize = 0;
} }
private:
BlocksList(const BlocksList&) = delete;
BlocksList(BlocksList&&) = delete;
}; // END of struct BlocksList. }; // END of struct BlocksList.
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -715,6 +743,11 @@ struct ThreadStorage
ThreadStorage(); ThreadStorage();
private:
ThreadStorage(const ThreadStorage&) = delete;
ThreadStorage(ThreadStorage&&) = delete;
}; // END of struct ThreadStorage. }; // END of struct ThreadStorage.
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -822,13 +855,15 @@ public:
private: private:
void registerThread();
void beginFrame(); void beginFrame();
void endFrame(); void endFrame();
void enableEventTracer(); void enableEventTracer();
void disableEventTracer(); 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 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); void storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp);