diff --git a/Framework/Foundation/CMakeLists.txt b/Framework/Foundation/CMakeLists.txt index 6d8862350cd73..128a9533ef26b 100644 --- a/Framework/Foundation/CMakeLists.txt +++ b/Framework/Foundation/CMakeLists.txt @@ -15,6 +15,7 @@ install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/Framework o2_add_library(FrameworkFoundation SOURCES src/RuntimeError.cxx + src/Signpost.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::FrameworkFoundation3rdparty ) @@ -39,9 +40,14 @@ add_executable(o2-test-framework-Signpost test/test_Signpost.cxx) target_link_libraries(o2-test-framework-Signpost PRIVATE O2::FrameworkFoundation) +add_executable(o2-test-framework-SignpostLogger + test/test_SignpostLogger.cxx) +target_link_libraries(o2-test-framework-SignpostLogger PRIVATE O2::FrameworkFoundation) + get_filename_component(outdir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../tests ABSOLUTE) set_property(TARGET o2-test-framework-foundation PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) set_property(TARGET o2-test-framework-Signpost PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) +set_property(TARGET o2-test-framework-SignpostLogger PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) add_test(NAME framework:foundation COMMAND o2-test-framework-foundation) diff --git a/Framework/Foundation/include/Framework/Signpost.h b/Framework/Foundation/include/Framework/Signpost.h index 2e9ea6fdb00e9..5df53f2a3c520 100644 --- a/Framework/Foundation/include/Framework/Signpost.h +++ b/Framework/Foundation/include/Framework/Signpost.h @@ -55,77 +55,18 @@ // the signpost information to the log. #include #include -#include -#include "Framework/RuntimeError.h" - #include -#include -#include #include +#include -namespace { +namespace +{ struct _o2_lock_free_stack { static constexpr size_t N = 1024; std::atomic top = 0; int stack[N]; }; -// returns true if the push was successful, false if the stack was full -// @param spin if true, will spin until the stack is not full -bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin = false) -{ - size_t currentTop = stack.top.load(std::memory_order_relaxed); - while (true) { - if (currentTop == _o2_lock_free_stack::N && spin == false) { - return false; - } else if (currentTop == _o2_lock_free_stack::N) { -// Avoid consuming too much CPU time if we are spinning. -#if defined(__x86_64__) || defined(__i386__) - __asm__ __volatile__("pause" :: - : "memory"); -#elif defined(__aarch64__) - __asm__ __volatile__("yield" :: - : "memory"); -#endif - continue; - } - - if (stack.top.compare_exchange_weak(currentTop, currentTop + 1, - std::memory_order_release, - std::memory_order_relaxed)) { - stack.stack[currentTop] = value; - return true; - } - } -} - -bool _o2_lock_free_stack_pop(_o2_lock_free_stack& stack, int& value, bool spin = false) -{ - size_t currentTop = stack.top.load(std::memory_order_relaxed); - while (true) { - if (currentTop == 0 && spin == false) { - return false; - } else if (currentTop == 0) { -// Avoid consuming too much CPU time if we are spinning. -#if defined(__x86_64__) || defined(__i386__) - __asm__ __volatile__("pause" :: - : "memory"); -#elif defined(__aarch64__) - __asm__ __volatile__("yield" :: - : "memory"); -#endif - continue; - } - - if (stack.top.compare_exchange_weak(currentTop, currentTop - 1, - std::memory_order_acquire, - std::memory_order_relaxed)) { - value = stack.stack[currentTop - 1]; - return true; - } - } -} - // A log is simply an inbox which keeps track of the available id, so that we can print out different signposts // with different indentation levels. // supports up to 1024 paralle signposts before it spinlocks. @@ -162,6 +103,18 @@ struct _o2_log_t { std::atomic stacktrace = 1; }; +bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin = false); +bool _o2_lock_free_stack_pop(_o2_lock_free_stack& stack, int& value, bool spin = false); +//_o2_signpost_id_t _o2_signpost_id_generate_local(_o2_log_t* log); +//_o2_signpost_id_t _o2_signpost_id_make_with_pointer(_o2_log_t* log, void* pointer); +_o2_signpost_index_t o2_signpost_id_make_with_pointer(_o2_log_t* log, void* pointer); +_o2_log_t* _o2_log_create(char const* name, int stacktrace); +void _o2_signpost_event_emit(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...); +void _o2_signpost_interval_begin(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...); +void _o2_signpost_interval_end_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args); +void _o2_signpost_interval_end(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...); +void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace); + // This generates a unique id for a signpost. Do not use this directly, use O2_SIGNPOST_ID_GENERATE instead. // Notice that this is only valid on a given computer. // This is guaranteed to be unique at 5 GHz for at least 63 years, if my math is correct. @@ -190,6 +143,71 @@ inline _o2_signpost_index_t o2_signpost_id_make_with_pointer(_o2_log_t* log, voi log->ids[signpost_index].id = (int64_t)pointer; return signpost_index; } +} // namespace + +// Implementation start here. Include this file with O2_SIGNPOST_IMPLEMENTATION defined in one file of your +// project. +#ifdef O2_SIGNPOST_IMPLEMENTATION +#include +#include +#include "Framework/RuntimeError.h" +namespace +{ +// returns true if the push was successful, false if the stack was full +// @param spin if true, will spin until the stack is not full +bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin) +{ + size_t currentTop = stack.top.load(std::memory_order_relaxed); + while (true) { + if (currentTop == _o2_lock_free_stack::N && spin == false) { + return false; + } else if (currentTop == _o2_lock_free_stack::N) { +// Avoid consuming too much CPU time if we are spinning. +#if defined(__x86_64__) || defined(__i386__) + __asm__ __volatile__("pause" :: + : "memory"); +#elif defined(__aarch64__) + __asm__ __volatile__("yield" :: + : "memory"); +#endif + continue; + } + + if (stack.top.compare_exchange_weak(currentTop, currentTop + 1, + std::memory_order_release, + std::memory_order_relaxed)) { + stack.stack[currentTop] = value; + return true; + } + } +} + +bool _o2_lock_free_stack_pop(_o2_lock_free_stack& stack, int& value, bool spin) +{ + size_t currentTop = stack.top.load(std::memory_order_relaxed); + while (true) { + if (currentTop == 0 && spin == false) { + return false; + } else if (currentTop == 0) { +// Avoid consuming too much CPU time if we are spinning. +#if defined(__x86_64__) || defined(__i386__) + __asm__ __volatile__("pause" :: + : "memory"); +#elif defined(__aarch64__) + __asm__ __volatile__("yield" :: + : "memory"); +#endif + continue; + } + + if (stack.top.compare_exchange_weak(currentTop, currentTop - 1, + std::memory_order_acquire, + std::memory_order_relaxed)) { + value = stack.stack[currentTop - 1]; + return true; + } + } +} _o2_log_t* _o2_log_create(char const* name, int stacktrace) { @@ -318,7 +336,8 @@ void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace) { log->stacktrace = stacktrace; } -} +} // anonymous namespace +#endif // O2_SIGNPOST_IMPLEMENTATION /// Dynamic logs need to be enabled via the O2_LOG_ENABLE_DYNAMIC macro. Notice this will only work /// for the logger based logging, since the Apple version needs instruments to enable them. diff --git a/Framework/Foundation/src/Signpost.cxx b/Framework/Foundation/src/Signpost.cxx new file mode 100644 index 0000000000000..be2d14ceeb750 --- /dev/null +++ b/Framework/Foundation/src/Signpost.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define O2_SIGNPOST_IMPLEMENTATION +#include "Framework/Signpost.h" + diff --git a/Framework/Foundation/test/test_Signpost.cxx b/Framework/Foundation/test/test_Signpost.cxx index 685cd5b387d06..480c921172b93 100644 --- a/Framework/Foundation/test/test_Signpost.cxx +++ b/Framework/Foundation/test/test_Signpost.cxx @@ -9,7 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define O2_FORCE_LOGGER_SIGNPOST 1 #include "Framework/Signpost.h" #include diff --git a/Framework/Foundation/test/test_SignpostLogger.cxx b/Framework/Foundation/test/test_SignpostLogger.cxx new file mode 100644 index 0000000000000..effbf7bb6e4b6 --- /dev/null +++ b/Framework/Foundation/test/test_SignpostLogger.cxx @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + + +#ifdef __APPLE__ +#define O2_SIGNPOST_IMPLEMENTATION +#endif +#define O2_FORCE_LOGGER_SIGNPOST 1 +#include "Framework/Signpost.h" +#include + +int main(int argc, char** argv) +{ + O2_DECLARE_LOG(test_Signpost, "my category"); + O2_DECLARE_DYNAMIC_LOG(test_SignpostDynamic); + + O2_LOG_DEBUG(test_Signpost, "%s %d", "test_Signpost", 1); + O2_SIGNPOST_ID_GENERATE(id, test_Signpost); + O2_SIGNPOST_ID_GENERATE(id2, test_Signpost); + O2_SIGNPOST_START(test_Signpost, id, "Test category", "This is a test signpost"); + O2_SIGNPOST_START(test_Signpost, id2, "Test category", "A sepaarate interval"); + O2_SIGNPOST_EVENT_EMIT(test_Signpost, id, "Test category", "An event in an interval"); + O2_SIGNPOST_END(test_Signpost, id, "Test category", "End of the first interval"); + O2_SIGNPOST_END(test_Signpost, id2, "Test category", "A sepaarate interval"); + O2_SIGNPOST_ID_FROM_POINTER(id3, test_Signpost, &id2); + O2_SIGNPOST_START(test_Signpost, id3, "Test category", "A signpost interval from a pointer"); + O2_SIGNPOST_END(test_Signpost, id3, "Test category", "A signpost interval from a pointer"); + + // This has an engineering type, which we will not use on Linux / FairLogger + O2_SIGNPOST_ID_FROM_POINTER(id4, test_Signpost, &id3); + O2_SIGNPOST_START(test_Signpost, id4, "Test category", "A signpost with an engineering type formatter " O2_ENG_TYPE(size - in - bytes, "d"), 1); + O2_SIGNPOST_END(test_Signpost, id4, "Test category", "A signpost interval from a pointer"); + + O2_SIGNPOST_START(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will not see, because they are off by default"); + O2_SIGNPOST_END(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will not see, because they are off by default"); + O2_LOG_ENABLE_DYNAMIC(test_SignpostDynamic); +#ifdef __APPLE__ + // On Apple there is no way to turn on signposts in the logger, so we do not display this message + O2_SIGNPOST_START(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will see, because we turned them on"); + O2_SIGNPOST_END(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will see, because we turned them on"); +#endif +}