diff --git a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt index 2d739e73..791f726d 100644 --- a/profiler/CMakeLists.txt +++ b/profiler/CMakeLists.txt @@ -212,6 +212,7 @@ Embed(PROFILER_FILES TextAnnotateZones src/achievements/AnnotateZones.md TEXT) Embed(PROFILER_FILES TextAppInfo src/achievements/AppInfo.md TEXT) Embed(PROFILER_FILES TextConnectToClient src/achievements/ConnectToClient.md TEXT) Embed(PROFILER_FILES TextContention src/achievements/Contention.md TEXT) +Embed(PROFILER_FILES TextContextSwitches src/achievements/ContextSwitches.md TEXT) Embed(PROFILER_FILES TextEntryStacks src/achievements/EntryStacks.md TEXT) Embed(PROFILER_FILES TextFindZone src/achievements/FindZone.md TEXT) Embed(PROFILER_FILES TextFrameImages src/achievements/FrameImages.md TEXT) diff --git a/profiler/src/achievements/ContextSwitches.md b/profiler/src/achievements/ContextSwitches.md new file mode 100644 index 00000000..5e0b90d1 --- /dev/null +++ b/profiler/src/achievements/ContextSwitches.md @@ -0,0 +1,14 @@ +# See the whole machine + +Your program shares the CPU cores with every other process on the machine. When another process wants to run, the scheduler can take a core away and later give your thread a different one. A thread can be ready to run and still not get a core. [Context switches](#contextswitches) capture exactly that: the kernel reports every time a thread leaves or enters a core. + +With context switch data, each thread carries a thick state line: green while it runs, red while it waits to be resumed by the scheduler, bronze just before it is resumed, and blue while it waits to continue on a different core. Hover a red region for the waiting time. A blue one shows the two cores involved. While the thread is off a core, the zones covering that time are drawn darkened. + +Above your threads, the *CPU data* lane has one row per logical CPU. Each row shows who holds that core: bright colors are threads the profiler tracks, dark blue is your application's threads it doesn't know, gray is other programs. When a thread stalls, check the row of the core it was running on. Gray at that moment means another program held the core. Empty means the core was idle: your thread was waiting on I/O, a lock, or a sleep. + +> [!TIP] +> **Tip** +> +> If there is no lane and no state lines under your threads, the client captured no system data. The collection runs inside the profiled application, so the elevated rights the kernel demands must be granted to it. Details in [privilege elevation](#privilegeelevation). + +Try finding a long red wait on a hot thread and check what the lane says about its core. diff --git a/profiler/src/profiler/TracyAchievementData.cpp b/profiler/src/profiler/TracyAchievementData.cpp index a99b4b5d..61cee7da 100644 --- a/profiler/src/profiler/TracyAchievementData.cpp +++ b/profiler/src/profiler/TracyAchievementData.cpp @@ -9,6 +9,7 @@ #include "data/TextCompareTraces.hpp" #include "data/TextConnectToClient.hpp" #include "data/TextContention.hpp" +#include "data/TextContextSwitches.hpp" #include "data/TextEntryStacks.hpp" #include "data/TextFindZone.hpp" #include "data/TextFrameImages.hpp" @@ -71,7 +72,13 @@ AchievementItem ai_entryStacks = { .text = Unpack( TextEntryStacks ) }; -AchievementItem* ac_samplingIntroItems[] = { &ai_ghostZones, &ai_samplingStats, &ai_entryStacks, nullptr }; +AchievementItem ai_contextSwitches = { + .id = "contextSwitches", + .name = "See the whole machine", + .text = Unpack( TextContextSwitches ) +}; + +AchievementItem* ac_samplingIntroItems[] = { &ai_ghostZones, &ai_samplingStats, &ai_entryStacks, &ai_contextSwitches, nullptr }; AchievementItem ai_samplingIntro = { .id = "samplingIntro", diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index d6ab01c3..e0087892 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -763,6 +763,7 @@ bool View::DrawImpl() if( m_worker.GetZoneCount() > 0 ) Achieve( "instrumentationIntro" ); if( m_worker.GetZoneCount() > 100 * 1000 * 1000 ) Achieve( "100million" ); if( m_worker.GetCallstackSampleCount() > 0 ) Achieve( "samplingIntro" ); + if( s_config.achievements && m_worker.HasContextSwitches() ) Achieve( "contextSwitches" ); if( m_worker.AreFramesUsed() ) Achieve( "instrumentFrames" ); }