From 9e4af3aca062222cad9576315ba99a0b96b60e49 Mon Sep 17 00:00:00 2001 From: Arnim Balzer Date: Sat, 20 Apr 2024 09:04:07 +0100 Subject: [PATCH] Add name buffer to tracy client library To be used by Python and Go bindings to store const char * accessible via a lookup --- CMakeLists.txt | 14 ++++++ manual/tracy.tex | 8 ++-- public/TracyClient.cpp | 4 ++ public/common/TracyNameBuffer.cpp | 44 +++++++++++++++++++ public/common/TracyNameBuffer.hpp | 37 ++++++++++++++++ public/tracy/Tracy.hpp | 4 ++ public/tracy/TracyC.h | 17 ++++++++ python/CMakeLists.txt | 6 --- python/bindings/Memory.hpp | 3 +- python/bindings/Module.cpp | 71 ++++++++++++++----------------- python/bindings/NameBuffer.hpp | 59 ------------------------- 11 files changed, 159 insertions(+), 108 deletions(-) create mode 100644 public/common/TracyNameBuffer.cpp create mode 100644 public/common/TracyNameBuffer.hpp delete mode 100644 python/bindings/NameBuffer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ad4d6ab5..70f3690c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ set_option(TRACY_TIMER_FALLBACK "Use lower resolution timers" OFF) set_option(TRACY_LIBUNWIND_BACKTRACE "Use libunwind backtracing where supported" OFF) set_option(TRACY_SYMBOL_OFFLINE_RESOLVE "Instead of full runtime symbol resolution, only resolve the image path and offset to enable offline symbol resolution" OFF) set_option(TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT "Enable libbacktrace to support dynamically loaded elfs in symbol resolution resolution after the first symbol resolve operation" OFF) +set_option(TRACY_NAME_BUFFER "Enable name buffer for other languages" OFF) if(NOT TRACY_STATIC) target_compile_definitions(TracyClient PRIVATE TRACY_EXPORTS) @@ -141,6 +142,16 @@ set(common_includes ${TRACY_PUBLIC_DIR}/common/TracyUwp.hpp ${TRACY_PUBLIC_DIR}/common/TracyYield.hpp) +if(TRACY_NAME_BUFFER) + set(TRACY_BUFFER_SIZE 128 CACHE STRING "The size of the name buffer") + set(TRACY_NAME_LENGTH 128 CACHE STRING "The length of a name in the buffer") + + list(APPEND common_includes ${TRACY_PUBLIC_DIR}/common/TracyNameBuffer.hpp) + + target_compile_definitions(TracyClient PRIVATE TRACY_BUFFER_SIZE=${TRACY_BUFFER_SIZE}) + target_compile_definitions(TracyClient PRIVATE TRACY_NAME_LENGTH=${TRACY_NAME_LENGTH}) +endif() + install(TARGETS TracyClient EXPORT TracyConfig RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} @@ -169,6 +180,9 @@ if(TRACY_CLIENT_PYTHON) if(TRACY_STATIC) message(FATAL_ERROR "Python-bindings require a shared client library") endif() + if(NOT TRACY_NAME_BUFFER) + message(FATAL_ERROR "Python-bindings require name buffer being enabled") + endif() add_subdirectory(python) endif() diff --git a/manual/tracy.tex b/manual/tracy.tex index b280906f..a7d67c2b 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -2316,15 +2316,15 @@ Please not the use of ids as way to cope with the need for unique pointers for c \subsubsection{Building the Python package} To build the Python package, you will need to use the CMake build system to compile the Tracy-Client. -The CMake option \texttt{-D TRACY\_CLIENT\_PYTHON=ON} is used to enable the generation of the Python bindings in conjunction with a mandatory creation of a shared Tracy-Client library via one of the CMake options \texttt{-D BUILD\_SHARED\_LIBS=ON} or \texttt{-D DEFAULT\_STATIC=OFF}. +The CMake option \texttt{-D TRACY\_CLIENT\_PYTHON=ON} is used to enable the generation of the Python bindings in conjunction with a mandatory creation of a shared Tracy-Client library via one of the CMake options \texttt{-D BUILD\_SHARED\_LIBS=ON} or \texttt{-D DEFAULT\_STATIC=OFF}. Moreover, the tracy name buffer needs to be built into the client via \texttt{-D TRACY\_NAME\_BUFFER=ON}. The following other variables are available in addition: \begin{itemize} \item \texttt{EXTERNAL\_PYBIND11} --- Can be used to disable the download of pybind11 when Tracy is embedded in another CMake project that already uses pybind11. \item \texttt{TRACY\_CLIENT\_PYTHON\_TARGET} --- Optional directory to copy Tracy Python bindings to when Tracy is embedded in another CMake project. -\item \texttt{BUFFER\_SIZE} --- The size of the global pointer buffer (defaults to 128) for naming Tracy profiling entities like frame marks, plots, and memory locations. -\item \texttt{NAME\_LENGTH} --- The maximum length (defaults to 128) of a name stored in the global pointer buffer. +\item \texttt{TRACY\_BUFFER\_SIZE} --- The size of the global pointer buffer (defaults to 128) for naming Tracy profiling entities like frame marks, plots, and memory locations. +\item \texttt{TRACY\_NAME\_LENGTH} --- The maximum length (defaults to 128) of a name stored in the global pointer buffer. \end{itemize} Be aware that the memory allocated by this buffer is global and is not freed, see section~\ref{uniquepointers}. @@ -2334,7 +2334,7 @@ See below for example steps to build the Python bindings using CMake: \begin{lstlisting} mkdir build cd build -cmake -DTRACY_STATIC=OFF -DTRACY_CLIENT_PYTHON=ON ../ +cmake -DTRACY_STATIC=OFF -DTRACY_NAME_BUFFER=ON -DTRACY_CLIENT_PYTHON=ON ../ \end{lstlisting} Once this has finished building the Python package can be built as follows: diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp index 74811901..619184d1 100644 --- a/public/TracyClient.cpp +++ b/public/TracyClient.cpp @@ -49,6 +49,10 @@ # endif #endif +#ifdef TRACY_NAME_BUFFER +#include "common/TracyNameBuffer.cpp" +#endif + #ifdef _MSC_VER # pragma comment(lib, "ws2_32.lib") # pragma comment(lib, "dbghelp.lib") diff --git a/public/common/TracyNameBuffer.cpp b/public/common/TracyNameBuffer.cpp new file mode 100644 index 00000000..c7181bd2 --- /dev/null +++ b/public/common/TracyNameBuffer.cpp @@ -0,0 +1,44 @@ +#include "TracyNameBuffer.hpp" +using namespace tracy; + +#include "TracyApi.h" + +#ifndef TRACY_BUFFER_SIZE +#define TRACY_BUFFER_SIZE = 128 +#endif + +#ifndef TRACY_NAME_LENGTH +#define TRACY_NAME_LENGTH = 128 +#endif + +NameBuffer::NameBuffer() : m_buffer(TRACY_BUFFER_SIZE, nullptr), m_index(0ul) { + for (std::size_t index = 0ul, end = m_buffer.size(); index < end; ++index) + m_buffer[index] = new char[TRACY_NAME_LENGTH]; +} + +BufferEntry NameBuffer::add( const std::string& name ) { + std::lock_guard lock(m_mutex); + if (m_index >= TRACY_BUFFER_SIZE || name.size() > TRACY_NAME_LENGTH) + return std::make_pair(std::nullopt, nullptr); + + auto index = m_index++; + name.copy(m_buffer[index], name.size()); + return std::make_pair(index, m_buffer[index]); +} + +const char* NameBuffer::get( uint16_t index ) { + std::lock_guard lock(m_mutex); + if (index >= TRACY_BUFFER_SIZE) return nullptr; + return m_buffer[index]; +} + +#ifdef TRACY_NAME_BUFFER +TRACY_API const char* ___tracy_name_buffer_add( const char* name, uint16_t* id ) { + auto entry = NameBuffer::Add(name); + if (!entry.first) return nullptr; + + if (id != nullptr) *id = *entry.first; + return entry.second; +} +TRACY_API const char* ___tracy_name_buffer_get( uint16_t id ) { return NameBuffer::Get(id); } +#endif diff --git a/public/common/TracyNameBuffer.hpp b/public/common/TracyNameBuffer.hpp new file mode 100644 index 00000000..3cb07dc2 --- /dev/null +++ b/public/common/TracyNameBuffer.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +namespace tracy { +using OptionalNumber = std::optional; +using BufferEntry = std::pair; + +class NameBuffer { + public: + static inline BufferEntry Add( const std::string& name ) { + return getBuffer().add(name); + } + + static inline const char* Get( uint16_t index ) { + return getBuffer().get(index); + } + + private: + NameBuffer(); + + std::mutex m_mutex; + std::vector m_buffer; + std::size_t m_index; + + static inline NameBuffer& getBuffer() { + static NameBuffer buffer; + return buffer; + } + + BufferEntry add( const std::string& name ); + const char* get( uint16_t index ); +}; +} // namespace tracy diff --git a/public/tracy/Tracy.hpp b/public/tracy/Tracy.hpp index e5738ff0..78ffde57 100644 --- a/public/tracy/Tracy.hpp +++ b/public/tracy/Tracy.hpp @@ -291,6 +291,10 @@ # define TracyFiberLeave tracy::Profiler::LeaveFiber() #endif +#ifdef TRACY_NAME_BUFFER +# include "../common/TracyNameBuffer.hpp" +#endif + #endif #endif diff --git a/public/tracy/TracyC.h b/public/tracy/TracyC.h index 8b447beb..e3e70b1e 100644 --- a/public/tracy/TracyC.h +++ b/public/tracy/TracyC.h @@ -37,6 +37,8 @@ TRACY_API void ___tracy_set_thread_name( const char* name ); #ifndef TRACY_ENABLE +#define TracyCEnabled() 0 + typedef const void* TracyCZoneCtx; typedef const void* TracyCLockCtx; @@ -116,8 +118,15 @@ typedef const void* TracyCLockCtx; # define TracyCFiberLeave #endif +#ifdef TRACY_NAME_BUFFER +# define TracyCNameBufferAdd(name, id) 0 +# define TracyCNameBufferGet(id) 0 +#endif + #else +#define TracyCEnabled() 1 + #ifndef TracyConcat # define TracyConcat(x,y) TracyConcatIndirect(x,y) #endif @@ -408,6 +417,14 @@ TRACY_API void ___tracy_fiber_leave( void ); # define TracyCFiberLeave ___tracy_fiber_leave(); #endif +#ifdef TRACY_NAME_BUFFER +TRACY_API const char* ___tracy_name_buffer_add( const char* name, uint16_t* id ); +TRACY_API const char* ___tracy_name_buffer_get( uint16_t id ); + +# define TracyCNameBufferAdd(name, id) ___tracy_name_buffer_add( name, id ); +# define TracyCNameBufferGet(id) ___tracy_name_buffer_get( id ); +#endif + #endif #ifdef __cplusplus diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index e1ca2a74..2b0e821f 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -9,14 +9,8 @@ if(EXTERNAL_PYBIND11) FetchContent_MakeAvailable(pybind11) endif() -set(BUFFER_SIZE 128 CACHE STRING "The size of the pointer buffer") -set(NAME_LENGTH 128 CACHE STRING "The length of a name in the buffer") - pybind11_add_module(TracyClientBindings SHARED bindings/Module.cpp) target_link_libraries(TracyClientBindings PUBLIC TracyClient) -target_compile_definitions(TracyClientBindings PUBLIC BUFFER_SIZE=${BUFFER_SIZE}) -target_compile_definitions(TracyClientBindings PUBLIC NAME_LENGTH=${NAME_LENGTH}) - set(TRACY_PYTHON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tracy_client) set(TRACY_LIB_SYMLINK $$$) diff --git a/python/bindings/Memory.hpp b/python/bindings/Memory.hpp index 91430ca9..f2712cb1 100644 --- a/python/bindings/Memory.hpp +++ b/python/bindings/Memory.hpp @@ -3,8 +3,8 @@ #include namespace py = pybind11; -#include "NameBuffer.hpp" #include "tracy/Tracy.hpp" +using namespace tracy; using OptionalString = std::optional; using OptionalInt = std::optional; @@ -61,6 +61,7 @@ bool MemoryFree(const Type &type, const OptionalNumber &id = std::nullopt, return true; } #else +using OptionalNumber = std::optional; template OptionalNumber MemoryAllocate(const Type &, std::size_t, const OptionalString &, diff --git a/python/bindings/Module.cpp b/python/bindings/Module.cpp index 0a13b836..8ef3ba05 100644 --- a/python/bindings/Module.cpp +++ b/python/bindings/Module.cpp @@ -1,25 +1,18 @@ #include "Memory.hpp" #include "ScopedZone.hpp" #include "tracy/TracyC.h" +using namespace tracy; namespace tracy { #ifndef TRACY_ENABLE enum class PlotFormatType : uint8_t { Number, Memory, Percentage }; #endif - -constexpr static inline bool IsEnabled() { -#ifdef TRACY_ENABLE - return true; -#else - return false; -#endif -} } // namespace tracy PYBIND11_MODULE(TracyClientBindings, m) { m.doc() = "Tracy Client Bindings"; - m.def("is_enabled", &tracy::IsEnabled); + m.def("is_enabled", []() -> bool { return TracyCEnabled(); }); py::enum_(m, "ColorType") .value("Snow", tracy::Color::Snow) @@ -703,10 +696,10 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "program_name", [](const std::string &name) { - if (!tracy::IsEnabled()) return true; - auto entry = NameBuffer::Add(name); - if (!entry.first) return false; - TracySetProgramName(entry.second); + if (!TracyCEnabled()) return true; + auto ptr = TracyCNameBufferAdd(name.c_str(), nullptr); + if (!ptr) return false; + TracySetProgramName(ptr); return true; }, "name"_a.none(false)); @@ -714,7 +707,7 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "thread_name", [](const std::string &name) { - if (!tracy::IsEnabled()) return; + if (!TracyCEnabled()) return; tracy::SetThreadName(name.c_str()); }, "name"_a.none(false)); @@ -722,7 +715,7 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "app_info", [](const std::string &text) { - if (!tracy::IsEnabled()) return true; + if (!TracyCEnabled()) return true; if (text.size() >= std::numeric_limits::max()) return false; TracyAppInfo(text.c_str(), text.size()); return true; @@ -732,7 +725,7 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "message", [](const std::string &message) { - if (!tracy::IsEnabled()) return true; + if (!TracyCEnabled()) return true; if (message.size() >= std::numeric_limits::max()) return false; TracyMessage(message.c_str(), message.size()); @@ -743,7 +736,7 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "message", [](const std::string &message, uint32_t pColor) { - if (!tracy::IsEnabled()) return true; + if (!TracyCEnabled()) return true; if (message.size() >= std::numeric_limits::max()) return false; TracyMessageC(message.c_str(), message.size(), pColor); @@ -755,20 +748,21 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "frame_mark_start", - [](const std::string &name) { - if (!tracy::IsEnabled()) return static_cast(0ul); - auto entry = NameBuffer::Add(name); - if (!entry.first) return static_cast(std::nullopt); - FrameMarkStart(entry.second); - return entry.first; + [](const std::string &name) -> OptionalNumber { + if (!TracyCEnabled()) return 0ul; + uint16_t id = 0ul; + auto ptr = TracyCNameBufferAdd(name.c_str(), &id); + if (!ptr) return static_cast(std::nullopt); + FrameMarkStart(ptr); + return id; }, "name"_a.none(false)); m.def( "frame_mark_end", [](std::size_t id) { - if (!tracy::IsEnabled()) return true; - auto ptr = NameBuffer::Get(id); + if (!TracyCEnabled()) return true; + auto ptr = TracyCNameBufferGet(id); if (!ptr) return false; FrameMarkEnd(ptr); return true; @@ -779,7 +773,7 @@ PYBIND11_MODULE(TracyClientBindings, m) { "frame_image", [](const py::bytes &image, uint16_t width, uint16_t height, uint8_t offset = 0, bool flip = false) { - if (!tracy::IsEnabled()) return true; + if (!TracyCEnabled()) return true; if (width % 4 != 0 || height % 4 != 0) return false; TracyCFrameImage(std::string(image).data(), width, height, offset, flip); @@ -821,12 +815,13 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "_plot_config", [](const std::string &name, int type, bool step, bool fill, - uint32_t color = 0) { - if (!tracy::IsEnabled()) return static_cast(0ul); - auto entry = NameBuffer::Add(name); - if (!entry.first) return static_cast(std::nullopt); - TracyCPlotConfig(entry.second, type, step, fill, color); - return entry.first; + uint32_t color = 0) -> OptionalNumber { + if (!TracyCEnabled()) return 0ul; + uint16_t id = 0ul; + auto ptr = TracyCNameBufferAdd(name.c_str(), &id); + if (!ptr) return static_cast(std::nullopt); + TracyCPlotConfig(ptr, type, step, fill, color); + return id; }, "name"_a.none(false), "type"_a.none(false), "step"_a.none(false), "fill"_a.none(false), "color"_a.none(false)); @@ -840,8 +835,8 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "plot", [](std::size_t id, double value) { - if (!tracy::IsEnabled()) return true; - auto ptr = NameBuffer::Get(id); + if (!TracyCEnabled()) return true; + auto ptr = TracyCNameBufferGet(id); if (!ptr) return false; TracyCPlot(ptr, value); return true; @@ -850,8 +845,8 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "plot", [](std::size_t id, float value) { - if (!tracy::IsEnabled()) return true; - auto ptr = NameBuffer::Get(id); + if (!TracyCEnabled()) return true; + auto ptr = TracyCNameBufferGet(id); if (!ptr) return false; TracyCPlotF(ptr, value); return true; @@ -860,8 +855,8 @@ PYBIND11_MODULE(TracyClientBindings, m) { m.def( "plot", [](std::size_t id, int64_t value) { - if (!tracy::IsEnabled()) return true; - auto ptr = NameBuffer::Get(id); + if (!TracyCEnabled()) return true; + auto ptr = TracyCNameBufferGet(id); if (!ptr) return false; TracyCPlotI(ptr, value); return true; diff --git a/python/bindings/NameBuffer.hpp b/python/bindings/NameBuffer.hpp deleted file mode 100644 index 071010e5..00000000 --- a/python/bindings/NameBuffer.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#ifndef BUFFER_SIZE -#define BUFFER_SIZE = 128 -#endif - -#ifndef NAME_LENGTH -#define NAME_LENGTH = 128 -#endif - -using OptionalNumber = std::optional; -using BufferEntry = std::pair; - -class NameBuffer { - public: - static inline BufferEntry Add(const std::string& name) { - return getBuffer().add(name); - } - - static inline const char* Get(std::size_t index) { - return getBuffer().get(index); - } - - private: - NameBuffer() : m_buffer(BUFFER_SIZE, nullptr), m_index(0ul) { - for (std::size_t index = 0ul, end = m_buffer.size(); index < end; ++index) - m_buffer[index] = new char[NAME_LENGTH]; - } - - std::mutex m_mutex; - std::vector m_buffer; - std::size_t m_index; - - static inline NameBuffer& getBuffer() { - static NameBuffer buffer; - return buffer; - } - - BufferEntry add(const std::string& name) { - std::lock_guard lock(m_mutex); - if (m_index >= BUFFER_SIZE || name.size() > NAME_LENGTH) - return std::make_pair(std::nullopt, nullptr); - - auto index = m_index++; - name.copy(m_buffer[index], name.size()); - return std::make_pair(index, m_buffer[index]); - } - - const char* get(std::size_t index) { - std::lock_guard lock(m_mutex); - if (index >= BUFFER_SIZE) return nullptr; - return m_buffer[index]; - } -};