Expose actual BLE identity in nRF52 trace diagnostics

This commit is contained in:
mikecarper committed 2026-10-19 13:33:02 -07:00
1 parent c1c0c3178c
commit 042d5b7f0d
3 files changed
+122

No files matched your search

+6
View File
@@ -30,6 +30,7 @@ struct MeshBleTraceBuffer {
};
extern "C" { extern MeshBleTraceBuffer mesh_ble_trace; }
void meshBleTraceState(char* reply, size_t capacity);
void meshBleTraceIdentity(char* reply, size_t capacity);
static inline void meshBleTraceMax(std::atomic<uint32_t>& value, uint32_t sample) {
uint32_t old = value.load(std::memory_order_relaxed);
while (sample > old && !value.compare_exchange_weak(old, sample,
@@ -77,6 +78,11 @@ static inline void meshBleTraceDispatchExit(uint32_t event, uint32_t handle) {
}
static inline bool meshBleTraceCommand(const char* command, char* reply,
size_t capacity) {
if (!strcmp(command, "get bluetooth.trace.identity")
|| !strcmp(command, "get ble.trace.identity")) {
meshBleTraceIdentity(reply, capacity);
return true;
}
if (!strcmp(command, "get bluetooth.trace.state")) {
meshBleTraceState(reply, capacity);
return true;
+42
View File
@@ -52,6 +52,48 @@ void meshBleTraceState(char* reply, size_t capacity) {
if (instance) instance->formatDebugState(reply, capacity);
else snprintf(reply, capacity, "> Bluetooth not initialized");
}
void meshBleTraceIdentity(char* reply, size_t capacity) {
if (!instance) {
snprintf(reply, capacity, "> Bluetooth not initialized");
return;
}
ble_gap_addr_t address = {};
const uint32_t address_status = sd_ble_gap_addr_get(&address);
// Read the real GAP name rather than the application's saved preferences.
// The SDK returns a non-NUL-terminated byte string and the complete length,
// even when it only copies the supplied buffer's bounded prefix.
uint8_t name[32] = {};
uint16_t name_length = sizeof(name);
const uint32_t name_status = sd_ble_gap_device_name_get(name, &name_length);
// The global SVC ABI guard protects these calls under LTO. Also retain the
// explicit output reload barrier used by the other diagnostic SDK queries.
__asm__ __volatile__("" ::: "memory");
static const char hex[] = "0123456789abcdef";
char address_hex[13] = {};
if (address_status == NRF_SUCCESS) {
for (size_t i = 0; i < sizeof(address.addr); ++i) {
const uint8_t byte = address.addr[sizeof(address.addr) - 1 - i];
address_hex[2 * i] = hex[byte >> 4];
address_hex[2 * i + 1] = hex[byte & 15];
}
}
char name_hex[2 * sizeof(name) + 1] = {};
if (name_status == NRF_SUCCESS) {
const size_t copied = name_length < sizeof(name) ? name_length : sizeof(name);
for (size_t i = 0; i < copied; ++i) {
name_hex[2 * i] = hex[name[i] >> 4];
name_hex[2 * i + 1] = hex[name[i] & 15];
}
}
snprintf(reply, capacity,
"> a=%lu,t=%u,mac=%s,n=%lu,len=%u,hex=%s,adv=%u,scan=%u",
(unsigned long)address_status,
address_status == NRF_SUCCESS ? (unsigned)address.addr_type : 255u,
address_hex, (unsigned long)name_status, (unsigned)name_length,
name_hex, (unsigned)Bluefruit.Advertising.count(),
(unsigned)Bluefruit.ScanResponse.count());
}
#endif
static bool formatDeviceName(char* dest, size_t capacity,
+74
View File
@@ -24,6 +24,9 @@ extern "C" { MeshBleTraceBuffer mesh_ble_trace; }
void meshBleTraceState(char* reply, size_t capacity) {
snprintf(reply,capacity,"> state");
}
void meshBleTraceIdentity(char* reply, size_t capacity) {
snprintf(reply,capacity,"> identity");
}
int main() {
char reply[160];
assert(!meshBleTraceCommand("get radio",reply,sizeof(reply)));
@@ -42,6 +45,10 @@ int main() {
assert(!strcmp(reply,"> 257,1234,0x0010,65535,0,0"));
assert(meshBleTraceCommand("get bluetooth.trace.state",reply,sizeof(reply)));
assert(!strcmp(reply,"> state"));
for (const char* command : {"get bluetooth.trace.identity", "get ble.trace.identity"}) {
assert(meshBleTraceCommand(command,reply,sizeof(reply)));
assert(!strcmp(reply,"> identity"));
}
meshBleTraceDispatchEnter(0x10,0);
assert(mesh_ble_trace.dispatch_event.load()==0x10);
meshBleTraceDispatchExit(0x10,0);
@@ -76,6 +83,73 @@ int main() {
check=True, capture_output=True)
subprocess.run([str(binary)],check=True,capture_output=True)
def test_real_identity_query_bounds_sdk_outputs_and_reports_failures(self):
source = (ROOT / "src/helpers/nrf52/SerialBLEInterface.cpp").read_text()
start = source.index("void meshBleTraceIdentity(")
implementation = source[start:source.index("\n}\n", start) + 3]
with tempfile.TemporaryDirectory() as directory:
work = Path(directory)
cpp = work / "identity.cpp"
cpp.write_text(r'''
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <cstring>
static constexpr uint32_t NRF_SUCCESS = 0;
struct ble_gap_addr_t { uint8_t addr_type; uint8_t addr[6]; };
static bool initialized;
static void* instance;
static unsigned calls;
static uint32_t address_result, name_result;
static uint16_t actual_length;
struct Data { uint8_t count() { return 255; } };
static struct { Data Advertising, ScanResponse; } Bluefruit;
static uint32_t sd_ble_gap_addr_get(ble_gap_addr_t* address) {
assert(initialized); ++calls;
*address = {1, {0xc6, 0xbb, 0xcd, 0x86, 0xa0, 0xd1}};
return address_result;
}
static uint32_t sd_ble_gap_device_name_get(uint8_t* name, uint16_t* length) {
assert(initialized && *length == 32); ++calls;
const uint8_t bytes[] = {0x41, 0x00, 0xc3, 0xa9};
const size_t copied = actual_length < *length ? actual_length : *length;
for (size_t i = 0; i < copied; ++i) name[i] = bytes[i % sizeof(bytes)];
*length = actual_length;
return name_result;
}
@IMPLEMENTATION@
int main() {
char reply[172];
meshBleTraceIdentity(reply, sizeof(reply));
assert(!strcmp(reply, "> Bluetooth not initialized") && calls == 0);
initialized = true; instance = &initialized; actual_length = 4;
meshBleTraceIdentity(reply, sizeof(reply));
assert(!strcmp(reply, "> a=0,t=1,mac=d1a086cdbbc6,n=0,len=4,hex=4100c3a9,adv=255,scan=255"));
actual_length = 65535;
meshBleTraceIdentity(reply, sizeof(reply));
assert(strstr(reply, ",len=65535,hex="));
assert(strstr(reply, ",adv=255,scan=255"));
assert(strlen(reply) < sizeof(reply) - 1);
const char* begin = strstr(reply, "hex=") + 4;
assert(strchr(begin, ',') - begin == 64);
address_result = name_result = UINT32_MAX;
meshBleTraceIdentity(reply, sizeof(reply));
assert(!strcmp(reply, "> a=4294967295,t=255,mac=,n=4294967295,len=65535,hex=,adv=255,scan=255"));
char tiny[5] = {};
meshBleTraceIdentity(tiny, sizeof(tiny));
assert(tiny[4] == 0);
}
'''.replace("@IMPLEMENTATION@", implementation))
binary = work / "identity"
compiled = subprocess.run([
"c++", "-std=c++17", "-O2", "-flto", "-Wall", "-Wextra", "-Werror",
"-fsanitize=address,undefined", "-fno-omit-frame-pointer",
"-fno-pie", "-no-pie", str(cpp), "-o", str(binary),
], capture_output=True, text=True)
self.assertEqual(compiled.returncode, 0, compiled.stderr)
result = subprocess.run([str(binary)], capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
def test_concurrent_reader_never_accepts_mixed_records(self):
with tempfile.TemporaryDirectory() as directory:
work = Path(directory)