chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
320 / 338
94.7%
Branches:
224 / 303
73.9%
Functions:
41 / 42
97.6%

util/profiler.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/util/profiler.h"
3
4 #include <algorithm>
5 #include <atomic>
6 #include <chrono>
7 #include <deque>
8 #include <map>
9 #include <mutex>
10 #include <optional>
11 #include <string>
12 #include <unordered_map>
13 #include <utility>
14
15 #if defined(_WIN32)
16 #include <windows.h>
17 #elif defined(__linux__)
18 #include <sys/syscall.h>
19 #include <unistd.h>
20 #endif
21
22 #include <fmt/format.h>
23 #include <nvtx3/nvToolsExt.h>
24
25 #include "chrono_batch_render/util/environment.h"
26 #include "chrono_batch_render/util/library.h"
27 #include "chrono_batch_render/util/text.h"
28
29 namespace chrono::batch_render::profiler {
30 namespace {
31
32 struct Names {
33 std::mutex mutex;
34 std::deque<std::string> names;
35 std::unordered_map<std::string_view, uint32_t> indices;
36 };
37
38 87710 Names& GetNames() {
39
4/5
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 87696 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 14 times.
87710 static Names names;
40 87710 return names;
41 }
42
43 42 struct TrackEntry {
44 Track track;
45 uint32_t osThread = 0;
46 };
47
48 struct State {
49 std::mutex mutex;
50 Frame current;
51 std::deque<Frame> frames;
52 size_t history = 300;
53 std::vector<TrackEntry> tracks;
54 };
55
56 587 State& GetState() {
57
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 582 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
587 static State state;
58 587 return state;
59 }
60
61 std::atomic<bool> g_recording{false};
62 nvtxDomainHandle_t g_domain = nullptr;
63
64 enum RangeSink : uint8_t { kNvtxRanges = 1, kRoctxRanges = 2 };
65
66 14 struct Roctx {
67 Library library;
68 int (*push)(const char*) = nullptr;
69 int (*pop)() = nullptr;
70 int (*nameThread)(const char*) = nullptr;
71 };
72
73 thread_local uint32_t t_depth = 0;
74
75 5 uint32_t OsThread() {
76 #if defined(_WIN32)
77 return uint32_t(GetCurrentThreadId());
78 #elif defined(__linux__)
79 5 return uint32_t(syscall(SYS_gettid));
80 #else
81 return 0;
82 #endif
83 }
84
85 463 uint32_t ThreadTrack() {
86 473 thread_local const uint32_t track = [] {
87 5 State& state = GetState();
88 5 const std::lock_guard lock(state.mutex);
89
1/1
✓ Branch 1 taken 5 times.
5 const auto index = uint32_t(state.tracks.size());
90 10 state.tracks.push_back({{fmt::format("Thread {}", index), false}, OsThread()});
91 5 return index;
92
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 458 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
478 }();
93 463 return track;
94 }
95
96 36 void DropOldFrames(State& state) {
97
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 36 times.
41 while (state.frames.size() > state.history) {
98 5 state.frames.pop_front();
99 }
100 36 }
101
102 14 bool StartNvtx() {
103
2/2
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 1 time.
14 if (Environment("CHRONO_BATCH_RENDER_NVTX") == "0") {
104 return false;
105 }
106
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 nvtxInitialize(nullptr);
107
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12 times.
13 g_domain = nvtxDomainCreateA("chrono_batch_render");
108
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12 times.
13 if (g_domain == nullptr) {
109 return false;
110 }
111
112 1 State& state = GetState();
113 1 const std::lock_guard lock(state.mutex);
114
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (const TrackEntry& entry : state.tracks) {
115
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (!entry.track.gpu) {
116
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
3 nvtxNameOsThreadA(entry.osThread, entry.track.name.c_str());
117 }
118 }
119 1 return true;
120 1 }
121
122 545641 bool NvtxActive() {
123
4/5
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 545627 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 14 times.
545641 static const bool active = StartNvtx();
124 545641 return active;
125 }
126
127 14 std::optional<Roctx> StartRoctx() {
128 14 const std::string wanted = Environment("CHRONO_BATCH_RENDER_ROCTX");
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (wanted == "0") {
130 ✗ return std::nullopt;
131 }
132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 Roctx roctx;
133 14 roctx.library =
134 14 wanted == "1"
135
3/8
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 14 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
28 ? Library::Load({"librocprofiler-sdk-roctx.so.1", "libroctx64.so.4", "libroctx64.so"})
136 14 : Library::Find({"librocprofiler-sdk-roctx.so.1", "libroctx64.so.4", "libroctx64.so"});
137
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
14 if (!roctx.library || !roctx.library.Symbol("roctxRangePushA", roctx.push) ||
138
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ !roctx.library.Symbol("roctxRangePop", roctx.pop)) {
139 14 return std::nullopt;
140 }
141
0/1
✗ Branch 1 not taken.
✗ roctx.library.Symbol("roctxNameOsThread", roctx.nameThread);
142 ✗ return roctx;
143 28 }
144
145 545641 const Roctx* RoctxActive() {
146
4/5
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 545627 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 14 times.
545641 static const std::optional<Roctx> active = StartRoctx();
147
1/2
✓ Branch 0 taken 545641 times.
✗ Branch 1 not taken.
1091282 return active ? &*active : nullptr;
148 }
149
150 ✗ void PushRoctx(const Roctx& roctx, ZoneId zone) {
151
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ thread_local std::vector<std::string> names;
152
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (zone.Index() >= names.size()) {
153 ✗ names.resize(zone.Index() + 1);
154 }
155
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ std::string& name = names[zone.Index()];
156
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (name.empty()) {
157
0/1
✗ Branch 3 not taken.
✗ name = "chrono:" + std::string(zone.Name());
158 }
159 ✗ roctx.push(name.c_str());
160 ✗ }
161
162 2 void PushRange(ZoneId zone) {
163
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 thread_local std::vector<nvtxStringHandle_t> registered;
164
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (zone.Index() >= registered.size()) {
165 1 registered.resize(zone.Index() + 1);
166 }
167
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 nvtxStringHandle_t& handle = registered[zone.Index()];
168
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (handle == nullptr) {
169
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
2 handle = nvtxDomainRegisterStringA(g_domain, std::string(zone.Name()).c_str());
170 }
171
172 2 nvtxEventAttributes_t attributes{};
173 2 attributes.version = NVTX_VERSION;
174 2 attributes.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
175 2 attributes.messageType = NVTX_MESSAGE_TYPE_REGISTERED;
176 2 attributes.message.registered = handle;
177
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 nvtxDomainRangePushEx(g_domain, &attributes);
178 2 }
179
180 17 double DurationMs(uint64_t startNs, uint64_t endNs) {
181 17 return double(endNs - startNs) * 1e-6;
182 }
183
184 5 void Gather(std::vector<ReportNode>& roots, std::vector<const Zone*> zones) {
185 5 std::ranges::sort(zones, [](const Zone* a, const Zone* b) {
186
3/24
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✓ Branch 22 taken 2 times.
✓ Branch 23 taken 2 times.
6 return a->startNs != b->startNs ? a->startNs < b->startNs : a->depth < b->depth;
187 });
188
189 5 std::vector<ReportNode*> path;
190
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
14 for (const Zone* zone : zones) {
191
3/3
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 9 times.
10 path.resize(std::min<size_t>(zone->depth, path.size()));
192
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 std::vector<ReportNode>& siblings = path.empty() ? roots : path.back()->children;
193
1/1
✓ Branch 1 taken 9 times.
9 const std::string_view name = zone->id.Name();
194 9 auto node = std::ranges::find(siblings, name, &ReportNode::name);
195
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 if (node == siblings.end()) {
196
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 siblings.emplace_back().name = name;
197 5 node = siblings.end() - 1;
198 }
199
200 9 const double ms = DurationMs(zone->startNs, zone->endNs);
201
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
9 node->minMs = node->calls == 0 ? ms : std::min(node->minMs, ms);
202
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 node->maxMs = std::max(node->maxMs, ms);
203 9 node->ms += ms;
204 9 node->calls += 1;
205
1/1
✓ Branch 1 taken 9 times.
9 path.push_back(&*node);
206 }
207 5 }
208
209 8 void Average(std::vector<ReportNode>& nodes, double frames, double frameMs) {
210
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
13 for (ReportNode& node : nodes) {
211 5 node.ms /= frames;
212 5 node.calls /= frames;
213
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
5 node.percent = frameMs > 0 ? 100 * node.ms / frameMs : 0;
214 5 Average(node.children, frames, frameMs);
215 }
216 8 }
217
218 5 std::string TrackName(const std::vector<Track>& tracks, uint32_t index) {
219
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 return index < tracks.size() ? tracks[index].name : fmt::format("Track {}", index);
220 }
221
222 5 bool IsGpuTrack(const std::vector<Track>& tracks, uint32_t index) {
223
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2 times.
5 return index < tracks.size() && tracks[index].gpu;
224 }
225
226 8 void AddLines(std::string& text, const std::vector<ReportNode>& nodes, const ReportFilter& filter,
227 uint32_t depth) {
228 8 std::vector<const ReportNode*> order;
229
1/1
✓ Branch 1 taken 8 times.
8 order.reserve(nodes.size());
230
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 8 times.
15 for (const ReportNode& node : nodes) {
231
1/1
✓ Branch 1 taken 7 times.
7 order.push_back(&node);
232 }
233
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (filter.byTime) {
234 2 std::ranges::stable_sort(
235
2/18
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 time.
✗ Branch 17 not taken.
2 order, [](const ReportNode* a, const ReportNode* b) { return a->ms > b->ms; });
236 }
237
238
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 8 times.
15 for (const ReportNode* node : order) {
239
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
7 if (node->ms < filter.minMs) {
240 1 continue;
241 }
242
1/1
✓ Branch 1 taken 6 times.
12 text += fmt::format("{:<56} {:12.3f} {:9.2f}%\n",
243
1/1
✓ Branch 1 taken 6 times.
12 std::string(size_t(2) * (depth + 1), ' ') + node->name, node->ms,
244
1/1
✓ Branch 1 taken 6 times.
12 node->percent);
245
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (depth + 1 < filter.depth) {
246
1/1
✓ Branch 1 taken 4 times.
4 AddLines(text, node->children, filter, depth + 1);
247 }
248 }
249 8 }
250
251 14 std::string Quote(std::string_view text) {
252 14 std::string quoted = "\"";
253
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 14 times.
91 for (const char c : text) {
254
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 75 times.
77 if (c == '"' || c == '\\') {
255
1/1
✓ Branch 1 taken 2 times.
2 quoted += '\\';
256 }
257
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 1 time.
154 quoted += static_cast<unsigned char>(c) < 32 ? ' ' : c;
258 }
259
1/1
✓ Branch 1 taken 14 times.
14 return quoted + "\"";
260 14 }
261
262 3 std::string ZonesJson(const std::vector<ReportNode>& nodes) {
263 3 std::vector<std::string> zones;
264
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 for (const ReportNode& node : nodes) {
265
1/1
✓ Branch 1 taken 1 time.
2 zones.push_back(fmt::format(
266 R"({{"name":{},"ms":{:.6g},"calls":{:.6g},"percent":{:.6g},"minMs":{:.6g},"maxMs":{:.6g},"children":{}}})",
267
1/1
✓ Branch 1 taken 1 time.
2 Quote(node.name), node.ms, node.calls, node.percent, node.minMs, node.maxMs,
268
1/1
✓ Branch 1 taken 1 time.
2 ZonesJson(node.children)));
269 }
270
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
9 return "[" + Join(zones, ",") + "]";
271 3 }
272
273 3 std::string SpanEvent(std::string_view name, int pid, std::string_view tid, double start,
274 double duration) {
275 3 return fmt::format(R"({{"name":{},"ph":"X","pid":{},"tid":{},"ts":{:.3f},"dur":{:.3f}}})",
276
1/1
✓ Branch 2 taken 3 times.
6 Quote(name), pid, tid, start, duration);
277 }
278
279 8 std::string MetaEvent(std::string_view kind, int pid, std::string_view tid, std::string_view name) {
280 8 return fmt::format(R"({{"name":"{}","ph":"M","pid":{}{},"args":{{"name":{}}}}})", kind, pid,
281
5/5
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 8 times.
16 tid.empty() ? "" : fmt::format(R"(,"tid":{})", tid), Quote(name));
282 }
283
284 }
285
286 38509 ZoneId ZoneId::Named(std::string_view name) {
287 38509 Names& names = GetNames();
288 38509 const std::lock_guard lock(names.mutex);
289
2/2
✓ Branch 1 taken 38031 times.
✓ Branch 2 taken 478 times.
38509 if (const auto found = names.indices.find(name); found != names.indices.end()) {
290 38031 return ZoneId(found->second);
291 }
292
293
1/1
✓ Branch 1 taken 478 times.
478 const auto index = uint32_t(names.names.size());
294
2/2
✓ Branch 1 taken 478 times.
✓ Branch 4 taken 478 times.
478 names.indices.emplace(names.names.emplace_back(name), index);
295 478 return ZoneId(index);
296 38509 }
297
298 49201 std::string_view ZoneId::Name() const {
299 49201 Names& names = GetNames();
300 49201 const std::lock_guard lock(names.mutex);
301
1/1
✓ Branch 1 taken 49201 times.
49201 return names.names.at(m_index);
302 49201 }
303
304 19 void SetEnabled(bool enabled) {
305 19 g_recording.store(enabled, std::memory_order_relaxed);
306 19 }
307
308 589062 bool Enabled() {
309 589062 return g_recording.load(std::memory_order_relaxed);
310 }
311
312 960 uint64_t Now() {
313 1920 return uint64_t(std::chrono::duration_cast<std::chrono::nanoseconds>(
314 960 std::chrono::steady_clock::now().time_since_epoch())
315 960 .count());
316 }
317
318 29 void BeginFrame() {
319 29 State& state = GetState();
320 29 const uint64_t now = Now();
321 29 const std::lock_guard lock(state.mutex);
322
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 3 times.
29 if (state.current.startNs != 0) {
323 26 state.current.endNs = now;
324 26 const uint64_t next = state.current.index + 1;
325
1/1
✓ Branch 2 taken 26 times.
26 state.frames.push_back(std::exchange(state.current, Frame{.index = next}));
326 26 DropOldFrames(state);
327 }
328 29 state.current.startNs = now;
329 29 }
330
331 15 uint64_t CurrentFrame() {
332 15 State& state = GetState();
333 15 const std::lock_guard lock(state.mutex);
334 15 return state.current.index;
335 15 }
336
337 12 std::vector<Frame> Frames() {
338 12 State& state = GetState();
339 12 const std::lock_guard lock(state.mutex);
340
1/1
✓ Branch 1 taken 12 times.
12 return {state.frames.begin(), state.frames.end()};
341 12 }
342
343 10 void SetHistory(size_t frames) {
344 10 State& state = GetState();
345 10 const std::lock_guard lock(state.mutex);
346
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 9 times.
10 state.history = std::max<size_t>(frames, 1);
347 10 DropOldFrames(state);
348 10 }
349
350 15 void Clear() {
351 15 State& state = GetState();
352 15 const std::lock_guard lock(state.mutex);
353 15 state.frames.clear();
354 15 state.current.zones.clear();
355 15 }
356
357 3 void SetThreadName(std::string_view name) {
358 3 const uint32_t track = ThreadTrack();
359 3 const bool nvtx = NvtxActive();
360 3 const Roctx* roctx = RoctxActive();
361 3 State& state = GetState();
362 3 const std::lock_guard lock(state.mutex);
363
1/1
✓ Branch 1 taken 3 times.
3 TrackEntry& entry = state.tracks[track];
364
1/1
✓ Branch 1 taken 3 times.
3 entry.track.name = name;
365
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 if (nvtx) {
366
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 nvtxNameOsThreadA(entry.osThread, entry.track.name.c_str());
367 }
368
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (roctx != nullptr && roctx->nameThread != nullptr) {
369
0/1
✗ Branch 1 not taken.
✗ roctx->nameThread(entry.track.name.c_str());
370 }
371 3 }
372
373 10 std::vector<Track> Tracks() {
374 10 State& state = GetState();
375 10 const std::lock_guard lock(state.mutex);
376 10 std::vector<Track> tracks;
377
1/1
✓ Branch 1 taken 10 times.
10 tracks.reserve(state.tracks.size());
378
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 10 times.
45 for (const TrackEntry& entry : state.tracks) {
379
1/1
✓ Branch 1 taken 35 times.
35 tracks.push_back(entry.track);
380 }
381 10 return tracks;
382 10 }
383
384 6 uint32_t AddGpuTrack(std::string_view name) {
385 6 State& state = GetState();
386 6 const std::lock_guard lock(state.mutex);
387 6 state.tracks.push_back({{std::string(name), true}});
388 12 return uint32_t(state.tracks.size() - 1);
389 12 }
390
391 21 void AddZone(uint64_t frame, const Zone& zone) {
392 21 State& state = GetState();
393 21 const std::lock_guard lock(state.mutex);
394
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2 times.
21 if (frame == state.current.index) {
395
1/1
✓ Branch 1 taken 19 times.
19 state.current.zones.push_back(zone);
396 19 return;
397 }
398
399 2 const auto kept = std::ranges::find(state.frames, frame, &Frame::index);
400
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (kept != state.frames.end()) {
401
1/1
✓ Branch 1 taken 1 time.
1 kept->zones.push_back(zone);
402 }
403 21 }
404
405 545638 ScopedZone::ScopedZone(ZoneId zone) : m_zone(zone) {
406
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 545636 times.
545638 if (NvtxActive()) {
407 2 m_ranges |= kNvtxRanges;
408 2 PushRange(zone);
409 }
410
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 545638 times.
545638 if (const Roctx* roctx = RoctxActive()) {
411 ✗ m_ranges |= kRoctxRanges;
412 ✗ PushRoctx(*roctx, zone);
413 }
414
415 545638 m_recording = Enabled();
416
2/2
✓ Branch 0 taken 460 times.
✓ Branch 1 taken 545178 times.
545638 if (m_recording) {
417 460 m_startNs = Now();
418 460 ++t_depth;
419 }
420 545638 }
421
422 546098 ScopedZone::~ScopedZone() {
423
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 545638 times.
545638 if ((m_ranges & kRoctxRanges) != 0) {
424 ✗ RoctxActive()->pop();
425 }
426
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 545636 times.
545638 if ((m_ranges & kNvtxRanges) != 0) {
427
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 nvtxDomainRangePop(g_domain);
428 }
429
2/2
✓ Branch 0 taken 545178 times.
✓ Branch 1 taken 460 times.
545638 if (!m_recording) {
430 545178 return;
431 }
432
433 460 --t_depth;
434 460 const Zone zone{m_zone, ThreadTrack(), t_depth, m_startNs, Now()};
435 460 State& state = GetState();
436 460 const std::lock_guard lock(state.mutex);
437 460 state.current.zones.push_back(zone);
438 545638 }
439
440 3 Report Summarize(const std::vector<Frame>& frames) {
441 3 Report report;
442
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if (frames.empty()) {
443 return report;
444 }
445
446 2 std::map<uint32_t, std::vector<ReportNode>> roots;
447 2 std::map<uint32_t, double> outermostMs;
448
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 for (const Frame& frame : frames) {
449 3 report.frameMs += DurationMs(frame.startNs, frame.endNs);
450 3 std::map<uint32_t, std::vector<const Zone*>> byTrack;
451
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 for (const Zone& zone : frame.zones) {
452
2/2
✓ Branch 1 taken 9 times.
✓ Branch 4 taken 9 times.
9 byTrack[zone.track].push_back(&zone);
453
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 if (zone.depth == 0) {
454
1/1
✓ Branch 1 taken 5 times.
5 outermostMs[zone.track] += DurationMs(zone.startNs, zone.endNs);
455 }
456 }
457
3/3
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 3 times.
8 for (auto& [track, zones] : byTrack) {
458
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 Gather(roots[track], std::move(zones));
459 }
460 3 }
461
462
1/1
✓ Branch 1 taken 2 times.
2 const std::vector<Track> tracks = Tracks();
463 2 const auto count = double(frames.size());
464 2 report.frames = frames.size();
465 2 report.frameMs /= count;
466
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
5 for (auto& [index, zones] : roots) {
467 3 Average(zones, count, report.frameMs);
468 6 report.tracks.push_back({.name = TrackName(tracks, index),
469 3 .gpu = IsGpuTrack(tracks, index),
470 6 .ms = outermostMs[index] / count,
471 .zones = std::move(zones)});
472 }
473 2 return report;
474 5 }
475
476 2 std::string ReportText(const Report& report, const ReportFilter& filter) {
477 2 std::string text = fmt::format("{:<56} {:>12} {:>10}\n", "", "Duration (ms)", "Percent");
478
1/1
✓ Branch 1 taken 2 times.
4 text += fmt::format("{:<56} {:12.3f} ({} frames)\n", "Frame", report.frameMs, report.frames);
479
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (const ReportTrack& track : report.tracks) {
480
3/3
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 4 times.
6 const std::string title = fmt::format("{} ({})", track.name, track.gpu ? "GPU" : "CPU");
481
1/1
✓ Branch 1 taken 4 times.
8 text += fmt::format("\n{:<56} {:12.3f}\n", title, track.ms);
482
1/1
✓ Branch 1 taken 4 times.
4 AddLines(text, track.zones, filter, 0);
483 4 }
484 2 return text;
485 ✗ }
486
487 1 std::string ReportJson(const Report& report) {
488 1 std::vector<std::string> tracks;
489
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (const ReportTrack& track : report.tracks) {
490
1/1
✓ Branch 1 taken 2 times.
4 tracks.push_back(fmt::format(R"({{"name":{},"gpu":{},"ms":{:.6g},"zones":{}}})",
491
1/1
✓ Branch 1 taken 2 times.
4 Quote(track.name), track.gpu, track.ms,
492
1/1
✓ Branch 1 taken 2 times.
4 ZonesJson(track.zones)));
493 }
494
1/1
✓ Branch 1 taken 1 time.
2 return fmt::format(R"({{"frames":{},"frameMs":{:.6g},"tracks":[{}]}})", report.frames,
495
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
1 report.frameMs, Join(tracks, ",")) +
496 2 "\n";
497 1 }
498
499 2 std::string TraceJson(const std::vector<Frame>& frames) {
500 2 constexpr int kCpu = 1;
501 2 constexpr int kGpu = 2;
502 2 const std::vector<Track> tracks = Tracks();
503
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 const uint64_t origin = frames.empty() ? 0 : frames.front().startNs;
504 5 const auto us = [origin](uint64_t ns) { return double(int64_t(ns - origin)) * 1e-3; };
505
506 2 std::vector<std::string> events{MetaEvent("process_name", kCpu, "", "CPU"),
507 MetaEvent("process_name", kGpu, "", "GPU"),
508
3/5
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
8 MetaEvent("thread_name", kCpu, "\"frames\"", "Frames")};
509 2 std::map<uint32_t, bool> used;
510
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 for (const Frame& frame : frames) {
511
1/1
✓ Branch 1 taken 1 time.
2 events.push_back(SpanEvent(fmt::format("Frame {}", frame.index), kCpu, "\"frames\"",
512
1/1
✓ Branch 1 taken 1 time.
1 us(frame.startNs), double(frame.endNs - frame.startNs) * 1e-3));
513
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (const Zone& zone : frame.zones) {
514 2 const bool gpu = IsGpuTrack(tracks, zone.track);
515
1/1
✓ Branch 1 taken 2 times.
2 used[zone.track] = gpu;
516
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
5 events.push_back(SpanEvent(zone.id.Name(), gpu ? kGpu : kCpu,
517
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
4 std::to_string(zone.track), us(zone.startNs),
518 2 double(zone.endNs - zone.startNs) * 1e-3));
519 }
520 }
521
3/3
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
4 for (const auto& [track, gpu] : used) {
522
3/3
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 2 times.
5 events.push_back(MetaEvent("thread_name", gpu ? kGpu : kCpu, std::to_string(track),
523
1/1
✓ Branch 1 taken 2 times.
4 TrackName(tracks, track)));
524 }
525
526
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
8 return R"({"traceEvents":[)" + Join(events, ",") + R"(],"displayTimeUnit":"ms"})" + "\n";
527 6 }
528
529 }
530