mirror of
https://github.com/wolfpld/tracy.git
synced 2026-06-08 00:23:47 +00:00
Compare commits
11 Commits
fc5318dcad
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19519bbeb0 | ||
|
|
fc4f52e61d | ||
|
|
e2ac8f7973 | ||
|
|
e5aa8eba51 | ||
|
|
7437c41514 | ||
|
|
f441a5070b | ||
|
|
00b6abd67b | ||
|
|
e4e3d75eb8 | ||
|
|
7cb98245ce | ||
|
|
55d5436fb9 | ||
|
|
2b11785b05 |
@@ -137,6 +137,7 @@ set_option(TRACY_SYMBOL_OFFLINE_RESOLVE "Instead of full runtime symbol resoluti
|
||||
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 TracyClient)
|
||||
set_option(TRACY_DEBUGINFOD "Enable debuginfod support" OFF TracyClient)
|
||||
set_option(TRACY_IGNORE_MEMORY_FAULTS "Ignore instrumentation errors from memory free events that do not have a matching allocation" OFF TracyClient)
|
||||
set_option(TRACY_OPENGL_AUTO_CALIBRATION "Periodically recalibrate OpenGL GPU/CPU clock drift (forces a CPU/GPU sync each time)" OFF TracyClient)
|
||||
|
||||
# advanced
|
||||
set_option(TRACY_VERBOSE "[advanced] Verbose output from the profiler" OFF TracyClient)
|
||||
|
||||
@@ -1701,6 +1701,12 @@ logo=\bcattention
|
||||
\end{itemize}
|
||||
\end{bclogo}
|
||||
|
||||
\subparagraph{Calibrated context}
|
||||
|
||||
By default, the OpenGL context is uncalibrated: the CPU and GPU clocks are aligned only once, when the context is created, so over long captures the two time domains may drift apart (section~\ref{options} describes correcting this drift manually). Defining \texttt{TRACY\_OPENGL\_AUTO\_CALIBRATION} before including \texttt{TracyOpenGL.hpp} enables periodic recalibration instead: roughly once per second Tracy samples the GPU and CPU clocks together and emits a calibration event, allowing the profiler to track and remove the drift automatically.
|
||||
|
||||
This is opt-in because OpenGL exposes no atomic CPU+GPU timestamp query (unlike Vulkan's \texttt{VK\_EXT\_calibrated\_timestamps} or Direct3D~12, whose contexts are always calibrated). Recalibration therefore reads the GPU clock with \texttt{glGetInteger64v(GL\_TIMESTAMP)}, which forces a CPU/GPU synchronization (a pipeline stall) each time it runs. Enable it only when the improved long-capture alignment is worth the periodic stall.
|
||||
|
||||
\subsubsection{Vulkan}
|
||||
|
||||
Similarly, for Vulkan support you should include the \texttt{public/tracy/TracyVulkan.hpp} header file. Tracing Vulkan devices and queues is a bit more involved, and the Vulkan initialization macro \texttt{TracyVkContext(physdev, device, queue, cmdbuf)} returns an instance of \texttt{TracyVkCtx} object, which tracks an associated Vulkan queue. Cleanup is performed using the \texttt{TracyVkDestroy(ctx)} macro. You may create multiple Vulkan contexts. To set a custom name for the context, use the \texttt{TracyVkContextName(ctx, name, size)} macro.
|
||||
@@ -2041,6 +2047,20 @@ filesystem setup as the one used to run the tracy instrumented application).
|
||||
You can do path substitution with the \texttt{-p} option to perform any number of path
|
||||
substitions in order to use symbols located elsewhere.
|
||||
|
||||
By default symbol resolution is performed with the platform's native facility: the DbgHelp
|
||||
library on Windows, and the \texttt{addr2line} tool found in \texttt{PATH} elsewhere. You can
|
||||
override this with the \texttt{-a} option, passing the path to a custom
|
||||
\texttt{addr2line}-compatible tool (for instance an \texttt{addr2line} from a cross-compilation
|
||||
toolchain, or \texttt{llvm-addr2line}). The \texttt{-a} option works on all platforms, including
|
||||
Windows, and takes precedence over the platform default.
|
||||
|
||||
Extra arguments can be passed verbatim to the resolution tool with the \texttt{-A} option. Tracy
|
||||
records callstack frame offsets relative to the image base, but \texttt{addr2line}-compatible
|
||||
tools expect a full virtual address for images that have a non-zero preferred image base (such as
|
||||
PE on Windows or Mach-O on Apple). For these, pass \texttt{-A "--relative-address"} so that
|
||||
\texttt{llvm-addr2line} or \texttt{llvm-symbolizer} adds the image base back. ELF images need no
|
||||
such adjustment.
|
||||
|
||||
\begin{bclogo}[
|
||||
noborder=true,
|
||||
couleur=black!5,
|
||||
|
||||
@@ -135,6 +135,10 @@ if get_option('ignore_memory_faults')
|
||||
tracy_common_args += ['-DTRACY_IGNORE_MEMORY_FAULTS']
|
||||
endif
|
||||
|
||||
if get_option('opengl_auto_calibration')
|
||||
tracy_common_args += ['-DTRACY_OPENGL_AUTO_CALIBRATION']
|
||||
endif
|
||||
|
||||
tracy_shared_libs = get_option('default_library') == 'shared'
|
||||
|
||||
if tracy_shared_libs
|
||||
|
||||
@@ -29,3 +29,4 @@ option('verbose', type : 'boolean', value : false, description : 'Enable verbose
|
||||
option('no_internal_message', type : 'boolean', value : false, description : 'Prevent the profiler from logging messages')
|
||||
option('debuginfod', type : 'boolean', value : false, description : 'Enable debuginfod support')
|
||||
option('ignore_memory_faults', type : 'boolean', value : false, description : 'Ignore instrumentation errors from memory free events that do not have a matching allocation')
|
||||
option('opengl_auto_calibration', type : 'boolean', value : false, description : 'Periodically recalibrate OpenGL GPU/CPU clock drift (forces a CPU/GPU sync each time)')
|
||||
|
||||
@@ -149,6 +149,7 @@ Embed(PROFILER_FILES SystemPrompt src/llm/system.prompt.md)
|
||||
Embed(PROFILER_FILES SkillCallstack src/llm/skill.callstack.md)
|
||||
Embed(PROFILER_FILES SkillOptimization src/llm/skill.optimization.md)
|
||||
Embed(PROFILER_FILES ToolsJson src/llm/tools.json)
|
||||
|
||||
Embed(PROFILER_FILES FontFixed src/font/FiraCode-Retina.ttf)
|
||||
Embed(PROFILER_FILES FontIcons src/font/Font\ Awesome\ 7\ Free-Solid-900.otf)
|
||||
Embed(PROFILER_FILES FontNormal src/font/Roboto-Regular.ttf)
|
||||
@@ -156,8 +157,22 @@ Embed(PROFILER_FILES FontBold src/font/Roboto-Bold.ttf)
|
||||
Embed(PROFILER_FILES FontItalic src/font/Roboto-Italic.ttf)
|
||||
Embed(PROFILER_FILES FontBoldItalic src/font/Roboto-BoldItalic.ttf)
|
||||
Embed(PROFILER_FILES FontEmoji src/font/NotoEmoji-Regular.ttf)
|
||||
|
||||
Embed(PROFILER_FILES Manual ../manual/tracy.md)
|
||||
|
||||
Embed(PROFILER_FILES Text100Million src/achievements/100Million.md)
|
||||
Embed(PROFILER_FILES TextConnectToClient src/achievements/ConnectToClient.md)
|
||||
Embed(PROFILER_FILES TextFindZone src/achievements/FindZone.md)
|
||||
Embed(PROFILER_FILES TextFrameImages src/achievements/FrameImages.md)
|
||||
Embed(PROFILER_FILES TextGlobalSettings src/achievements/GlobalSettings.md)
|
||||
Embed(PROFILER_FILES TextInstrumentationIntro src/achievements/InstrumentationIntro.md)
|
||||
Embed(PROFILER_FILES TextInstrumentationStatistics src/achievements/InstrumentationStatistics.md)
|
||||
Embed(PROFILER_FILES TextInstrumentFrames src/achievements/InstrumentFrames.md)
|
||||
Embed(PROFILER_FILES TextIntro src/achievements/Intro.md)
|
||||
Embed(PROFILER_FILES TextLoadTrace src/achievements/LoadTrace.md)
|
||||
Embed(PROFILER_FILES TextSamplingIntro src/achievements/SamplingIntro.md)
|
||||
Embed(PROFILER_FILES TextSaveTrace src/achievements/SaveTrace.md)
|
||||
|
||||
set(INCLUDES "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(LIBS "")
|
||||
|
||||
|
||||
12
profiler/src/achievements/100Million.md
Normal file
12
profiler/src/achievements/100Million.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# It's over 100 million!
|
||||
|
||||
Tracy can handle a lot of data. How about 100 million zones in a single trace? Add a lot of zones to your program and see how it handles it!
|
||||
|
||||
Capturing a long-running profile trace is easy. Need to profile an hour of your program execution? You can do it.
|
||||
|
||||
Note that it doesn't make much sense to instrument every little function you might have. The cost of the instrumentation itself will be higher than the cost of the function in such a case.
|
||||
|
||||
> [!TIP]
|
||||
> Keep in mind that the more zones you have, the more memory and CPU time the profiler will use. Be careful not to run out of memory.
|
||||
>
|
||||
> To capture 100 million zones, you will need approximately 4 GB of RAM.
|
||||
10
profiler/src/achievements/ConnectToClient.md
Normal file
10
profiler/src/achievements/ConnectToClient.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# First profiling session
|
||||
|
||||
Let's start our adventure by instrumenting your application and connecting it to the profiler. Here's a quick refresher:
|
||||
|
||||
1. Integrate Tracy Profiler into your application. This can be done using CMake, Meson, or simply by adding the source files to your project.
|
||||
2. Make sure that `TracyClient.cpp` (or the Tracy library) is included in your build.
|
||||
3. Define `TRACY_ENABLE` in your build configuration, for the whole application. Do not do it in a single source file because it won't work.
|
||||
4. Start your application, and * Connect* to it with the profiler.
|
||||
|
||||
Please refer to the [user manual](https://github.com/wolfpld/tracy/releases) for more details.
|
||||
11
profiler/src/achievements/FindZone.md
Normal file
11
profiler/src/achievements/FindZone.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Find some zones
|
||||
|
||||
You can search for zones in the trace by opening the search window with the * Find zone* button on the top bar. It will ask you for the zone name, which in most cases will be the function name in the code.
|
||||
|
||||
The search may find more than one zone with the same name. A list of all the zones found is displayed, and you can select any of them.
|
||||
|
||||
Alternatively, you can open the Statistics window and click an entry there. This will open the Find zone window as if you had searched for that zone.
|
||||
|
||||
When a zone is selected, a number of statistics are displayed to help you understand the performance of your application. In addition, a histogram of the zone execution times is displayed to make it easier for you to determine the performance of the profiled code. Be sure to select a zone with a large number of calls to make the histogram look interesting!
|
||||
|
||||
Note that you can draw a range on the histogram to limit the number of entries displayed in the zone list below. This list allows you to examine each zone individually. There are also a number of zone groupings that you can select. Each group can be selected and the time associated with the selected group will be highlighted on the histogram.
|
||||
11
profiler/src/achievements/FrameImages.md
Normal file
11
profiler/src/achievements/FrameImages.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# A picture is worth a thousand words
|
||||
|
||||
Tracy allows you to add context to each frame, by attaching a screenshot. You can do this with the `FrameImage` macro.
|
||||
|
||||
You will have to do the screen capture and resizing yourself, which can be a bit complicated. The manual provides a sample code that shows how to do this in a performant way.
|
||||
|
||||
The frame images are displayed in the context of a frame, for example, when you hover over the frame in the timeline or in the frame graph at the top of the screen.
|
||||
|
||||
You can even view a recording of what your application was doing by clicking the * Tools* icon and then selecting the * Playback* option. Try it out!
|
||||
|
||||
The `FrameImage` macro is a great way to see what happened in your application at a particular time. Maybe you have a performance problem that only occurs when a certain object is on the screen?
|
||||
5
profiler/src/achievements/GlobalSettings.md
Normal file
5
profiler/src/achievements/GlobalSettings.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Global settings
|
||||
|
||||
Tracy has a variety of settings that can be adjusted to suit your needs. These settings can be found by clicking on the * Wrench* icon on the welcome screen. This will open the about window, where you can expand the * Global settings* menu.
|
||||
|
||||
The settings are saved between sessions, so you only need to set them once.
|
||||
22
profiler/src/achievements/InstrumentFrames.md
Normal file
22
profiler/src/achievements/InstrumentFrames.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Instrumenting frames
|
||||
|
||||
In addition to instrumenting functions, you can also instrument frames. This allows you to see how much time is spent in each frame of your application.
|
||||
|
||||
To instrument frames, you need to add the `FrameMark` macro at the beginning of each frame. This can be done in the main loop of your application, or in a separate function that is called at the beginning of each frame.
|
||||
|
||||
```c++
|
||||
#include "Tracy.hpp"
|
||||
|
||||
void Render()
|
||||
{
|
||||
// Render the frame
|
||||
SwapBuffers();
|
||||
FrameMark;
|
||||
}
|
||||
```
|
||||
|
||||
When you profile your application, you will see a new frame appear on the timeline each time the `FrameMark` macro is called. This allows you to see how much time is spent in each frame and how many frames are rendered per second.
|
||||
|
||||
The `FrameMark` macro is a great way to see at a glance how your application is performing over time. Maybe there are some performance problems that only appear after a few minutes of running the application? A frame graph is drawn at the top of the profiler window where you can see the timing of all frames.
|
||||
|
||||
Note that some applications do not have a frame-based structure, and in such cases, frame instrumentation may not be useful. That's ok.
|
||||
22
profiler/src/achievements/InstrumentationIntro.md
Normal file
22
profiler/src/achievements/InstrumentationIntro.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Instrumentating your application
|
||||
|
||||
Instrumentation is a powerful feature that allows you to see the exact runtime of each call to the selected set of functions. The downside is that it takes a bit of manual work to get it set up.
|
||||
|
||||
To get started, open a source file and include the `Tracy.hpp` header. This will give you access to a variety of macros provided by Tracy. Next, add the `ZoneScoped` macro to the beginning of one of your functions, like this:
|
||||
|
||||
```c++
|
||||
#include "Tracy.hpp"
|
||||
|
||||
void SomeFunction()
|
||||
{
|
||||
ZoneScoped;
|
||||
// Your code here
|
||||
}
|
||||
```
|
||||
|
||||
Now, when you profile your application, you will see a new zone appear on the timeline for each call to the function. This allows you to see how much time is spent in each call and how many times the function is called.
|
||||
|
||||
> [!NOTE]
|
||||
> The `ZoneScoped` macro is just one of the many macros provided by Tracy. See the documentation for more information.
|
||||
|
||||
The above description applies to C++ code, but things are done similarly in other programming languages. Refer to the documentation for your language for more information.
|
||||
5
profiler/src/achievements/InstrumentationStatistics.md
Normal file
5
profiler/src/achievements/InstrumentationStatistics.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Show me the stats!
|
||||
|
||||
Once you have instrumented your application, you can view the statistics for each zone in the timeline. This allows you to see how much time is spent in each zone and how many times it is called.
|
||||
|
||||
To view the statistics, click on the * Statistics* button on the top bar. This will open a new window with a list of all zones in the trace.
|
||||
12
profiler/src/achievements/Intro.md
Normal file
12
profiler/src/achievements/Intro.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Click here to discover achievements!
|
||||
|
||||
Clicking on the * Achievements* button opens the Achievements List. Here you can see the tasks to be completed along with a short description of what needs to be done.
|
||||
|
||||
As you complete each Achievement, new Achievements will appear, so be sure to keep checking the list for new ones!
|
||||
|
||||
To make the new things easier to spot, the Achievements List will show a marker next to them. The achievements * Achievements* button will glow yellow when there are new things to see.
|
||||
|
||||
- New tasks: orange
|
||||
- Completed tasks: green
|
||||
|
||||
Good luck!
|
||||
3
profiler/src/achievements/LoadTrace.md
Normal file
3
profiler/src/achievements/LoadTrace.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Load a trace
|
||||
|
||||
You can open a previously saved trace file (or one received from a friend) with the * Open saved trace* button on the welcome screen.
|
||||
10
profiler/src/achievements/SamplingIntro.md
Normal file
10
profiler/src/achievements/SamplingIntro.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Sampling program execution
|
||||
|
||||
Sampling program execution is a great way to find out where the hot spots are in your program. It can be used to find out which functions take the most time, or which lines of code are executed the most often.
|
||||
|
||||
While instrumentation requires changes to your code, sampling does not. However, because of the way it works, the results are coarser and it's not possible to know when functions are called or when they return.
|
||||
|
||||
Sampling is automatic on Linux. On Windows, you must run the profiled application as an administrator for it to work.
|
||||
|
||||
> [!WARNING]
|
||||
> Depending on your system configuration, some additional steps may be required. Please refer to the user manual for more information.
|
||||
12
profiler/src/achievements/SaveTrace.md
Normal file
12
profiler/src/achievements/SaveTrace.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Save a trace
|
||||
|
||||
Now that you have traced your application (or are in the process of doing so), you can save it to disk for future reference. You can do this by clicking on the * Connection* icon in the top left corner of the screen and then clicking on the * Save trace* button.
|
||||
|
||||
Keeping old traces on hand can be beneficial, as you can compare the performance of your optimizations with what you had before.
|
||||
|
||||
You can also share the trace with your friends or co-workers by sending them the trace file.
|
||||
|
||||
> [!WARNING]
|
||||
> **Warning**
|
||||
>
|
||||
> Trace files can contain sensitive information about your application, such as program code, or even the contents of source files. Be careful when sharing them with others.
|
||||
@@ -1466,9 +1466,17 @@ Would you like to enable achievements?
|
||||
{
|
||||
ImGui::Columns( 2 );
|
||||
ImGui::SetColumnWidth( 0, 300 * dpiScale );
|
||||
ImGui::BeginChild( "##achievementtoc", ImVec2( 0, 0 ), ImGuiChildFlags_AlwaysUseWindowPadding );
|
||||
DrawAchievements( c->items );
|
||||
ImGui::EndChild();
|
||||
ImGui::NextColumn();
|
||||
if( s_achievementItem ) s_achievementItem->description();
|
||||
ImGui::BeginChild( "##achievementtext", ImVec2( 0, 0 ), ImGuiChildFlags_AlwaysUseWindowPadding );
|
||||
if( s_achievementItem )
|
||||
{
|
||||
tracy::Markdown md( nullptr, nullptr );
|
||||
md.Print( s_achievementItem->text.c_str(), s_achievementItem->text.size() );
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndColumns();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
@@ -1,52 +1,60 @@
|
||||
#include "IconsFontAwesome7.h"
|
||||
#include "TracyAchievements.hpp"
|
||||
#include "TracyImGui.hpp"
|
||||
#include "TracySourceContents.hpp"
|
||||
#include "TracyWeb.hpp"
|
||||
#include "../Fonts.hpp"
|
||||
#include "TracyEmbed.hpp"
|
||||
|
||||
#include "data/Text100Million.hpp"
|
||||
#include "data/TextConnectToClient.hpp"
|
||||
#include "data/TextFindZone.hpp"
|
||||
#include "data/TextFrameImages.hpp"
|
||||
#include "data/TextGlobalSettings.hpp"
|
||||
#include "data/TextInstrumentFrames.hpp"
|
||||
#include "data/TextInstrumentationIntro.hpp"
|
||||
#include "data/TextInstrumentationStatistics.hpp"
|
||||
#include "data/TextIntro.hpp"
|
||||
#include "data/TextLoadTrace.hpp"
|
||||
#include "data/TextSamplingIntro.hpp"
|
||||
#include "data/TextSaveTrace.hpp"
|
||||
|
||||
namespace tracy::data
|
||||
{
|
||||
|
||||
AchievementItem ai_samplingIntro = { "samplingIntro", "Sampling program execution", [](){
|
||||
ImGui::TextWrapped( "Sampling program execution is a great way to find out where the hot spots are in your program. It can be used to find out which functions take the most time, or which lines of code are executed the most often." );
|
||||
ImGui::TextWrapped( "While instrumentation requires changes to your code, sampling does not. However, because of the way it works, the results are coarser and it's not possible to know when functions are called or when they return." );
|
||||
ImGui::TextWrapped( "Sampling is automatic on Linux. On Windows, you must run the profiled application as an administrator for it to work." );
|
||||
ImGui::PushFont( g_fonts.normal, FontSmall );
|
||||
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
|
||||
ImGui::TextWrapped( "Depending on your system configuration, some additional steps may be required. Please refer to the user manual for more information." );
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopFont();
|
||||
} };
|
||||
static std::string UnpackImpl( size_t size, size_t lz4Size, const uint8_t* data )
|
||||
{
|
||||
std::string ret;
|
||||
const EmbedData unembed( size, lz4Size, data );
|
||||
ret.assign( unembed.data(), unembed.size() );
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define Unpack( name ) UnpackImpl( Embed::name##Size, Embed::name##Lz4Size, Embed::name##Data )
|
||||
|
||||
|
||||
AchievementItem ai_samplingIntro = {
|
||||
.id = "samplingIntro",
|
||||
.name = "Sampling program execution",
|
||||
.text = Unpack( TextSamplingIntro ),
|
||||
};
|
||||
|
||||
AchievementItem* ac_samplingItems[] = { &ai_samplingIntro, nullptr };
|
||||
AchievementCategory ac_sampling = { "sampling", "Sampling", ac_samplingItems };
|
||||
|
||||
|
||||
AchievementItem ai_100million = { "100million", "It's over 100 million!", [](){
|
||||
ImGui::TextWrapped( "Tracy can handle a lot of data. How about 100 million zones in a single trace? Add a lot of zones to your program and see how it handles it!" );
|
||||
ImGui::TextWrapped( "Capturing a long-running profile trace is easy. Need to profile an hour of your program execution? You can do it." );
|
||||
ImGui::TextWrapped( "Note that it doesn't make much sense to instrument every little function you might have. The cost of the instrumentation itself will be higher than the cost of the function in such a case." );
|
||||
ImGui::PushFont( g_fonts.normal, FontSmall );
|
||||
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
|
||||
ImGui::TextWrapped( "Keep in mind that the more zones you have, the more memory and CPU time the profiler will use. Be careful not to run out of memory." );
|
||||
ImGui::TextWrapped( "To capture 100 million zones, you will need approximately 4 GB of RAM." );
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopFont();
|
||||
} };
|
||||
AchievementItem ai_100million = {
|
||||
.id = "100million",
|
||||
.name = "It's over 100 million!",
|
||||
.text = Unpack( Text100Million )
|
||||
};
|
||||
|
||||
AchievementItem ai_instrumentationStatistics = { "instrumentationStatistics", "Show me the stats!", [](){
|
||||
ImGui::TextWrapped( "Once you have instrumented your application, you can view the statistics for each zone in the timeline. This allows you to see how much time is spent in each zone and how many times it is called." );
|
||||
ImGui::TextWrapped( "To view the statistics, click on the \"" ICON_FA_ARROW_UP_WIDE_SHORT " Statistics\" button on the top bar. This will open a new window with a list of all zones in the trace." );
|
||||
} };
|
||||
AchievementItem ai_instrumentationStatistics = {
|
||||
.id = "instrumentationStatistics",
|
||||
.name = "Show me the stats!",
|
||||
.text = Unpack( TextInstrumentationStatistics )
|
||||
};
|
||||
|
||||
AchievementItem ai_findZone = { "findZone", "Find some zones", [](){
|
||||
ImGui::TextWrapped( "You can search for zones in the trace by opening the search window with the \"" ICON_FA_MAGNIFYING_GLASS " Find zone\" button on the top bar. It will ask you for the zone name, which in most cases will be the function name in the code." );
|
||||
ImGui::TextWrapped( "The search may find more than one zone with the same name. A list of all the zones found is displayed, and you can select any of them." );
|
||||
ImGui::TextWrapped( "Alternatively, you can open the Statistics window and click an entry there. This will open the Find zone window as if you had searched for that zone." );
|
||||
ImGui::TextWrapped( "When a zone is selected, a number of statistics are displayed to help you understand the performance of your application. In addition, a histogram of the zone execution times is displayed to make it easier for you to determine the performance of the profiled code. Be sure to select a zone with a large number of calls to make the histogram look interesting!" );
|
||||
ImGui::TextWrapped( "Note that you can draw a range on the histogram to limit the number of entries displayed in the zone list below. This list allows you to examine each zone individually. There are also a number of zone groupings that you can select. Each group can be selected and the time associated with the selected group will be highlighted on the histogram." );
|
||||
} };
|
||||
AchievementItem ai_findZone = {
|
||||
.id = "findZone",
|
||||
.name = "Find some zones",
|
||||
.text = Unpack( TextFindZone )
|
||||
};
|
||||
|
||||
AchievementItem* ac_instrumentationIntroItems[] = {
|
||||
&ai_100million,
|
||||
@@ -55,90 +63,46 @@ AchievementItem* ac_instrumentationIntroItems[] = {
|
||||
nullptr
|
||||
};
|
||||
|
||||
AchievementItem ai_instrumentationIntro = { "instrumentationIntro", "Instrumentating your application", [](){
|
||||
constexpr const char* src = R"(#include "Tracy.hpp"
|
||||
AchievementItem ai_instrumentationIntro = {
|
||||
.id = "instrumentationIntro",
|
||||
.name = "Instrumentating your application",
|
||||
.text = Unpack( TextInstrumentationIntro ),
|
||||
.items = ac_instrumentationIntroItems
|
||||
};
|
||||
|
||||
void SomeFunction()
|
||||
{
|
||||
ZoneScoped;
|
||||
// Your code here
|
||||
}
|
||||
)";
|
||||
|
||||
static SourceContents sc;
|
||||
sc.Parse( src );
|
||||
|
||||
ImGui::TextWrapped( "Instrumentation is a powerful feature that allows you to see the exact runtime of each call to the selected set of functions. The downside is that it takes a bit of manual work to get it set up." );
|
||||
ImGui::TextWrapped( "To get started, open a source file and include the Tracy.hpp header. This will give you access to a variety of macros provided by Tracy. Next, add the ZoneScoped macro to the beginning of one of your functions, like this:" );
|
||||
ImGui::PushFont( g_fonts.mono, FontNormal );
|
||||
PrintSource( sc.get() );
|
||||
ImGui::PopFont();
|
||||
ImGui::TextWrapped( "Now, when you profile your application, you will see a new zone appear on the timeline for each call to the function. This allows you to see how much time is spent in each call and how many times the function is called." );
|
||||
ImGui::PushFont( g_fonts.normal, FontSmall );
|
||||
ImGui::PushStyleColor( ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled] );
|
||||
ImGui::TextWrapped( "Note: The ZoneScoped macro is just one of the many macros provided by Tracy. See the documentation for more information." );
|
||||
ImGui::TextWrapped( "The above description applies to C++ code, but things are done similarly in other programming languages. Refer to the documentation for your language for more information." );
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopFont();
|
||||
}, ac_instrumentationIntroItems };
|
||||
|
||||
AchievementItem ai_frameImages = { "frameImages", "A picture is worth a thousand words", [](){
|
||||
ImGui::TextWrapped( "Tracy allows you to add context to each frame, by attaching a screenshot. You can do this with the FrameImage macro." );
|
||||
ImGui::TextWrapped( "You will have to do the screen capture and resizing yourself, which can be a bit complicated. The manual provides a sample code that shows how to do this in a performant way." );
|
||||
ImGui::TextWrapped( "The frame images are displayed in the context of a frame, for example, when you hover over the frame in the timeline or in the frame graph at the top of the screen." );
|
||||
ImGui::TextWrapped( "You can even view a recording of what your application was doing by clicking the " ICON_FA_SCREWDRIVER_WRENCH " icon and then selecting the \"" ICON_FA_PLAY " Playback\" option. Try it out!" );
|
||||
ImGui::TextWrapped( "The FrameImage macro is a great way to see what happened in your application at a particular time. Maybe you have a performance problem that only occurs when a certain object is on the screen?" );
|
||||
} };
|
||||
AchievementItem ai_frameImages = {
|
||||
.id = "frameImages",
|
||||
.name = "A picture is worth a thousand words",
|
||||
.text = Unpack( TextFrameImages )
|
||||
};
|
||||
|
||||
AchievementItem* ac_instrumentFramesItems[] = {
|
||||
&ai_frameImages,
|
||||
nullptr
|
||||
};
|
||||
|
||||
AchievementItem ai_instrumentFrames = { "instrumentFrames", "Instrumenting frames", [](){
|
||||
constexpr const char* src = R"(#include "Tracy.hpp"
|
||||
|
||||
void Render()
|
||||
{
|
||||
// Render the frame
|
||||
SwapBuffers();
|
||||
FrameMark;
|
||||
}
|
||||
)";
|
||||
|
||||
static SourceContents sc;
|
||||
sc.Parse( src );
|
||||
|
||||
ImGui::TextWrapped( "In addition to instrumenting functions, you can also instrument frames. This allows you to see how much time is spent in each frame of your application." );
|
||||
ImGui::TextWrapped( "To instrument frames, you need to add the FrameMark macro at the beginning of each frame. This can be done in the main loop of your application, or in a separate function that is called at the beginning of each frame." );
|
||||
ImGui::PushFont( g_fonts.mono, FontNormal );
|
||||
PrintSource( sc.get() );
|
||||
ImGui::PopFont();
|
||||
ImGui::TextWrapped( "When you profile your application, you will see a new frame appear on the timeline each time the FrameMark macro is called. This allows you to see how much time is spent in each frame and how many frames are rendered per second." );
|
||||
ImGui::TextWrapped( "The FrameMark macro is a great way to see at a glance how your application is performing over time. Maybe there are some performance problems that only appear after a few minutes of running the application? A frame graph is drawn at the top of the profiler window where you can see the timing of all frames." );
|
||||
ImGui::TextWrapped( "Note that some applications do not have a frame-based structure, and in such cases, frame instrumentation may not be useful. That's ok." );
|
||||
}, ac_instrumentFramesItems };
|
||||
AchievementItem ai_instrumentFrames = {
|
||||
.id = "instrumentFrames",
|
||||
.name = "Instrumenting frames",
|
||||
.text = Unpack( TextInstrumentFrames ),
|
||||
.items = ac_instrumentFramesItems
|
||||
};
|
||||
|
||||
AchievementItem* ac_instrumentationItems[] = { &ai_instrumentationIntro, &ai_instrumentFrames, nullptr };
|
||||
AchievementCategory ac_instrumentation = { "instrumentation", "Instrumentation", ac_instrumentationItems };
|
||||
|
||||
|
||||
AchievementItem ai_loadTrace = { "loadTrace", "Load a trace", [](){
|
||||
ImGui::TextWrapped( "You can open a previously saved trace file (or one received from a friend) with the \"" ICON_FA_FOLDER_OPEN " Open saved trace\" button on the welcome screen." );
|
||||
} };
|
||||
AchievementItem ai_loadTrace = {
|
||||
.id = "loadTrace",
|
||||
.name = "Load a trace",
|
||||
.text = Unpack( TextLoadTrace )
|
||||
};
|
||||
|
||||
AchievementItem ai_saveTrace = { "saveTrace", "Save a trace", [](){
|
||||
ImGui::TextWrapped( "Now that you have traced your application (or are in the process of doing so), you can save it to disk for future reference. You can do this by clicking on the " ICON_FA_WIFI " icon in the top left corner of the screen and then clicking on the \"" ICON_FA_FLOPPY_DISK " Save trace\" button." );
|
||||
ImGui::TextWrapped( "Keeping old traces on hand can be beneficial, as you can compare the performance of your optimizations with what you had before." );
|
||||
ImGui::TextWrapped( "You can also share the trace with your friends or co-workers by sending them the trace file." );
|
||||
ImGui::Spacing();
|
||||
tracy::TextColoredUnformatted( 0xFF44FFFF, ICON_FA_TRIANGLE_EXCLAMATION );
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted( "Warning" );
|
||||
ImGui::SameLine();
|
||||
tracy::TextColoredUnformatted( 0xFF44FFFF, ICON_FA_TRIANGLE_EXCLAMATION );
|
||||
ImGui::TextWrapped( "Trace files can contain sensitive information about your application, such as program code, or even the contents of source files. Be careful when sharing them with others." );
|
||||
} };
|
||||
AchievementItem ai_saveTrace = {
|
||||
.id = "saveTrace",
|
||||
.name = "Save a trace",
|
||||
.text = Unpack( TextSaveTrace )
|
||||
};
|
||||
|
||||
AchievementItem* ac_connectToServerItems[] = {
|
||||
&ai_saveTrace,
|
||||
@@ -152,23 +116,19 @@ AchievementItem* ac_connectToServerUnlock[] = {
|
||||
nullptr
|
||||
};
|
||||
|
||||
AchievementItem ai_connectToServer = { "connectToClient", "First profiling session", [](){
|
||||
ImGui::TextWrapped( "Let's start our adventure by instrumenting your application and connecting it to the profiler. Here's a quick refresher:" );
|
||||
ImGui::TextWrapped( " 1. Integrate Tracy Profiler into your application. This can be done using CMake, Meson, or simply by adding the source files to your project." );
|
||||
ImGui::TextWrapped( " 2. Make sure that TracyClient.cpp (or the Tracy library) is included in your build." );
|
||||
ImGui::TextWrapped( " 3. Define TRACY_ENABLE in your build configuration, for the whole application. Do not do it in a single source file because it won't work." );
|
||||
ImGui::TextWrapped( " 4. Start your application, and \"" ICON_FA_WIFI " Connect\" to it with the profiler." );
|
||||
ImGui::TextWrapped( "Please refer to the user manual for more details." );
|
||||
if( ImGui::SmallButton( "Download the user manual" ) )
|
||||
{
|
||||
tracy::OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
|
||||
}
|
||||
}, ac_connectToServerItems, ac_connectToServerUnlock };
|
||||
AchievementItem ai_connectToServer = {
|
||||
.id = "connectToClient",
|
||||
.name = "First profiling session",
|
||||
.text = Unpack( TextConnectToClient ),
|
||||
.items = ac_connectToServerItems,
|
||||
.unlocks = ac_connectToServerUnlock
|
||||
};
|
||||
|
||||
AchievementItem ai_globalSettings = { "globalSettings", "Global settings", [](){
|
||||
ImGui::TextWrapped( "Tracy has a variety of settings that can be adjusted to suit your needs. These settings can be found by clicking on the " ICON_FA_WRENCH " icon on the welcome screen. This will open the about window, where you can expand the \"" ICON_FA_TOOLBOX " Global settings\" menu." );
|
||||
ImGui::TextWrapped( "The settings are saved between sessions, so you only need to set them once." );
|
||||
} };
|
||||
AchievementItem ai_globalSettings = {
|
||||
.id = "globalSettings",
|
||||
.name = "Global settings",
|
||||
.text = Unpack( TextGlobalSettings )
|
||||
};
|
||||
|
||||
AchievementItem* ac_achievementsIntroItems[] = {
|
||||
&ai_connectToServer,
|
||||
@@ -176,18 +136,14 @@ AchievementItem* ac_achievementsIntroItems[] = {
|
||||
nullptr
|
||||
};
|
||||
|
||||
AchievementItem ai_achievementsIntro = { "achievementsIntro", "Click here to discover achievements!", [](){
|
||||
ImGui::TextWrapped( "Clicking on the " ICON_FA_STAR " button opens the Achievements List. Here you can see the tasks to be completed along with a short description of what needs to be done." );
|
||||
ImGui::TextWrapped( "As you complete each Achievement, new Achievements will appear, so be sure to keep checking the list for new ones!" );
|
||||
ImGui::TextWrapped( "To make the new things easier to spot, the Achievements List will show a marker next to them. The achievements " ICON_FA_STAR " button will glow yellow when there are new things to see." );
|
||||
ImGui::TextUnformatted( "New tasks:" );
|
||||
ImGui::SameLine();
|
||||
TextColoredUnformatted( 0xFF4488FF, ICON_FA_CIRCLE_EXCLAMATION );
|
||||
ImGui::TextUnformatted( "Completed tasks:" );
|
||||
ImGui::SameLine();
|
||||
TextColoredUnformatted( 0xFF44FF44, ICON_FA_CIRCLE_CHECK );
|
||||
ImGui::TextWrapped( "Good luck!" );
|
||||
}, ac_achievementsIntroItems, nullptr, true, 1 };
|
||||
AchievementItem ai_achievementsIntro = {
|
||||
.id = "achievementsIntro",
|
||||
.name = "Click here to discover achievements!",
|
||||
.text = Unpack( TextIntro ),
|
||||
.items = ac_achievementsIntroItems,
|
||||
.keepOpen = true,
|
||||
.unlockTime = 1
|
||||
};
|
||||
|
||||
AchievementItem* ac_firstStepsItems[] = { &ai_achievementsIntro, nullptr };
|
||||
AchievementCategory ac_firstSteps = { "firstSteps", "First steps", ac_firstStepsItems, 1 };
|
||||
|
||||
@@ -20,7 +20,7 @@ struct AchievementItem
|
||||
{
|
||||
const char* id;
|
||||
const char* name;
|
||||
void(*description)();
|
||||
std::string text;
|
||||
AchievementItem** items;
|
||||
AchievementItem** unlocks;
|
||||
bool keepOpen;
|
||||
|
||||
@@ -25,6 +25,8 @@ void View::DrawManual()
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
TextDisabledUnformatted( "This user manual is missing features. See the PDF file for the proper version." );
|
||||
ImGui::SameLine();
|
||||
if( ImGui::Button( ICON_FA_BOOK " PDF Manual" ) ) OpenWebpage( "https://github.com/wolfpld/tracy/releases" );
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::BeginChild( "##usermanual" );
|
||||
|
||||
@@ -34,6 +34,9 @@ public:
|
||||
#include <atomic>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef TRACY_OPENGL_AUTO_CALIBRATION
|
||||
# include <chrono>
|
||||
#endif
|
||||
|
||||
#include "Tracy.hpp"
|
||||
#include "../client/TracyProfiler.hpp"
|
||||
@@ -106,6 +109,14 @@ public:
|
||||
GLint bits;
|
||||
glGetQueryiv( GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &bits );
|
||||
|
||||
#ifdef TRACY_OPENGL_AUTO_CALIBRATION
|
||||
// The anchor above is never refreshed; advertise calibration and emit periodic
|
||||
// GpuCalibration events to correct CPU/GPU drift (see Recalibrate). Opt-in,
|
||||
// because Recalibrate() calls glGetInteger64v( GL_TIMESTAMP ), which forces a
|
||||
// CPU/GPU sync.
|
||||
m_prevCalibration = GetHostTimeNs();
|
||||
#endif
|
||||
|
||||
const float period = 1.f;
|
||||
const auto thread = GetThreadHandle();
|
||||
TracyLfqPrepare( QueueType::GpuNewContext );
|
||||
@@ -114,7 +125,11 @@ public:
|
||||
MemWrite( &item->gpuNewContext.thread, thread );
|
||||
MemWrite( &item->gpuNewContext.period, period );
|
||||
MemWrite( &item->gpuNewContext.context, m_context );
|
||||
#ifdef TRACY_OPENGL_AUTO_CALIBRATION
|
||||
MemWrite( &item->gpuNewContext.flags, GpuContextFlags( GpuContextCalibration ) );
|
||||
#else
|
||||
MemWrite( &item->gpuNewContext.flags, GpuContextFlags( 0 ) );
|
||||
#endif
|
||||
MemWrite( &item->gpuNewContext.type, GpuContextType::OpenGl );
|
||||
|
||||
#ifdef TRACY_ON_DEMAND
|
||||
@@ -143,8 +158,6 @@ public:
|
||||
{
|
||||
ZoneScopedC( Color::Red4 );
|
||||
|
||||
if( m_tail == m_head ) return;
|
||||
|
||||
#ifdef TRACY_ON_DEMAND
|
||||
if( !GetProfiler().IsConnected() )
|
||||
{
|
||||
@@ -153,6 +166,14 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TRACY_OPENGL_AUTO_CALIBRATION
|
||||
// Before the drain's early-returns, so it runs even on frames with no
|
||||
// completed queries.
|
||||
Recalibrate();
|
||||
#endif
|
||||
|
||||
if( m_tail == m_head ) return;
|
||||
|
||||
while( m_tail != m_head )
|
||||
{
|
||||
GLint available;
|
||||
@@ -173,6 +194,38 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef TRACY_OPENGL_AUTO_CALIBRATION
|
||||
// Monotonic host ns for the inter-calibration interval (cpuDelta), kept
|
||||
// separate from Profiler::GetTime() as in the D3D12/Vulkan backends.
|
||||
static tracy_force_inline int64_t GetHostTimeNs()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch() ).count();
|
||||
}
|
||||
|
||||
// OpenGL has no atomic CPU+GPU timestamp query, so sample back-to-back; the
|
||||
// gap is negligible against the recalibration interval below. Note this forces
|
||||
// a CPU/GPU sync, which is why the whole path is opt-in (TRACY_OPENGL_AUTO_CALIBRATION).
|
||||
tracy_force_inline void Recalibrate()
|
||||
{
|
||||
const int64_t hostNow = GetHostTimeNs();
|
||||
const int64_t delta = hostNow - m_prevCalibration;
|
||||
if( delta < 1000ll * 1000 * 1000 ) return; // throttle: ~once per second
|
||||
|
||||
int64_t tgpu;
|
||||
glGetInteger64v( GL_TIMESTAMP, &tgpu );
|
||||
const int64_t refCpu = Profiler::GetTime();
|
||||
m_prevCalibration = hostNow;
|
||||
|
||||
TracyLfqPrepare( QueueType::GpuCalibration );
|
||||
MemWrite( &item->gpuCalibration.gpuTime, tgpu );
|
||||
MemWrite( &item->gpuCalibration.cpuTime, refCpu );
|
||||
MemWrite( &item->gpuCalibration.cpuDelta, delta );
|
||||
MemWrite( &item->gpuCalibration.context, m_context );
|
||||
TracyLfqCommit;
|
||||
}
|
||||
#endif
|
||||
|
||||
tracy_force_inline unsigned int NextQueryId()
|
||||
{
|
||||
const auto id = m_head;
|
||||
@@ -196,6 +249,10 @@ private:
|
||||
|
||||
unsigned int m_head;
|
||||
unsigned int m_tail;
|
||||
|
||||
#ifdef TRACY_OPENGL_AUTO_CALIBRATION
|
||||
int64_t m_prevCalibration; // host-ns timestamp of the last emitted calibration
|
||||
#endif
|
||||
};
|
||||
|
||||
class GpuCtxScope
|
||||
|
||||
@@ -11,6 +11,22 @@
|
||||
|
||||
#include "OfflineSymbolResolver.h"
|
||||
|
||||
bool ResolveSymbols( const std::string& addr2lineToolPath, const std::string& addr2lineArgs,
|
||||
const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// On Windows the default (no custom tool given) is the DbgHelp backend.
|
||||
if( addr2lineToolPath.empty() )
|
||||
{
|
||||
return ResolveSymbolsDbgHelp( imagePath, inputEntryList, resolvedEntries );
|
||||
}
|
||||
#endif
|
||||
// Everywhere else, and whenever a custom tool is given, use the addr2line-compatible backend.
|
||||
// An empty path lets that backend fall back to the 'addr2line' found in PATH.
|
||||
return ResolveSymbolsAddr2Line( addr2lineToolPath, addr2lineArgs, imagePath, inputEntryList, resolvedEntries );
|
||||
}
|
||||
|
||||
bool ApplyPathSubstitutions( std::string& path, const PathSubstitutionList& pathSubstitutionlist )
|
||||
{
|
||||
for( const auto& substitution : pathSubstitutionlist )
|
||||
@@ -31,7 +47,35 @@ tracy::StringIdx AddSymbolString( tracy::Worker& worker, const std::string& str
|
||||
return tracy::StringIdx( location.idx );
|
||||
}
|
||||
|
||||
bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& pathSubstitutionlist, bool verbose )
|
||||
void ResetSymbols( tracy::Worker& worker )
|
||||
{
|
||||
std::cout << "Resetting callstack frame symbols to the unresolved state..." << std::endl;
|
||||
|
||||
const tracy::StringIdx unresolvedName = AddSymbolString( worker, "[unresolved]" );
|
||||
const tracy::StringIdx unknownFile = AddSymbolString( worker, "[unknown]" );
|
||||
|
||||
uint64_t frameCount = 0;
|
||||
auto& callstackFrameMap = worker.GetCallstackFrameMap();
|
||||
for( auto it = callstackFrameMap.begin(); it != callstackFrameMap.end(); ++it )
|
||||
{
|
||||
if( !it->second ) continue;
|
||||
|
||||
tracy::CallstackFrameData& frameData = *it->second;
|
||||
for( uint8_t f = 0; f < frameData.size; f++ )
|
||||
{
|
||||
tracy::CallstackFrame& frame = frameData.data[f];
|
||||
frame.name = unresolvedName;
|
||||
frame.file = unknownFile;
|
||||
frame.line = 0;
|
||||
++frameCount;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Reset " << frameCount << " callstack frames." << std::endl;
|
||||
}
|
||||
|
||||
bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& pathSubstitutionlist,
|
||||
const std::string& addr2lineToolPath, const std::string& addr2lineArgs, bool verbose )
|
||||
{
|
||||
uint64_t callstackFrameCount = worker.GetCallstackFrameCount();
|
||||
std::string relativeSoNameMatch = "[unresolved]";
|
||||
@@ -91,7 +135,7 @@ bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& p
|
||||
}
|
||||
|
||||
SymbolEntryList resolvedEntries;
|
||||
ResolveSymbols( imagePath, entries, resolvedEntries );
|
||||
ResolveSymbols( addr2lineToolPath, addr2lineArgs, imagePath, entries, resolvedEntries );
|
||||
|
||||
if( resolvedEntries.size() != entries.size() )
|
||||
{
|
||||
@@ -131,7 +175,8 @@ bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& p
|
||||
return true;
|
||||
}
|
||||
|
||||
void PatchSymbols( tracy::Worker& worker, const std::vector<std::string>& pathSubstitutionsStrings, bool verbose )
|
||||
void PatchSymbols( tracy::Worker& worker, const std::vector<std::string>& pathSubstitutionsStrings,
|
||||
const std::string& addr2lineToolPath, const std::string& addr2lineArgs, bool verbose )
|
||||
{
|
||||
std::cout << "Resolving and patching symbols..." << std::endl;
|
||||
|
||||
@@ -160,7 +205,7 @@ void PatchSymbols( tracy::Worker& worker, const std::vector<std::string>& pathSu
|
||||
}
|
||||
}
|
||||
|
||||
if ( !PatchSymbolsWithRegex(worker, pathSubstitutionList, verbose) )
|
||||
if ( !PatchSymbolsWithRegex(worker, pathSubstitutionList, addr2lineToolPath, addr2lineArgs, verbose) )
|
||||
{
|
||||
std::cerr << "Failed to patch symbols" << std::endl;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,41 @@ struct SymbolEntry
|
||||
|
||||
using SymbolEntryList = std::vector<SymbolEntry>;
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
// Dispatches to the appropriate backend depending on the platform and whether a custom
|
||||
// addr2line-compatible tool was specified. When addr2lineToolPath is non-empty, the tool at
|
||||
// that path is invoked (on any platform); otherwise the platform default is used (DbgHelp on
|
||||
// Windows, the 'addr2line' found in PATH elsewhere). addr2lineArgs are extra arguments passed
|
||||
// verbatim to the addr2line-compatible tool (e.g. "--relative-address").
|
||||
bool ResolveSymbols( const std::string& addr2lineToolPath, const std::string& addr2lineArgs,
|
||||
const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries );
|
||||
|
||||
void PatchSymbols( tracy::Worker& worker, const std::vector<std::string>& pathSubstitutionsStrings, bool verbose = false );
|
||||
// Backend invoking an addr2line-compatible tool. Available on all platforms. An empty
|
||||
// addr2lineToolPath falls back to the 'addr2line' found in PATH. addr2lineArgs are inserted
|
||||
// verbatim into the tool's command line.
|
||||
bool ResolveSymbolsAddr2Line( const std::string& addr2lineToolPath, const std::string& addr2lineArgs,
|
||||
const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries );
|
||||
|
||||
#ifdef _WIN32
|
||||
// Backend using the Windows DbgHelp library.
|
||||
bool ResolveSymbolsDbgHelp( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries );
|
||||
#endif
|
||||
|
||||
// Resets all callstack frame symbols back to the unresolved state ("[unresolved]" / "[unknown]"),
|
||||
// so a subsequent PatchSymbols pass re-resolves every frame. This is useful to chain several
|
||||
// resolution passes with different path substitutions. Only meaningful for traces captured with
|
||||
// TRACY_SYMBOL_OFFLINE_RESOLVE, where each frame's symAddr holds the image-relative offset.
|
||||
void ResetSymbols( tracy::Worker& worker );
|
||||
|
||||
void PatchSymbols( tracy::Worker& worker, const std::vector<std::string>& pathSubstitutionsStrings,
|
||||
const std::string& addr2lineToolPath = std::string(),
|
||||
const std::string& addr2lineArgs = std::string(), bool verbose = false );
|
||||
|
||||
using PathSubstitutionList = std::vector<std::pair<std::regex, std::string> >;
|
||||
bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& pathSubstituionlist, bool verbose = false );
|
||||
bool PatchSymbolsWithRegex( tracy::Worker& worker, const PathSubstitutionList& pathSubstituionlist,
|
||||
const std::string& addr2lineToolPath = std::string(),
|
||||
const std::string& addr2lineArgs = std::string(), bool verbose = false );
|
||||
|
||||
#endif // __SYMBOLRESOLVER_HPP__
|
||||
@@ -1,5 +1,3 @@
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "OfflineSymbolResolver.h"
|
||||
|
||||
#include <fstream>
|
||||
@@ -10,6 +8,11 @@
|
||||
#include <memory>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# define popen _popen
|
||||
# define pclose _pclose
|
||||
#endif
|
||||
|
||||
std::string ExecShellCommand( const char* cmd )
|
||||
{
|
||||
std::array<char, 128> buffer;
|
||||
@@ -29,23 +32,66 @@ std::string ExecShellCommand( const char* cmd )
|
||||
class SymbolResolver
|
||||
{
|
||||
public:
|
||||
SymbolResolver()
|
||||
SymbolResolver( const std::string& addr2lineToolPath, const std::string& addr2lineArgs )
|
||||
{
|
||||
// Extra arguments are inserted verbatim into the tool invocation. Tracy records frame
|
||||
// offsets as RVAs; for images with a non-zero preferred image base (PE, Mach-O) the user
|
||||
// can pass "--relative-address" here so llvm-addr2line / llvm-symbolizer add the base back.
|
||||
if( !addr2lineArgs.empty() )
|
||||
{
|
||||
m_addr2LineArgs = " " + addr2lineArgs;
|
||||
}
|
||||
|
||||
if( !addr2lineToolPath.empty() )
|
||||
{
|
||||
// If the value looks like a path (not a bare command name resolved via PATH), verify
|
||||
// it exists so a wrong path fails with an actionable error instead of a cryptic shell one.
|
||||
const bool looksLikePath = addr2lineToolPath.find( '/' ) != std::string::npos ||
|
||||
addr2lineToolPath.find( '\\' ) != std::string::npos;
|
||||
if( looksLikePath && !std::ifstream( addr2lineToolPath ).good() )
|
||||
{
|
||||
std::cerr << "Specified symbol resolution tool not found: '" << addr2lineToolPath
|
||||
<< "' (check the path passed to the '-a' option)" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// A user-provided path may contain spaces or other shell-special characters.
|
||||
escapeShellParam( addr2lineToolPath, m_addr2LinePath );
|
||||
std::cout << "Using user-specified symbol resolution tool: '" << addr2lineToolPath.c_str() << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::cerr << "No symbol resolution tool specified (use the '-a' option to provide one)" << std::endl;
|
||||
#else
|
||||
std::stringstream result( ExecShellCommand("which addr2line") );
|
||||
std::getline(result, m_addr2LinePath);
|
||||
|
||||
if( !m_addr2LinePath.length() )
|
||||
{
|
||||
std::cerr << "'addr2line' was not found in the system, please installed it" << std::endl;
|
||||
std::cerr << "'addr2line' was not found in the system, please install it" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Using 'addr2line' found at: '" << m_addr2LinePath.c_str() << "'" << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void escapeShellParam(std::string const& s, std::string& out)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// cmd.exe / the CRT command parser do not understand POSIX backslash escapes, and
|
||||
// backslashes are path separators on Windows. Wrap the parameter in double quotes
|
||||
// (which handles spaces) and drop any embedded quotes, which cannot appear in a path.
|
||||
out.reserve( s.size() + 2 );
|
||||
out.push_back( '"' );
|
||||
for( char c : s )
|
||||
{
|
||||
if( c != '"' ) out.push_back( c );
|
||||
}
|
||||
out.push_back( '"' );
|
||||
#else
|
||||
out.reserve( s.size() + 2 );
|
||||
out.push_back( '"' );
|
||||
for( unsigned char c : s )
|
||||
@@ -73,34 +119,51 @@ public:
|
||||
}
|
||||
}
|
||||
out.push_back( '"' );
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
if( !m_addr2LinePath.length() ) return false;
|
||||
|
||||
|
||||
std:: string escapedPath;
|
||||
escapeShellParam( imagePath, escapedPath );
|
||||
|
||||
// Command-line length limits: cmd.exe (used by _popen on Windows) allows ~8191 characters;
|
||||
// a single POSIX 'sh -c' argument is capped by MAX_ARG_STRLEN (128 KiB on Linux).
|
||||
// 8000 stays under all of these, so a single conservative budget works on every platform.
|
||||
const size_t maxCmdLength = 8000;
|
||||
|
||||
size_t entryIdx = 0;
|
||||
while( entryIdx < inputEntryList.size() )
|
||||
{
|
||||
const size_t startIdx = entryIdx;
|
||||
const size_t batchEndIdx = std::min( inputEntryList.size(), startIdx + (size_t)1024 );
|
||||
|
||||
printf( "Resolving symbols [%zu-%zu]\n", startIdx, batchEndIdx );
|
||||
|
||||
// generate a single addr2line cmd line for all addresses in one invocation
|
||||
// generate a single addr2line cmd line for as many addresses as fit the length budget
|
||||
std::stringstream ss;
|
||||
ss << m_addr2LinePath << " -C -f -e " << escapedPath << " -a ";
|
||||
for( ; entryIdx < batchEndIdx; entryIdx++ )
|
||||
ss << m_addr2LinePath << " -C -f" << m_addr2LineArgs << " -e " << escapedPath << " -a ";
|
||||
while( entryIdx < inputEntryList.size() )
|
||||
{
|
||||
const FrameEntry& entry = inputEntryList[entryIdx];
|
||||
ss << " 0x" << std::hex << entry.symbolOffset;
|
||||
entryIdx++;
|
||||
// always include at least one address, then stop once near the length limit
|
||||
if( static_cast<size_t>( ss.tellp() ) >= maxCmdLength ) break;
|
||||
}
|
||||
const size_t batchEndIdx = entryIdx;
|
||||
|
||||
std::string resultStr = ExecShellCommand( ss.str().c_str() );
|
||||
printf( "Resolving symbols [%zu-%zu]\n", startIdx, batchEndIdx );
|
||||
|
||||
std::string cmd = ss.str();
|
||||
#ifdef _WIN32
|
||||
// _popen runs the command through 'cmd.exe /c', which strips the outermost pair of
|
||||
// quotes. Wrap the whole command so the quoting around the (possibly spaced) tool
|
||||
// and image paths survives.
|
||||
cmd = "\"" + cmd + "\"";
|
||||
#endif
|
||||
|
||||
std::string resultStr = ExecShellCommand( cmd.c_str() );
|
||||
std::stringstream result( resultStr );
|
||||
|
||||
//printf("executing: '%s' got '%s'\n", ss.str().c_str(), result.str().c_str());
|
||||
@@ -147,13 +210,13 @@ public:
|
||||
|
||||
private:
|
||||
std::string m_addr2LinePath;
|
||||
std::string m_addr2LineArgs;
|
||||
};
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
bool ResolveSymbolsAddr2Line( const std::string& addr2lineToolPath, const std::string& addr2lineArgs,
|
||||
const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
static SymbolResolver symbolResolver;
|
||||
static SymbolResolver symbolResolver( addr2lineToolPath, addr2lineArgs );
|
||||
return symbolResolver.ResolveSymbols( imagePath, inputEntryList, resolvedEntries );
|
||||
}
|
||||
|
||||
#endif // #ifndef _WIN32
|
||||
|
||||
@@ -122,8 +122,8 @@ private:
|
||||
|
||||
char SymbolResolver::s_symbolResolutionBuffer[symbolResolutionBufferSize];
|
||||
|
||||
bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
bool ResolveSymbolsDbgHelp( const std::string& imagePath, const FrameEntryList& inputEntryList,
|
||||
SymbolEntryList& resolvedEntries )
|
||||
{
|
||||
static SymbolResolver resolver;
|
||||
return resolver.ResolveSymbolsForModule( imagePath, inputEntryList, resolvedEntries );
|
||||
|
||||
@@ -38,7 +38,12 @@ void Usage()
|
||||
printf( " c: context switches, s: sampling data, C: symbol code, S: source cache\n" );
|
||||
printf( " -c: scan for source files missing in cache and add if found\n" );
|
||||
printf( " -r: resolve symbols and patch callstack frames\n");
|
||||
printf( " -R: reset all callstack frame symbols to unresolved (e.g. to re-run resolution)\n");
|
||||
printf( " -p: substitute symbol resolution path with an alternative: \"REGEX_MATCH;REPLACEMENT\"\n");
|
||||
printf( " -a: path to a custom addr2line-compatible tool to use for symbol resolution\n");
|
||||
printf( " -A: extra arguments passed verbatim to the symbol resolution tool,\n");
|
||||
printf( " e.g. \"--relative-address\" for llvm-addr2line on PE/Mach-O images\n");
|
||||
printf( " -v: verbose output while resolving symbols\n");
|
||||
printf( " -j: number of threads to use for compression (-1 to use all cores)\n" );
|
||||
|
||||
exit( 1 );
|
||||
@@ -61,10 +66,14 @@ int main( int argc, char** argv )
|
||||
bool buildDict = false;
|
||||
bool cacheSource = false;
|
||||
bool resolveSymbols = false;
|
||||
bool resetSymbols = false;
|
||||
std::vector<std::string> pathSubstitutions;
|
||||
std::string addr2lineToolPath;
|
||||
std::string addr2lineArgs;
|
||||
bool verboseSymbols = false;
|
||||
|
||||
int c;
|
||||
while( ( c = getopt( argc, argv, "4hez:ds:crp:j:" ) ) != -1 )
|
||||
while( ( c = getopt( argc, argv, "4hez:ds:crRp:a:A:vj:" ) ) != -1 )
|
||||
{
|
||||
switch( c )
|
||||
{
|
||||
@@ -137,9 +146,21 @@ int main( int argc, char** argv )
|
||||
case 'r':
|
||||
resolveSymbols = true;
|
||||
break;
|
||||
case 'R':
|
||||
resetSymbols = true;
|
||||
break;
|
||||
case 'p':
|
||||
pathSubstitutions.push_back(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
addr2lineToolPath = optarg;
|
||||
break;
|
||||
case 'A':
|
||||
addr2lineArgs = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verboseSymbols = true;
|
||||
break;
|
||||
case 'j':
|
||||
streams = atoi( optarg );
|
||||
break;
|
||||
@@ -171,7 +192,7 @@ int main( int argc, char** argv )
|
||||
{
|
||||
const auto t0 = std::chrono::high_resolution_clock::now();
|
||||
const bool allowBgThreads = false;
|
||||
const bool allowStringModification = resolveSymbols;
|
||||
const bool allowStringModification = resolveSymbols || resetSymbols;
|
||||
tracy::Worker worker( *f, (tracy::EventType::Type)events, allowBgThreads, allowStringModification );
|
||||
|
||||
#ifndef TRACY_NO_STATISTICS
|
||||
@@ -181,7 +202,8 @@ int main( int argc, char** argv )
|
||||
const auto t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if( cacheSource ) worker.CacheSourceFiles();
|
||||
if( resolveSymbols ) PatchSymbols( worker, pathSubstitutions );
|
||||
if( resetSymbols ) ResetSymbols( worker );
|
||||
if( resolveSymbols ) PatchSymbols( worker, pathSubstitutions, addr2lineToolPath, addr2lineArgs, verboseSymbols );
|
||||
|
||||
auto w = std::unique_ptr<tracy::FileWrite>( tracy::FileWrite::Open( output, clev, zstdLevel, streams ) );
|
||||
if( !w )
|
||||
|
||||
Reference in New Issue
Block a user