mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feat/ble-log-test-app-support' into 'master'
feat(ble_log): add BLE log perf test app with test transport See merge request espressif/esp-idf!51767
This commit is contained in:
@@ -88,6 +88,7 @@ endif()
|
||||
set(bt_priv_requires
|
||||
nvs_flash
|
||||
soc
|
||||
esp_hw_support
|
||||
esp_pm
|
||||
esp_phy
|
||||
esp_coex
|
||||
|
||||
@@ -160,6 +160,10 @@ if(CONFIG_BLE_LOG_ENABLED)
|
||||
list(APPEND bt_common_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ble_log/src/prph/ble_log_prph_dummy.c"
|
||||
)
|
||||
elseif(CONFIG_BLE_LOG_PRPH_TEST)
|
||||
list(APPEND bt_common_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ble_log/src/prph/ble_log_prph_test.c"
|
||||
)
|
||||
elseif(CONFIG_BLE_LOG_PRPH_SPI_MASTER_DMA)
|
||||
list(APPEND bt_common_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ble_log/src/prph/ble_log_prph_spi_master_dma.c"
|
||||
|
||||
@@ -198,6 +198,13 @@ if BLE_LOG_ENABLED
|
||||
help
|
||||
Dummy transport (dump only)
|
||||
|
||||
config BLE_LOG_PRPH_TEST
|
||||
bool "Test only transport"
|
||||
help
|
||||
Test transport that models DMA ownership and optional
|
||||
link-rate backpressure. Selected by BLE Log test apps
|
||||
only; not shown in menuconfig.
|
||||
|
||||
config BLE_LOG_PRPH_SPI_MASTER_DMA
|
||||
bool "Utilize SPI master DMA driver as transport"
|
||||
depends on SOC_GPSPI_SUPPORTED
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef __BLE_LOG_PRPH_TEST_H__
|
||||
#define __BLE_LOG_PRPH_TEST_H__
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
/* BLE Log - Peripheral-specific Transport - Test */
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
/* INCLUDE */
|
||||
#include "ble_log_prph.h"
|
||||
|
||||
/* TYPEDEF */
|
||||
typedef struct {
|
||||
uint8_t *trans_buf;
|
||||
} ble_log_prph_trans_ctx_t;
|
||||
|
||||
/* Reads and releases one pending transport, returning the bytes copied.
|
||||
* When bytes_per_second is non-zero the call blocks until the simulated
|
||||
* link has transmitted the whole transport at that rate. */
|
||||
size_t ble_log_prph_test_read(uint8_t *data, size_t len, TickType_t timeout,
|
||||
uint32_t bytes_per_second);
|
||||
|
||||
#endif /* __BLE_LOG_PRPH_TEST_H__ */
|
||||
190
components/bt/common/ble_log/src/prph/ble_log_prph_test.c
Normal file
190
components/bt/common/ble_log/src/prph/ble_log_prph_test.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/* ---------------------------------------------- */
|
||||
/* BLE Log - Peripheral-specific Transport - Test */
|
||||
/* ---------------------------------------------- */
|
||||
|
||||
/* INCLUDE */
|
||||
#include "ble_log_prph_test.h"
|
||||
#include "ble_log_lbm.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
/* VARIABLE */
|
||||
static QueueHandle_t s_pending_trans;
|
||||
static SemaphoreHandle_t s_tx_done;
|
||||
static esp_timer_handle_t s_tx_timer;
|
||||
static int64_t s_tx_deadline_us;
|
||||
static uint32_t s_tx_rate;
|
||||
|
||||
static void test_tx_done(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
xSemaphoreGive(s_tx_done);
|
||||
}
|
||||
|
||||
/* INTERFACE */
|
||||
bool ble_log_prph_init(size_t trans_cnt)
|
||||
{
|
||||
if (s_pending_trans) {
|
||||
return true;
|
||||
}
|
||||
|
||||
s_pending_trans = xQueueCreate(trans_cnt, sizeof(ble_log_prph_trans_t *));
|
||||
s_tx_done = xSemaphoreCreateBinary();
|
||||
const esp_timer_create_args_t timer_args = {
|
||||
.callback = test_tx_done,
|
||||
.name = "ble_log_test",
|
||||
};
|
||||
if (!s_pending_trans || !s_tx_done ||
|
||||
esp_timer_create(&timer_args, &s_tx_timer) != ESP_OK) {
|
||||
ble_log_prph_deinit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ble_log_prph_deinit(void)
|
||||
{
|
||||
s_tx_deadline_us = 0;
|
||||
s_tx_rate = 0;
|
||||
if (s_tx_timer) {
|
||||
esp_timer_stop(s_tx_timer);
|
||||
esp_timer_delete(s_tx_timer);
|
||||
s_tx_timer = NULL;
|
||||
}
|
||||
if (s_tx_done) {
|
||||
vSemaphoreDelete(s_tx_done);
|
||||
s_tx_done = NULL;
|
||||
}
|
||||
if (!s_pending_trans) {
|
||||
return;
|
||||
}
|
||||
|
||||
ble_log_prph_trans_t *trans;
|
||||
while (xQueueReceive(s_pending_trans, &trans, 0) == pdTRUE) {
|
||||
ble_log_lbm_recycle_trans(trans);
|
||||
}
|
||||
vQueueDelete(s_pending_trans);
|
||||
s_pending_trans = NULL;
|
||||
}
|
||||
|
||||
bool ble_log_prph_trans_init(ble_log_prph_trans_t **trans, size_t trans_size)
|
||||
{
|
||||
/* Validate inputs */
|
||||
if (!trans || !trans_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize peripheral transport data */
|
||||
*trans = (ble_log_prph_trans_t *)BLE_LOG_MALLOC(sizeof(ble_log_prph_trans_t));
|
||||
if (!(*trans)) {
|
||||
goto exit;
|
||||
}
|
||||
BLE_LOG_MEMSET(*trans, 0, sizeof(ble_log_prph_trans_t));
|
||||
(*trans)->size = trans_size;
|
||||
|
||||
/* Initialize peripheral-specific transport context */
|
||||
ble_log_prph_trans_ctx_t *test_trans_ctx = (ble_log_prph_trans_ctx_t *)BLE_LOG_MALLOC(sizeof(ble_log_prph_trans_ctx_t));
|
||||
if (!test_trans_ctx) {
|
||||
goto exit;
|
||||
}
|
||||
BLE_LOG_MEMSET(test_trans_ctx, 0, sizeof(ble_log_prph_trans_ctx_t));
|
||||
(*trans)->ctx = (void *)test_trans_ctx;
|
||||
|
||||
/* Initialize log buffer */
|
||||
(*trans)->buf = (uint8_t *)BLE_LOG_MALLOC(trans_size);
|
||||
if (!(*trans)->buf) {
|
||||
goto exit;
|
||||
}
|
||||
BLE_LOG_MEMSET((*trans)->buf, 0, trans_size);
|
||||
test_trans_ctx->trans_buf = (*trans)->buf;
|
||||
return true;
|
||||
|
||||
exit:
|
||||
ble_log_prph_trans_deinit(trans);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ble_log_prph_trans_deinit(ble_log_prph_trans_t **trans)
|
||||
{
|
||||
/* Validate inputs */
|
||||
if (!trans || !(*trans)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Release log buffer */
|
||||
if ((*trans)->buf) {
|
||||
BLE_LOG_FREE((*trans)->buf);
|
||||
}
|
||||
|
||||
/* Release peripheral-specific transport context */
|
||||
if ((*trans)->ctx) {
|
||||
BLE_LOG_FREE((*trans)->ctx);
|
||||
}
|
||||
|
||||
/* Release peripheral transport data */
|
||||
BLE_LOG_FREE(*trans);
|
||||
*trans = NULL;
|
||||
}
|
||||
|
||||
/* Model DMA ownership transfer: the test peripheral owns the transaction
|
||||
* after enqueueing it. ble_log_prph_test_read() consumes and recycles it,
|
||||
* equivalent to a real peripheral's asynchronous tx_done callback. */
|
||||
void ble_log_prph_send_trans(ble_log_prph_trans_t *trans)
|
||||
{
|
||||
if (xQueueSend(s_pending_trans, &trans, 0) != pdTRUE) {
|
||||
trans->pos = 0;
|
||||
ble_log_lbm_recycle_trans(trans);
|
||||
BLE_LOG_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ble_log_prph_test_read(uint8_t *data, size_t len, TickType_t timeout,
|
||||
uint32_t bytes_per_second)
|
||||
{
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ble_log_prph_trans_t *trans;
|
||||
if (xQueueReceive(s_pending_trans, &trans, timeout) != pdTRUE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytes_per_second) {
|
||||
int64_t now_us = esp_timer_get_time();
|
||||
uint64_t tx_time_us = ((uint64_t)trans->pos * 1000000ULL +
|
||||
bytes_per_second - 1) / bytes_per_second;
|
||||
if (!s_tx_deadline_us || s_tx_rate != bytes_per_second) {
|
||||
s_tx_deadline_us = now_us;
|
||||
}
|
||||
s_tx_rate = bytes_per_second;
|
||||
s_tx_deadline_us += (int64_t)tx_time_us;
|
||||
int64_t delay_us = s_tx_deadline_us - now_us;
|
||||
if (delay_us > 0 &&
|
||||
(esp_timer_start_once(s_tx_timer, (uint64_t)delay_us) != ESP_OK ||
|
||||
xSemaphoreTake(s_tx_done, portMAX_DELAY) != pdTRUE)) {
|
||||
trans->pos = 0;
|
||||
ble_log_lbm_recycle_trans(trans);
|
||||
BLE_LOG_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
s_tx_deadline_us = 0;
|
||||
s_tx_rate = 0;
|
||||
}
|
||||
|
||||
size_t copied = len < trans->pos ? len : trans->pos;
|
||||
BLE_LOG_MEMCPY(data, trans->buf, copied);
|
||||
trans->pos = 0;
|
||||
ble_log_lbm_recycle_trans(trans);
|
||||
if (uxQueueMessagesWaiting(s_pending_trans) == 0) {
|
||||
s_tx_deadline_us = 0;
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
components/bt/common/ble_log/test_apps:
|
||||
disable:
|
||||
- if: IDF_TARGET != "none"
|
||||
temporary: true
|
||||
reason: No BLE Log test runners are available yet
|
||||
depends_components:
|
||||
- bt
|
||||
@@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
list(PREPEND SDKCONFIG_DEFAULTS
|
||||
"$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers"
|
||||
"sdkconfig.defaults")
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ble_log_test)
|
||||
@@ -0,0 +1,65 @@
|
||||
# BLE Log Performance Test App
|
||||
|
||||
## Overview
|
||||
|
||||
Performance test app for the ESP-IDF BLE Log module. It measures the system-level
|
||||
behavior and per-API CPU cost of the Log Buffer Management (LBM) layer, so that
|
||||
two LBM designs can be compared before and after an optimization.
|
||||
|
||||
The test runs on real hardware (any BLE-capable chip, currently tested on
|
||||
ESP32-C6). The transport is replaced by a software model
|
||||
(`CONFIG_BLE_LOG_PRPH_TEST=y`) that mimics DMA ownership transfer and link
|
||||
bandwidth, so the measurements isolate the LBM layer itself.
|
||||
|
||||
## What Is Measured
|
||||
|
||||
| Dimension | Metrics |
|
||||
| --- | --- |
|
||||
| Throughput | sustained B/s against simulated links of 2 Mbps / 20 Mbps / unlimited, under a mixed load of all writer types |
|
||||
| Frame loss | per-writer failed counts cross-checked against the LBM's own lost-frame statistics; both must match |
|
||||
| API cycles | per-call CPU cycles of `ble_log_write_hex`, `ble_log_write_hex_ll` (task/ISR/append), and the compressed-log entry points, with avg/p50/p95/p99/max |
|
||||
| Compression split | encode vs downstream write_hex cost, derived from a companion writer at the same record length (no instrumentation in the module) |
|
||||
|
||||
## Test Cases
|
||||
|
||||
- `throughput`: fixed 32B / 64B / 128B / mixed 8-64B payload profiles, each at
|
||||
2 Mbps, 20 Mbps, and unlimited link. Runs 3 write_hex writers + LL task + LL
|
||||
HCI + compressed writer + 1 kHz ISR writer concurrently.
|
||||
- `write_hex cycles`: single writer, no link cap, payload 8/32/64/128 B.
|
||||
- `write_hex drop path cycles`: saturated 2 Mbps link, measures the cost of a
|
||||
failed (dropped) write.
|
||||
- `write_hex_ll cycles`: payload 8/32/64/128 B; plus a 32+32 B append case.
|
||||
- `compressed write cycles`: workload matrix of the compressed entry points —
|
||||
U32 args (0/1/2/mixed), U64 values (full 8B / leading-zero LZ / zero),
|
||||
strings (8B / 128B), raw buffer (128B), and the `hex_printv` va_list entry.
|
||||
|
||||
## Build, Flash, Run
|
||||
|
||||
```bash
|
||||
cd components/bt/common/ble_log/test_apps/ble_log_perf_test
|
||||
idf.py set-target <chip>
|
||||
idf.py -p <PORT> build flash monitor
|
||||
```
|
||||
|
||||
The app boots into the Unity menu; enter a test number to run it. Every run
|
||||
prints a `BLE_LOG_PERF` block (blank-line separated) with writer stats,
|
||||
throughput, flush cost, and LBM statistics.
|
||||
|
||||
## Parsing Results
|
||||
|
||||
Capture the monitor output and turn it into tables/CSV:
|
||||
|
||||
```bash
|
||||
idf.py -p <PORT> monitor | tee capture.log
|
||||
python3 tools/parse_perf_log.py capture.log # markdown tables
|
||||
python3 tools/parse_perf_log.py capture.log --csv out.csv
|
||||
```
|
||||
|
||||
Run the same capture twice (old vs new LBM) and diff the CSV.
|
||||
|
||||
## Supported Targets
|
||||
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |
|
||||
|
||||
CI builds are temporarily disabled until BLE Log test runners are available.
|
||||
@@ -0,0 +1,13 @@
|
||||
idf_component_register(
|
||||
SRCS "test_ble_log_main.c" "test_ble_log_perf.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity bt esp_driver_gpio esp_driver_spi esp_driver_uart esp_hw_support esp_timer
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
idf_component_get_property(bt_dir bt COMPONENT_DIR)
|
||||
target_include_directories(${COMPONENT_LIB} PRIVATE
|
||||
"${bt_dir}/common/ble_log/include"
|
||||
"${bt_dir}/common/ble_log/src/internal_include"
|
||||
"${bt_dir}/common/ble_log/src/internal_include/prph"
|
||||
)
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
|
||||
#include "ble_log.h"
|
||||
#include "ble_log_lbm.h"
|
||||
#include "test_ble_log_main.h"
|
||||
|
||||
bool test_ble_log_walk_frames(const uint8_t *data, size_t len,
|
||||
test_ble_log_frame_observer_t observer, void *ctx)
|
||||
{
|
||||
size_t offset = 0;
|
||||
while (len - offset >= BLE_LOG_FRAME_OVERHEAD) {
|
||||
ble_log_frame_head_t head;
|
||||
memcpy(&head, data + offset, sizeof(head));
|
||||
|
||||
size_t frame_len = BLE_LOG_FRAME_OVERHEAD + head.length;
|
||||
if (frame_len > len - offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t checksum;
|
||||
memcpy(&checksum, data + offset + BLE_LOG_FRAME_HEAD_LEN + head.length,
|
||||
sizeof(checksum));
|
||||
if (checksum != ble_log_fast_checksum(data + offset,
|
||||
BLE_LOG_FRAME_HEAD_LEN + head.length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (observer) {
|
||||
test_ble_log_frame_t frame = {
|
||||
.src = head.frame_meta & 0xff,
|
||||
.sn = head.frame_meta >> 8,
|
||||
.payload = data + offset + BLE_LOG_FRAME_HEAD_LEN,
|
||||
.payload_len = head.length,
|
||||
};
|
||||
observer(&frame, ctx);
|
||||
}
|
||||
offset += frame_len;
|
||||
}
|
||||
return offset == len;
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* The BLE Log module has no automatic system init on this branch; the
|
||||
* controller normally calls ble_log_init(). Initialize it explicitly. */
|
||||
TEST_ASSERT_TRUE_MESSAGE(ble_log_init(), "BLE Log init failed");
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ble_log.h"
|
||||
|
||||
typedef struct {
|
||||
ble_log_src_t src;
|
||||
uint32_t sn;
|
||||
const uint8_t *payload;
|
||||
size_t payload_len;
|
||||
} test_ble_log_frame_t;
|
||||
|
||||
typedef void (*test_ble_log_frame_observer_t)(const test_ble_log_frame_t *frame, void *ctx);
|
||||
|
||||
/* Walks a captured transport buffer, validating frame headers and checksums.
|
||||
* Returns true when the whole buffer consists of valid frames. */
|
||||
bool test_ble_log_walk_frames(const uint8_t *data, size_t len,
|
||||
test_ble_log_frame_observer_t observer, void *ctx);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BLE_LOG_ENABLED=y
|
||||
CONFIG_BLE_LOG_PRPH_TEST=y
|
||||
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n
|
||||
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||
# esp_timer ISR dispatch is used for the periodic ISR-context writer
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
# 64-bit assertions used by the perf test statistics checks
|
||||
CONFIG_UNITY_ENABLE_64BIT=y
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Turn captured BLE Log perf monitor output into tables / CSV.
|
||||
|
||||
Capture with: idf.py -p PORT monitor | tee capture.log
|
||||
Then: python3 parse_perf_log.py capture.log # markdown tables
|
||||
python3 parse_perf_log.py capture.log --csv out.csv
|
||||
"""
|
||||
|
||||
import csv
|
||||
import re
|
||||
import sys
|
||||
from typing import TypedDict
|
||||
|
||||
KV = re.compile(r'(\w+)=(\S+)')
|
||||
|
||||
WRITER_COLS = ('frames', 'failed', 'avg', 'avg_failed', 'p50', 'p95', 'p99', 'max')
|
||||
|
||||
|
||||
class Run(TypedDict):
|
||||
head: dict[str, str]
|
||||
writers: list[dict[str, str]]
|
||||
other: list[str]
|
||||
|
||||
|
||||
def fields(line: str) -> dict[str, str]:
|
||||
return dict(KV.findall(line))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
return 1
|
||||
path = sys.argv[1]
|
||||
csv_path = None
|
||||
if '--csv' in sys.argv:
|
||||
csv_path = sys.argv[sys.argv.index('--csv') + 1]
|
||||
|
||||
with open(path, encoding='utf-8', errors='replace') as f:
|
||||
lines = f.read().splitlines()
|
||||
|
||||
runs: list[Run] = []
|
||||
cur: Run | None = None
|
||||
for line in lines:
|
||||
if not line.startswith('BLE_LOG_PERF '):
|
||||
continue
|
||||
kv = fields(line)
|
||||
if line.startswith('BLE_LOG_PERF ===='):
|
||||
if ' RUN [' in line:
|
||||
cur = Run(head=kv, writers=[], other=[])
|
||||
runs.append(cur)
|
||||
# END separator: keep context, nothing to record
|
||||
continue
|
||||
if cur is None:
|
||||
continue
|
||||
if 'writer' in kv:
|
||||
cur['writers'].append(kv)
|
||||
elif 'mode' in kv:
|
||||
cur['head'].update(kv) # duration etc.
|
||||
else:
|
||||
cur['other'].append(line[len('BLE_LOG_PERF ') :])
|
||||
|
||||
if not runs:
|
||||
print(f'no BLE_LOG_PERF lines found in {path}')
|
||||
return 1
|
||||
|
||||
for i, run in enumerate(runs, 1):
|
||||
h = run['head']
|
||||
title = (
|
||||
f'## Run {i}: mode={h.get("mode", "?")} '
|
||||
f'profile={h.get("profile", "?")} '
|
||||
f'link={h.get("link", "?")} isolate={h.get("isolate", "?")}'
|
||||
)
|
||||
print(title)
|
||||
if run['writers']:
|
||||
header = '| writer | ' + ' | '.join(WRITER_COLS) + ' |'
|
||||
print(header)
|
||||
print('|---|' + '---|' * len(WRITER_COLS))
|
||||
for w in run['writers']:
|
||||
row = ' | '.join(w.get(c, '-') for c in WRITER_COLS)
|
||||
print(f'| {w.get("writer", "?")} | {row} |')
|
||||
for o in run['other']:
|
||||
print(f'- `{o}`')
|
||||
print()
|
||||
|
||||
if csv_path:
|
||||
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
|
||||
wcsv = csv.writer(f)
|
||||
wcsv.writerow(['run', 'mode', 'profile', 'link', 'isolate', 'writer', *WRITER_COLS])
|
||||
for i, run in enumerate(runs, 1):
|
||||
h = run['head']
|
||||
for w in run['writers']:
|
||||
wcsv.writerow(
|
||||
[
|
||||
i,
|
||||
h.get('mode', ''),
|
||||
h.get('profile', ''),
|
||||
h.get('link', ''),
|
||||
h.get('isolate', ''),
|
||||
w.get('writer', ''),
|
||||
*(w.get(c, '') for c in WRITER_COLS),
|
||||
]
|
||||
)
|
||||
print(f'CSV written to {csv_path}')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user