Allow compact histograms in private radio timing builds
This commit is contained in:
1 parent
fd2c99b890
commit
601f3ee622
3 files changed
+76
-7
No files matched your search
@@ -48,7 +48,8 @@ inline bool handle(const char* suffix, const char* args, int verb,
|
||||
|| (timing && (sscanf(args, "%u %u %c", &direction, &stage, &extra) != 2
|
||||
|| direction > 1 || stage >= profiletiming::StageCount))
|
||||
|| (hist && (sscanf(args, "%u %u %u %c", &direction, &stage, &page, &extra) != 3
|
||||
|| direction > 1 || stage >= profiletiming::StageCount || page > 1))) {
|
||||
|| direction > 1 || stage >= profiletiming::StageCount
|
||||
|| page >= profiletiming::HistogramCount / 8))) {
|
||||
snprintf(reply, capacity, "Error: get radio.fast.hw|hil; time D S; hist D S P");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include <stdint.h>
|
||||
#include "NrfRadioIrqObserver.h"
|
||||
|
||||
#ifndef MESH_HIL_PROFILE_HISTOGRAM_COUNT
|
||||
#define MESH_HIL_PROFILE_HISTOGRAM_COUNT 16
|
||||
#endif
|
||||
|
||||
namespace mesh {
|
||||
namespace profiletiming {
|
||||
|
||||
@@ -20,7 +24,9 @@ enum Stage : uint32_t {
|
||||
enum Result : uint32_t { Success = 0, Busy = 1, Failure = 2 };
|
||||
static constexpr uint32_t Magic = 0x4d435054; // MCPT
|
||||
static constexpr uint32_t Version = 3;
|
||||
static constexpr uint32_t HistogramCount = 16;
|
||||
static constexpr uint32_t HistogramCount = MESH_HIL_PROFILE_HISTOGRAM_COUNT;
|
||||
static_assert(HistogramCount == 8 || HistogramCount == 16,
|
||||
"Private profile timing supports eight or sixteen histogram bins");
|
||||
static constexpr uint32_t HistogramWidthUs = 64;
|
||||
#if defined(NRF52_PLATFORM)
|
||||
static constexpr uint32_t ClockDivisor = 64;
|
||||
@@ -33,7 +39,8 @@ static constexpr uint32_t ClockHz = ClockDivisor * 1000000;
|
||||
// MCU; sums are split low/high words rather than ABI-dependent uint64_t.
|
||||
struct StageStats {
|
||||
uint32_t count, sum_low_us, sum_high_us, min_us, max_us, last_us;
|
||||
uint32_t histogram[HistogramCount]; // final bucket includes >= 960 us
|
||||
// Final bucket includes >= (HistogramCount - 1) * HistogramWidthUs.
|
||||
uint32_t histogram[HistogramCount];
|
||||
};
|
||||
struct DirectionStats {
|
||||
uint32_t successes, busy, failures, last_result, last_mask;
|
||||
@@ -190,10 +197,10 @@ inline void finish(Result result, uint32_t now_ticks) {
|
||||
++capture.sequence;
|
||||
}
|
||||
|
||||
static_assert(sizeof(Capture) == 40 + 2 * (20 + StageCount * (8 + 88)),
|
||||
static_assert(sizeof(Capture) == 40 + 2 * (20 + StageCount * (32 + 4 * HistogramCount)),
|
||||
"Unexpected profile timing ABI");
|
||||
static_assert(StageCount == 11 && sizeof(Capture) == 2192,
|
||||
"Profile timing version 3 requires eleven stages and 2192 bytes");
|
||||
static_assert(StageCount == 11 && sizeof(Capture) == (HistogramCount == 16 ? 2192 : 1488),
|
||||
"Profile timing version 3 requires eleven stages and matching bin metadata");
|
||||
|
||||
} // namespace profiletiming
|
||||
} // namespace mesh
|
||||
|
||||
@@ -13,6 +13,12 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
class Esp32SpiABDiagnosticsTests(unittest.TestCase):
|
||||
def test_commands_reach_diagnostics_through_production_cli(self):
|
||||
self.check_production_cli()
|
||||
|
||||
def test_eight_bin_capture_preserves_stages_sums_and_rejects_a_second_page(self):
|
||||
self.check_production_cli(8)
|
||||
|
||||
def check_production_cli(self, histogram_count=None):
|
||||
fixture = (ROOT / 'test/fixtures/radio_profiles/switch_tuning_test.cpp').read_text()
|
||||
fixture = fixture.rsplit('\nint main() {', 1)[0]
|
||||
fixture += r'''
|
||||
@@ -29,12 +35,42 @@ int main() {
|
||||
mesh::esp_spiab::recordClock(0x1000,8888888);
|
||||
assert(!strcmp(f.cmd("get radio.fast.hw"),"> divider=0x00001000 hz=8888888 changes=1"));
|
||||
assert(strstr(f.cmd("get radio.fast.time 1 10"),"d=1 s=10 count=0"));
|
||||
#if MESH_HIL_PROFILE_HISTOGRAM_COUNT == 8
|
||||
static_assert(sizeof(mesh::profiletiming::Capture)==1488,"compact HIL capture");
|
||||
assert(strstr(f.cmd("get radio.fast.hist 0 10 1"),"Error:"));
|
||||
assert(strstr(f.cmd("get radio.fast.hist 0 10 0"),"p=0 bins=0,0,0,0,0,0,0,0"));
|
||||
assert(strstr(f.cmd("get radio.fast.hil"),"bytes=1488"));
|
||||
assert(strstr(f.cmd("get radio.fast.hil"),"stages=11 bins=8 width=64"));
|
||||
#else
|
||||
assert(strstr(f.cmd("get radio.fast.hist 0 10 1"),"p=1 bins=0,0,0,0,0,0,0,0"));
|
||||
assert(strstr(f.cmd("get radio.fast.hil"),"bytes=2192"));
|
||||
assert(strstr(f.cmd("get radio.fast.hil"),"stages=11 bins=16 width=64"));
|
||||
#endif
|
||||
assert(strstr(f.cmd("set radio.fast.hw 8",false),"read-only"));
|
||||
assert(strstr(f.cmd("get radio.fast.time 2 10"),"Error:"));
|
||||
assert(!strcmp(f.cmd("get radio.fast.buffer"),"> on"));
|
||||
assert(f.radio.attempts==0 && f.fs.files.empty());
|
||||
// Compact histograms coarsen only the tail. All eleven stage rows and
|
||||
// full-resolution sums/min/max remain unchanged for the same real sample.
|
||||
using namespace mesh::profiletiming;
|
||||
static_assert(StageCount==11,"all timing stages retained");
|
||||
begin(0,1,100);mark(Admission,132);finish(Success,1000);
|
||||
const auto& stats=mesh_hil_profile_timing.direction[0].stage[Whole];
|
||||
assert(stats.count==1 && stats.sum_low_us==900 && stats.sum_high_us==0);
|
||||
assert(stats.min_us==900 && stats.max_us==900 && stats.last_us==900);
|
||||
const unsigned bucket=900/HistogramWidthUs;
|
||||
assert(stats.histogram[bucket<HistogramCount?bucket:HistogramCount-1]==1);
|
||||
assert(strstr(f.cmd("get radio.fast.time 0 10"),"count=1 sum_hi=0 sum_lo=900"));
|
||||
#if MESH_HIL_PROFILE_HISTOGRAM_COUNT == 8
|
||||
assert(strstr(f.cmd("get radio.fast.hist 0 10 0"),"bins=0,0,0,0,0,0,0,1"));
|
||||
for(unsigned size=0;size<=160;++size) {
|
||||
char guarded[162];memset(guarded,0x55,sizeof(guarded));
|
||||
assert(mesh::esp_spiab::handle("hist","0 10 0",0,guarded+1,size));
|
||||
assert(guarded[0]==0x55 && guarded[size+1]==0x55);
|
||||
if(size) assert(memchr(guarded+1,0,size));
|
||||
}
|
||||
#endif
|
||||
assert(f.radio.attempts==0 && f.fs.files.empty());
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
@@ -44,9 +80,15 @@ int main() {
|
||||
encoding='ascii')
|
||||
cpp, binary = work / 'cli.cpp', work / 'cli'
|
||||
cpp.write_text(fixture, encoding='ascii')
|
||||
extra_flags = []
|
||||
if histogram_count is not None:
|
||||
extra_flags.append('-DMESH_HIL_PROFILE_HISTOGRAM_COUNT='+str(histogram_count))
|
||||
if os.name != 'nt':
|
||||
extra_flags += ['-fsanitize=address,undefined', '-fno-sanitize-recover=all',
|
||||
'-fno-pie', '-no-pie']
|
||||
built = subprocess.run([os.environ.get('CXX', 'g++'), '-std=c++17',
|
||||
'-Wall', '-Wextra', '-DESP32_PLATFORM=1', '-DMESH_HIL_PROFILE_TIMING=1',
|
||||
'-DMESH_HIL_ESP_SPI_AB=1', '-include', str(prelude),
|
||||
'-DMESH_HIL_ESP_SPI_AB=1', *extra_flags, '-include', str(prelude),
|
||||
'-I', str(ROOT / 'test/fixtures/radio_profiles'),
|
||||
'-I', str(ROOT / 'test/fixtures/radio_profiles/mocks'),
|
||||
'-I', str(ROOT / 'test/mocks'), '-I', str(ROOT / 'src'),
|
||||
@@ -55,6 +97,25 @@ int main() {
|
||||
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
|
||||
subprocess.run([str(binary)], check=True)
|
||||
|
||||
def test_histogram_geometry_is_private_and_rejects_unsupported_counts(self):
|
||||
source = ('#include <cstdint>\ninline uint32_t micros() { return 0; }\n'
|
||||
'#include <helpers/radiolib/ProfileSwitchTiming.h>\nint main() {}\n')
|
||||
for count in (0, 7, 9, 32):
|
||||
with self.subTest(count=count):
|
||||
built = subprocess.run([os.environ.get('CXX', 'g++'), '-std=c++11',
|
||||
'-DMESH_HIL_PROFILE_TIMING=1', '-DMESH_HIL_PROFILE_HISTOGRAM_COUNT='+str(count),
|
||||
'-x', 'c++', '-fsyntax-only', '-I', str(ROOT / 'src'), '-'],
|
||||
input=source, capture_output=True, text=True)
|
||||
self.assertNotEqual(built.returncode,0)
|
||||
self.assertIn('supports eight or sixteen histogram bins',built.stderr)
|
||||
source = ('#include <helpers/radiolib/ProfileSwitchTiming.h>\n'
|
||||
'#ifdef MESH_HIL_PROFILE_HISTOGRAM_COUNT\n#error HIL geometry leaked\n#endif\n'
|
||||
'int main() {}\n')
|
||||
built = subprocess.run([os.environ.get('CXX', 'g++'), '-std=c++11',
|
||||
'-x', 'c++', '-fsyntax-only', '-I', str(ROOT / 'src'), '-'],
|
||||
input=source, capture_output=True, text=True)
|
||||
self.assertEqual(built.returncode,0,built.stderr)
|
||||
|
||||
def test_live_divider_after_transaction_and_read_only_bounded_snapshots(self):
|
||||
fixture = r'''
|
||||
#include <cassert>
|
||||
|
||||
Reference in new issue
Block a user