Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
task_registry_presentation.hpp
Go to the documentation of this file.
1#pragma once
2
10
12#include <uni20/config.hpp>
13
14#include <algorithm>
15#include <cstddef>
16#include <cstdint>
17#include <fmt/format.h>
18#include <ranges>
19#include <string>
20#include <string_view>
21#include <vector>
22
23namespace uni20
24{
25
28{
29 std::string title{"Async task registry"};
30 std::string reason{};
31};
32
33namespace detail
34{
35
36template <typename Value> [[nodiscard]] std::string task_registry_join_ids(std::vector<Value> const& values)
37{
38 if (values.empty()) return "-";
39
40 std::string result;
41 for (auto const value : values)
42 {
43 if (!result.empty()) result += ", ";
44 result += fmt::format("{}", value);
45 }
46 return result;
47}
48
49[[nodiscard]] inline std::string task_registry_awaits(std::vector<TaskRegistryGraphAwaitDependency> const& dependencies)
50{
51 if (dependencies.empty()) return "-";
52
53 std::string result;
54 for (auto const& dependency : dependencies)
55 {
56 if (!result.empty()) result += ", ";
57 result +=
58 fmt::format("{}{}", dependency.role == TaskRegistryGraphRole::Reader ? "read " : "write ", dependency.node_id);
59 }
60 return result;
61}
62
63[[nodiscard]] inline std::string task_registry_task_source(TaskRegistryGraphTask const& task)
64{
65 if (task.state == "suspended" && !task.last_await_site.location.empty())
66 return "await: " + task.last_await_site.location;
67 if (!task.schedule_site.location.empty()) return "scheduled: " + task.schedule_site.location;
68 if (!task.creation_site.location.empty()) return "created: " + task.creation_site.location;
69 if (!task.creation_timestamp.empty()) return task.creation_timestamp;
70 return "-";
71}
72
73[[nodiscard]] inline std::string task_registry_active_awaits(TaskRegistryGraphTask const& task)
74{
75 auto awaits = task_registry_awaits(task.await_dependencies);
76 if (task.waiting_on.empty()) return awaits;
77 return awaits == "-" ? task.waiting_on : fmt::format("{}; {}", awaits, task.waiting_on);
78}
79
80[[nodiscard]] inline std::string task_registry_data_state(TaskRegistryGraphDataNode const& node,
81 TaskRegistryGraphDiagnostics const& diagnostics)
82{
83 std::string state = node.state;
84 if (std::ranges::find(diagnostics.missing_writer_node_ids, node.id) != diagnostics.missing_writer_node_ids.end())
85 state += "; missing writer";
86 if (std::ranges::find(diagnostics.cycle_node_ids, node.id) != diagnostics.cycle_node_ids.end())
87 state += "; dependency cycle";
88 return state;
89}
90
91[[nodiscard]] inline std::string task_registry_task_state(TaskRegistryGraphTask const& task,
92 TaskRegistryGraphDiagnostics const& diagnostics)
93{
94 std::string state = task.state;
95 if (std::ranges::find(diagnostics.blocked_read_task_ids, task.id) != diagnostics.blocked_read_task_ids.end())
96 state += "; blocked read";
97 if (std::ranges::find(diagnostics.blocked_write_task_ids, task.id) != diagnostics.blocked_write_task_ids.end())
98 state += "; blocked write";
99 if (std::ranges::find(diagnostics.cycle_task_ids, task.id) != diagnostics.cycle_task_ids.end())
100 state += "; dependency cycle";
101 return state;
102}
103
104} // namespace detail
105
116 TaskRegistryGraphDiagnostics const& diagnostics,
117 TaskRegistryReportOptions const& options = {})
118{
119 presentation::report_builder report(options.title);
120 if (!options.reason.empty()) report.status(presentation::semantic_glyph::info, options.reason);
121
122 if (!snapshot.snapshot_available)
123 {
125 snapshot.unavailable_reason.empty() ? "snapshot unavailable" : snapshot.unavailable_reason);
126 return report;
127 }
128
129 if (!diagnostics.failed_task_ids.empty())
130 {
131 report.status(presentation::semantic_glyph::failure, "failed coroutine task captured");
132 }
133 else if (!diagnostics.cycle_task_ids.empty() || !diagnostics.cycle_node_ids.empty())
134 {
135 report.status(presentation::semantic_glyph::failure, "dependency cycle detected");
136 }
137 else if (!diagnostics.missing_writer_node_ids.empty() || !diagnostics.missing_writer_epoch_ids.empty())
138 {
139 report.status(presentation::semantic_glyph::failure, "missing writer detected");
140 }
141 else if (!diagnostics.blocked_read_task_ids.empty() || !diagnostics.blocked_write_task_ids.empty())
142 {
143 report.status(presentation::semantic_glyph::warning, "blocked async work captured");
144 }
145 else
146 {
147 report.status(presentation::semantic_glyph::info, "snapshot captured");
148 }
149
150#if !UNI20_HAS_STACKTRACE
151 report.status(presentation::semantic_glyph::warning, "std::stacktrace is unavailable; source provenance is degraded");
152#endif
153
154 report.field("tracked async values", snapshot.data_nodes.size())
155 .field("tracked epoch contexts", snapshot.epochs.size())
156 .field("tracked coroutine tasks", snapshot.tasks.size())
157 .field("failed coroutine tasks", diagnostics.failed_task_ids.size())
158 .field("blocked readers", diagnostics.blocked_read_task_ids.size())
159 .field("blocked writers", diagnostics.blocked_write_task_ids.size());
160
161 if (!diagnostics.notes.empty())
162 {
163 auto& table = report.table("Diagnostics");
165 for (auto const& note : diagnostics.notes)
166 table.row(note);
167 }
168
169 if (!snapshot.data_nodes.empty())
170 {
171 auto& table = report.table("Async values");
177
178 for (auto const& node : snapshot.data_nodes)
179 {
180 auto name =
181 node.label.empty() ? fmt::format("data {}", node.id) : fmt::format("data {}: {}", node.id, node.label);
182 table.row(name, node.type.empty() ? "-" : node.type, detail::task_registry_data_state(node, diagnostics),
183 node.value.empty() ? "-" : node.value, node.storage_address.empty() ? "-" : node.storage_address);
184 }
185 }
186
187 if (!snapshot.epochs.empty())
188 {
189 auto& table = report.table("Epoch contexts");
197
198 for (auto const& epoch : snapshot.epochs)
199 {
200 auto const epoch_label = fmt::format("{} (gen {})", epoch.id, epoch.generation);
201 auto const phase = epoch.snapshot_available ? epoch.phase : "snapshot unavailable";
202 auto const value = epoch.has_node ? fmt::format("data {}", epoch.node_id) : "-";
203 auto const next = epoch.has_next_epoch ? fmt::format("epoch {}", epoch.next_epoch_id) : "-";
204 auto const created = !epoch.creation_site.location.empty()
205 ? epoch.creation_site.location
206 : (!epoch.creation_timestamp.empty() ? epoch.creation_timestamp : "-");
207 table.row(epoch_label, phase, value, detail::task_registry_join_ids(epoch.reader_task_ids),
208 detail::task_registry_join_ids(epoch.writer_task_ids), next, created);
209 }
210 }
211
212 if (!snapshot.tasks.empty())
213 {
214 if (std::ranges::any_of(snapshot.tasks, [](auto const& task) { return !task.failure.empty(); }))
215 {
216 auto& failures = report.table("Coroutine failures");
217 failures.column("task", presentation::table_alignment::left)
218 .column("exception", presentation::table_alignment::left);
219 for (auto const& task : snapshot.tasks)
220 {
221 if (task.failure.empty()) continue;
222 auto name =
223 task.label.empty() ? fmt::format("task {}", task.id) : fmt::format("task {}: {}", task.id, task.label);
224 failures.row(name, task.failure);
225 }
226 }
227
228 auto& table = report.table("Coroutine tasks");
231 .column("transitions")
232 .column("argument dependencies", presentation::table_alignment::left)
235
236 for (auto const& task : snapshot.tasks)
237 {
238 auto name =
239 task.label.empty() ? fmt::format("task {}", task.id) : fmt::format("task {}: {}", task.id, task.label);
240 auto dependencies = fmt::format("read: {}; write: {}", detail::task_registry_join_ids(task.read_dependencies),
241 detail::task_registry_join_ids(task.write_dependencies));
242 table.row(name, detail::task_registry_task_state(task, diagnostics), task.transition_count, dependencies,
243 detail::task_registry_active_awaits(task), detail::task_registry_task_source(task));
244 }
245 }
246
247 return report;
248}
249
250} // namespace uni20
void row(std::vector< formatted_cell > cells, std::source_location where=std::source_location::current())
Emit preformatted cells without discarding their numeric alignment semantics.
Definition display.cpp:448
streaming_table & column(std::string heading, column_width width_spec=width::share(), presentation::table_alignment alignment=presentation::table_alignment::right, column_format format={})
Builder for simple terminal reports with headings, status lines, fields, and tables.
Definition presentation.hpp:602
locale_t value
Definition data_table.cpp:49
Definition format.hpp:36
streaming_table table(std::string title, stream destination)
Create a schema-first streaming display table.
Definition display.cpp:800
Definition async/async.hpp:26
presentation::report_builder task_registry_report(TaskRegistryGraphSnapshot const &snapshot, TaskRegistryGraphDiagnostics const &diagnostics, TaskRegistryReportOptions const &options={})
Build a presentation-native summary of one async task-registry snapshot.
Definition task_registry_presentation.hpp:115
Diagnostics derived from a structured task-registry graph snapshot.
Definition task_registry_snapshot.hpp:114
std::vector< std::size_t > cycle_task_ids
Definition task_registry_snapshot.hpp:118
std::vector< std::uint64_t > cycle_node_ids
Definition task_registry_snapshot.hpp:119
std::vector< std::size_t > blocked_write_task_ids
Definition task_registry_snapshot.hpp:117
std::vector< std::size_t > missing_writer_epoch_ids
Definition task_registry_snapshot.hpp:121
std::vector< std::size_t > blocked_read_task_ids
Definition task_registry_snapshot.hpp:116
std::vector< std::string > notes
Definition task_registry_snapshot.hpp:115
std::vector< std::uint64_t > missing_writer_node_ids
Definition task_registry_snapshot.hpp:120
std::vector< std::size_t > failed_task_ids
Definition task_registry_snapshot.hpp:122
Structured task-registry graph snapshot.
Definition task_registry_snapshot.hpp:104
std::vector< TaskRegistryGraphEpoch > epochs
Definition task_registry_snapshot.hpp:108
std::string unavailable_reason
Definition task_registry_snapshot.hpp:106
std::vector< TaskRegistryGraphDataNode > data_nodes
Definition task_registry_snapshot.hpp:107
bool snapshot_available
Definition task_registry_snapshot.hpp:105
std::vector< TaskRegistryGraphTask > tasks
Definition task_registry_snapshot.hpp:109
Options controlling an async task-registry summary report.
Definition task_registry_presentation.hpp:28
std::string reason
Definition task_registry_presentation.hpp:30
std::string title
Definition task_registry_presentation.hpp:29
Structured async task-registry graph snapshots and diagnostics.