chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
35 / 95
36.8%
Branches:
11 / 65
16.9%
Functions:
7 / 11
63.6%

device/slang/debug.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/device/slang/debug.h"
3
4 #include <bit>
5 #include <cstring>
6
7 #include <fmt/format.h>
8
9 #include "chrono_batch_render/device/kernels/device.h"
10 #include "chrono_batch_render/util/log.h"
11 #include "chrono_batch_render/util/text.h"
12
13 namespace chrono::batch_render::device {
14 namespace {
15
16 constexpr size_t kShownFailures = 8;
17
18 ✗ bool Done(uint32_t stamp, uint64_t finished) {
19
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ return stamp != 0 && int32_t(uint32_t(finished) - stamp) >= 0;
20 }
21
22 ✗ std::string Value(uint32_t bits, uint32_t type) {
23
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ switch (type) {
24 ✗ case kPrintedInt:
25 ✗ return std::to_string(int32_t(bits));
26 ✗ case kPrintedFloat:
27 ✗ return fmt::format("{}", std::bit_cast<float>(bits));
28 ✗ case kPrintedBool:
29
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ return bits != 0 ? "true" : "false";
30 ✗ default:
31 ✗ return std::to_string(bits);
32 }
33 }
34
35 }
36
37 40 uint64_t DebugRecords::Bytes() {
38 40 return uint64_t(kDebugHeaderWords + kCapacity * kDebugRecordWords) * 4;
39 }
40
41 20 DebugRecords::DebugRecords(Ref<Buffer> buffer) : m_buffer(std::move(buffer)) {
42 20 m_header = static_cast<uint32_t*>(m_buffer->Mapped());
43 20 std::memset(m_header, 0, Bytes());
44 20 }
45
46 80099 BufferRange DebugRecords::Submission() const {
47 80099 return m_buffer->Slice(uint64_t(kSubmissionWord) * 4, 4);
48 }
49
50 1117 void DebugRecords::AddKernel(const std::string& kernel, std::span<const std::string> strings) {
51 1117 const std::lock_guard lock(m_mutex);
52
1/1
✓ Branch 1 taken 1117 times.
1117 m_kernels.try_emplace(SlangStringHash(kernel), kernel);
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1117 times.
1117 for (const std::string& text : strings) {
54
0/1
✗ Branch 1 not taken.
✗ m_strings.try_emplace(SlangStringHash(text), text);
55 }
56 1117 }
57
58 ✗ std::string DebugRecords::Format(const uint32_t* record) const {
59 ✗ const auto found = m_strings.find(record[kHashWord]);
60 ✗ std::string_view format = "{}";
61
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (found != m_strings.end()) {
62 ✗ format = found->second;
63 }
64
65 ✗ const uint32_t values = (record[kKindWord] >> 8) & 0xff;
66 ✗ std::string text;
67 ✗ uint32_t next = 0;
68
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ for (size_t at = format.find("{}"); at != std::string_view::npos; at = format.find("{}")) {
69
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ text.append(format.substr(0, at));
70 ✗ const uint32_t* value = record + kValuesWord + size_t(next) * 2;
71
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✗ text += next < values ? Value(value[0], value[1]) : "{}";
72 ✗ ++next;
73 ✗ format.remove_prefix(at + 2);
74 }
75
0/1
✗ Branch 1 not taken.
✗ text.append(format);
76 ✗ return text;
77 ✗ }
78
79 ✗ void DebugRecords::Report(const uint32_t* record, uint64_t finished) {
80 ✗ const auto found = m_kernels.find(record[kKernelWord]);
81
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ const std::string kernel = found != m_kernels.end() ? found->second : "a kernel";
82
0/1
✗ Branch 1 not taken.
✗ const std::string message = Format(record);
83 ✗ const uint32_t* grid = record + kThreadWord;
84 ✗ const std::string thread =
85
0/1
✗ Branch 1 not taken.
✗ fmt::format("thread {} ({}, {}, {})", record[kLinearWord], grid[0], grid[1], grid[2]);
86
87
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if ((record[kKindWord] & 0xff) == kAssertRecord) {
88 ✗ const uint64_t submission = (finished & ~uint64_t(0xffffffff)) | record[kStampWord];
89 ✗ m_failures.emplace(
90
0/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ submission > finished ? submission - (uint64_t(1) << 32) : submission,
91
0/1
✗ Branch 1 not taken.
✗ fmt::format("Assert failed in kernel {} at {}: {}", kernel, thread, message));
92
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if ((record[kKindWord] & 0xff) == kPrintRecord) {
93
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Info, "kernel {}, {}: {}", kernel, thread, message);
94 }
95 ✗ }
96
97 111868 void DebugRecords::Read(uint64_t finished, bool idle) {
98 111868 const std::lock_guard lock(m_mutex);
99 111868 const uint32_t taken = m_header[kTakenWord];
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111868 times.
111868 const uint32_t kept = std::min(taken, kCapacity);
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111868 times.
111868 for (; m_read < kept; ++m_read) {
102 ✗ uint32_t* record = m_header + kDebugHeaderWords + size_t(m_read) * kDebugRecordWords;
103
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!Done(record[kStampWord], finished)) {
104 break;
105 }
106
0/1
✗ Branch 1 not taken.
✗ Report(record, finished);
107 ✗ record[kStampWord] = 0;
108 }
109
110
3/4
✓ Branch 0 taken 57430 times.
✓ Branch 1 taken 54438 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 57430 times.
111868 if (!idle || m_read != kept) {
111 54438 return;
112 }
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57430 times.
57430 if (taken > kCapacity) {
114 ✗ Log(LogLevel::Warning,
115 "kernels wrote {} more asserts and prints than the {} the "
116 "debug buffer holds; those were dropped",
117
0/1
✗ Branch 1 not taken.
✗ taken - kCapacity, kCapacity);
118 }
119 57430 m_header[kTakenWord] = 0;
120 57430 m_read = 0;
121 111868 }
122
123 30157 std::optional<std::string> DebugRecords::TakeFailures(uint64_t submission) {
124 30157 const std::lock_guard lock(m_mutex);
125 30157 const auto end = m_failures.upper_bound(submission);
126
1/2
✓ Branch 0 taken 30157 times.
✗ Branch 1 not taken.
30157 if (end == m_failures.begin()) {
127 30157 return std::nullopt;
128 }
129
130 ✗ std::vector<std::string> messages;
131 ✗ size_t count = 0;
132
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for (auto it = m_failures.begin(); it != end; ++it, ++count) {
133
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (count < kShownFailures) {
134
0/1
✗ Branch 1 not taken.
✗ messages.push_back(it->second);
135 }
136 }
137
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (count > kShownFailures) {
138
0/1
✗ Branch 1 not taken.
✗ messages.push_back(fmt::format("and {} more failed asserts", count - kShownFailures));
139 }
140 ✗ m_failures.erase(m_failures.begin(), end);
141
0/1
✗ Branch 1 not taken.
✗ return Join(messages, "\n");
142 30157 }
143
144 1124 uint32_t SlangStringHash(std::string_view text) {
145 1124 uint32_t hash = 0;
146
2/2
✓ Branch 0 taken 13782 times.
✓ Branch 1 taken 1124 times.
14906 for (const char c : text) {
147 13782 hash = uint32_t(uint8_t(c)) + (hash << 6) + (hash << 16) - hash;
148 }
149 1124 return hash;
150 }
151
152 }
153