diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f82a665f8..e222de388c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,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) set_option(TRACY_DEBUGINFOD "Enable debuginfod support" OFF) # advanced @@ -221,6 +222,16 @@ set(common_includes ${TRACY_PUBLIC_DIR}/common/TracyWinFamily.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}/$,,$> @@ -277,6 +288,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 ce1a75acb8..8de408cf0f 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -3630,7 +3630,7 @@ \subsubsection{Timeline view} \item \emph{Gray} -- Threads assigned to other programs running in the system. \end{itemize} -When the \faMousePointer{}~mouse pointer is hovered over either the CPU data zone or the thread timeline label, Tracy will display a line connecting all zones associated with the selected thread. This can be used to quickly see how the thread migrated across the CPU cores. +When the \faMousePointer{}~mouse pointer is hovered over either the CPU data zone or the thread timeline label, Tracy will display a line connecting all zones associated with the selected thread. This can be used to quickly see how the thread migrated across the CPU cores. It will also add lines starting with a filed circle to denote wake up events. Those are useful to pinpoint the origin of a thread waking up, especially when holding locks. It may also start from an empty region, denoting the time at which the kernel chose to schedule or boost the priority of your thread. Wake ups will have a different color based on the reason for which the thread was waiting to be scheduled. @@ -3668,7 +3668,7 @@ \subsubsection{Timeline view} In the above picture, \emph{Thread B} migrates from \emph{Core 3} to \emph{Core 4} due to a wake up from \emph{Thread A}. Then it migrates from \emph{Core 4} to \emph{Core 1}. -Clicking the \LMB{}~left mouse button on a tracked thread will make it visible on the timeline if it was either hidden or collapsed before. It will also lock the selected thread so that you may pan and explore data while retaining the visualization of thread migrations and wake up events. +Clicking the \LMB{}~left mouse button on a tracked thread will make it visible on the timeline if it was either hidden or collapsed before. It will also lock the selected thread so that you may pan and explore data while retaining the visualization of thread migrations and wake up events. Clicking again somewhere empty on the timeline with the \LMB{}~left mouse button will unlock the selection. Careful examination of the data presented on this graph may allow you to determine areas where the profiled application was fighting for system resources with other programs (see section~\ref{checkenvironmentos}) or give you a hint to add more instrumentation macros. @@ -4717,7 +4717,7 @@ \subsubsection{Model selection} There are many factors to take into consideration when choosing a model to use. First, you should determine which model family you want to use: \begin{itemize} -\item \emph{Gemma 3} (\url{https://blog.google/technology/developers/gemma-3/}) is a well rounded model that can converse in multiple languages. +\item \emph{Gemma 3} (\url{https://blog.google/technology/developers/gemma-3/}) is a well rounded model that can converse in multiple languages. \item \emph{Qwen3} (\url{https://qwenlm.github.io/blog/qwen3/}) has a more technical feeling to it, it likes to write bullet point lists. \item \emph{Mistral Small} (\url{https://mistral.ai/news/mistral-small-3-1}) may also be considered. Despite the name, it is not small. \end{itemize} diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp index 8e66975968..6e240cbc19 100644 --- a/public/TracyClient.cpp +++ b/public/TracyClient.cpp @@ -35,6 +35,10 @@ #ifdef TRACY_ROCPROF # include "client/TracyRocprof.cpp" #endif + +#ifdef TRACY_NAME_BUFFER +#include "common/TracyNameBuffer.cpp" +#endif #ifdef _MSC_VER # pragma comment(lib, "ws2_32.lib") # pragma comment(lib, "advapi32.lib") diff --git a/public/common/TracyNameBuffer.cpp b/public/common/TracyNameBuffer.cpp new file mode 100644 index 0000000000..c7181bd24b --- /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 0000000000..3cb07dc22a --- /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 848139699f..4b71128d8e 100644 --- a/public/tracy/Tracy.hpp +++ b/public/tracy/Tracy.hpp @@ -273,6 +273,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 e77c01f76c..b9ad83f297 100644 --- a/public/tracy/TracyC.h +++ b/public/tracy/TracyC.h @@ -36,6 +36,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; @@ -122,8 +124,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 @@ -384,6 +393,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 be867665ae..b3e5bc3d5e 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -11,15 +11,9 @@ 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_link_libraries(TracyClientBindings PUBLIC ${Python_LIBRARIES}) -target_compile_definitions(TracyClientBindings PUBLIC BUFFER_SIZE=${BUFFER_SIZE}) -target_compile_definitions(TracyClientBindings PUBLIC NAME_LENGTH=${NAME_LENGTH}) - if (UNIX) set_target_properties(TracyClientBindings PROPERTIES BUILD_RPATH_USE_ORIGIN TRUE diff --git a/python/bindings/Memory.hpp b/python/bindings/Memory.hpp index 9bd7b2f790..11614124d3 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 631dcf68f6..2cd0e416b5 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, int32_t 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 071010e5b1..0000000000 --- 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]; - } -};