Sound Byte Libs 29c5ff3
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
report.hpp
Go to the documentation of this file.
1/**
2 * @file report.hpp
3 * @brief Profiling report formatters (human-readable and tagged)
4 *
5 * Formats AudioBudget + named SectionProfiles into log-friendly strings.
6 * Gated behind SBL_PROFILING_ENABLED — compiles to nothing when disabled.
7 *
8 * Two output modes:
9 *
10 * Human-readable (format_report):
11 * CPU: avg=36.9% peak=38.8% overruns=0
12 * osc=85119 svf=12928 verb=41100 dly=9344
13 *
14 * Tagged (format_tagged_report + format_tagged_histogram):
15 * #PROF cpu_avg=36.9 cpu_peak=38.8 cpu_min=34.2 overruns=0 blocks=48000
16 * #PROF.OSC cycles=85119
17 * #PROF.SVF cycles=12928
18 * #HIST b0_10=0 b10_20=0 b20_30=150 b30_40=47200 ...
19 *
20 * See docs/conventions/tagged-output.md for the tagged output spec.
21 *
22 * Usage:
23 * sbl::profiling::NamedSection sections[] = {
24 * {"osc", s_prof_osc.avg}, {"svf", s_prof_svf.avg},
25 * };
26 * char buf[256];
27 * sbl::profiling::format_tagged_report(buf, sizeof(buf), s_budget, sections, 2);
28 * uart_write(buf);
29 * sbl::profiling::format_tagged_histogram(buf, sizeof(buf), s_budget);
30 * uart_write(buf);
31 */
32#pragma once
33#include <cstdint>
34#include <cstddef>
36#include <sbl/log/format.hpp>
37
38namespace sbl::profiling {
39
41 const char* name;
42 uint32_t avg;
43};
44
45/**
46 * @brief Format a profiling report into a buffer
47 *
48 * @param buf Output buffer
49 * @param size Buffer size
50 * @param budget AudioBudget instance
51 * @param sections Array of named section profiles
52 * @param n Number of sections
53 * @return Number of characters written
54 */
55inline int format_report(char* buf, size_t size, const AudioBudget& budget,
56 const NamedSection* sections, size_t n) {
57#ifdef SBL_PROFILING_ENABLED
58 int pos = sbl::log::format(buf, size,
59 "CPU: avg=%d.%d%% peak=%d.%d%% overruns=%u\r\n",
60 static_cast<int>(budget.avg_load() * 100.0f),
61 static_cast<int>(budget.avg_load() * 1000.0f) % 10,
62 static_cast<int>(budget.peak_load() * 100.0f),
63 static_cast<int>(budget.peak_load() * 1000.0f) % 10,
64 budget.overruns());
65
66 if (n > 0 && static_cast<size_t>(pos) < size - 1) {
67 // " name=val name=val ...\r\n"
68 size_t remaining = size - static_cast<size_t>(pos);
69 int spos = sbl::log::format(buf + pos, remaining, " ");
70 pos += spos;
71
72 for (size_t i = 0; i < n && static_cast<size_t>(pos) < size - 1; ++i) {
73 remaining = size - static_cast<size_t>(pos);
74 if (i > 0) {
75 spos = sbl::log::format(buf + pos, remaining, " ");
76 pos += spos;
77 remaining = size - static_cast<size_t>(pos);
78 }
79 spos = sbl::log::format(buf + pos, remaining, "%s=%u",
80 sections[i].name, sections[i].avg);
81 pos += spos;
82 }
83
84 remaining = size - static_cast<size_t>(pos);
85 spos = sbl::log::format(buf + pos, remaining, "\r\n");
86 pos += spos;
87 }
88
89 return pos;
90#else
91 (void)budget; (void)sections; (void)n;
92 if (size > 0) buf[0] = '\0';
93 return 0;
94#endif
95}
96
97// ─── Tagged output formatters (FDP-028) ─────────────────────────────
98
99/**
100 * @brief Format profiling data as tagged output lines
101 *
102 * Emits one #PROF summary line followed by one #PROF.<NAME> line per section.
103 * Section names are uppercased in the tag (e.g., "osc" → #PROF.OSC).
104 *
105 * @return Number of characters written
106 */
107inline int format_tagged_report(char* buf, size_t size, const AudioBudget& budget,
108 const NamedSection* sections, size_t n) {
109#ifdef SBL_PROFILING_ENABLED
110 int pos = sbl::log::format(buf, size,
111 "#PROF cpu_avg=%d.%d cpu_peak=%d.%d cpu_min=%d.%d overruns=%u blocks=%u\r\n",
112 static_cast<int>(budget.avg_load() * 100.0f),
113 static_cast<int>(budget.avg_load() * 1000.0f) % 10,
114 static_cast<int>(budget.peak_load() * 100.0f),
115 static_cast<int>(budget.peak_load() * 1000.0f) % 10,
116 static_cast<int>(budget.min_load() * 100.0f),
117 static_cast<int>(budget.min_load() * 1000.0f) % 10,
118 budget.overruns(),
119 budget.count());
120
121 for (size_t i = 0; i < n && static_cast<size_t>(pos) < size - 1; ++i) {
122 size_t remaining = size - static_cast<size_t>(pos);
123 pos += sbl::log::format(buf + pos, remaining,
124 "#PROF.%s cycles=%u\r\n",
125 sections[i].name, sections[i].avg);
126 }
127
128 return pos;
129#else
130 (void)budget; (void)sections; (void)n;
131 if (size > 0) buf[0] = '\0';
132 return 0;
133#endif
134}
135
136/**
137 * @brief Format CPU load histogram as a tagged output line
138 *
139 * Emits a single #HIST line with 11 bucket counts (10% each, last is >100%).
140 *
141 * @return Number of characters written
142 */
143inline int format_tagged_histogram(char* buf, size_t size, const AudioBudget& budget) {
144#ifdef SBL_PROFILING_ENABLED
145 static const char* labels[] = {
146 "b0_10", "b10_20", "b20_30", "b30_40", "b40_50",
147 "b50_60", "b60_70", "b70_80", "b80_90", "b90_100", "over"
148 };
149
150 int pos = sbl::log::format(buf, size, "#HIST");
151
152 for (size_t i = 0; i < budget.hist_size() && static_cast<size_t>(pos) < size - 1; ++i) {
153 size_t remaining = size - static_cast<size_t>(pos);
154 pos += sbl::log::format(buf + pos, remaining, " %s=%u",
155 labels[i], budget.hist_bucket(i));
156 }
157
158 if (static_cast<size_t>(pos) < size - 1) {
159 size_t remaining = size - static_cast<size_t>(pos);
160 pos += sbl::log::format(buf + pos, remaining, "\r\n");
161 }
162
163 return pos;
164#else
165 (void)budget;
166 if (size > 0) buf[0] = '\0';
167 return 0;
168#endif
169}
170
171} // namespace sbl::profiling
Audio callback cycle budget tracker.
float avg_load() const
Average CPU load as fraction [0.0, 1.0+].
uint32_t overruns() const
Number of callbacks that exceeded the cycle budget.
static constexpr size_t hist_size()
Number of histogram buckets.
float min_load() const
Minimum CPU load since last reset.
float peak_load() const
Peak (worst-case) CPU load since last reset.
uint32_t count() const
Total callbacks measured.
uint32_t hist_bucket(size_t i) const
Read histogram bucket count (0-indexed, last bucket is >100%)
Minimal safe string formatting for embedded systems.
int format(char *buf, size_t size, const char *fmt,...)
Format a string into a buffer (snprintf-style)
Definition format.hpp:265
CPU load monitoring.
int format_tagged_report(char *buf, size_t size, const AudioBudget &budget, const NamedSection *sections, size_t n)
Format profiling data as tagged output lines.
Definition report.hpp:107
int format_report(char *buf, size_t size, const AudioBudget &budget, const NamedSection *sections, size_t n)
Format a profiling report into a buffer.
Definition report.hpp:55
int format_tagged_histogram(char *buf, size_t size, const AudioBudget &budget)
Format CPU load histogram as a tagged output line.
Definition report.hpp:143