Every Worker-reading Python binding except save_worker() read
m_data's zones/plots/threads/etc. without taking
Worker::ObtainLockForMainThread(), racing the live receive thread
that mutates the same structures. This matches
alandtse/tracy#2: crashes only ever hit live instances, never
file-loaded ones (no concurrent writer there), and cluster right
after a big save_trace when the receive thread is catching up on
backlog.
Routes every Worker accessor through a new locked() wrapper using
the same cooperative lock TracyView.cpp already takes once per
frame, so reads can't race live writes without introducing new
stalls on the profiled client.
Dereference of end() iterator in section category options list
(profiler/src/profiler/TracyView_Options.cpp:152-153)
The new category list iterates categories = m_worker.GetSectionDescriptions()
(the description map) and then looks up each category in sections = m_worker.
GetSections():
for( const auto& v : categories )
{
...
auto it = sections.find( v.first );
ImGui::TextDisabled( "(%s)", RealToString( it->second.size() ) );
...
}
sectionsDescription is a superset of sections's keys: ProcessSectionSetup
(server/TracyWorker.cpp:7484-7496) inserts into sectionsDescription only,
while ProcessSectionEnter (server/TracyWorker.cpp:7416-7454) inserts into
both. A category set up via TracySectionSetup that has no TracySectionEnter
events yet (or a saved trace containing such a category) will be in
sectionsDescription but not in sections. find returns end() and
it->second.size() is undefined behavior (likely crash).