From 33b4f0acc82014fde976a52b0096ac97ff5e5477 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 29 Oct 2026 18:38:11 +0100 Subject: [PATCH] Record BLE throughput results and harden the hardware probe --- doc/ble_etcp.txt | 34 ++++++++++++++++ doc/tasks.md | 15 +++++++ tools/cgathui_common/tests/ble_etcp_probe.c | 44 +++++++++++++++++---- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/doc/ble_etcp.txt b/doc/ble_etcp.txt index a183e9bc..78d1f985 100644 --- a/doc/ble_etcp.txt +++ b/doc/ble_etcp.txt @@ -99,6 +99,11 @@ Instrumentation восстанавливает Bluetooth-политику в fin THROUGHPUT считает verified_bytes_each_direction и aggregate_bytes_per_second за время от первого send до последнего проверенного echo, без GATT/ETCP handshake. Это двунаправленный echo goodput, не скорость PHY. Неполный прогон имеет ненулевой exit. +При неполном прогоне THROUGHPUT complete=0 учитывает всё окно до остановки, +включая простой после последнего echo; RESULT failed=1 и ERROR содержат target/sent/verified. +До первого send разрыв/смена адреса возвращает пробу к discovery; разрыв во время +передачи завершает её с ошибкой. UTUN_TEST_DEBUG=1 включает ETCP trace/connection debug +на desktop для подробного разбора ACK/RTO без изменения штатных фильтров приложения. Перед пробой выключить BLE обычного desktop GUI: два сервиса с одинаковыми UUID на одном адаптере делают GATT discovery неоднозначным. @@ -165,3 +170,32 @@ LAN-приглашения или исходящую отправку. Ново Это не полный нагрузочный проход обратной роли. - Windows backend проверен кросс-компиляцией MinGW/C++WinRT, без физического Win BT. - Общие тесты: 230/230; отдельные BLE/BlueZ/UI и ASAN/UBSAN пройдены. + +Write Without Response / Notification: проверка 2026-10-29 +-------------------------------------------------------- +Android, BlueZ и WinRT используют быстрый DATA, INVITE сохраняет Request/Indication. +Android HIGH/MTU517/2M сохранены; WinRT запрашивает ThroughputOptimized при наличии +API Windows 11. BlueZ D-Bus не предоставляет аналогичного per-link профиля. +Desktop GUI/Linux, Android APK+instrumentation (arm64/x86_64), WinRT cross-build PASS. +./check.sh: 230/230 + proxy/burst/load; BLE common/BlueZ/identity/ETCP/UI: 5/5; +BLE ASAN/UBSAN: 3/3; Android app/router unit tests: 770/770. + +Телефон RZ8R72MG64B, один desktop BLE-сервис на адаптере: +- Android central: 512/512 ETCP echo по 1500 байт, window=16, по 768000 байт + в каждом направлении за 61.694 с; суммарный goodput 24897 B/s (12448 B/s + на направление). Приглашения 939 байт и ACK приложений прошли в обе стороны. + PHY2M TX/RX, MTU517; для Android central подтверждён interval=12 (15 ms). +- Сырой BLE, Android central: 96/96 echo, по 45636 байт за 4.473 с, + суммарно 20407 B/s. Размеры пакетов различаются, напрямую с ETCP не сравнивать. +- Desktop central: приглашения/ACK прошли, но за окно 85 с ETCP подтвердил + только 20/512 echo. Повтор без приглашений, window=1: 0/32 echo за окно 65 с, + после первого send 50.060 с повторялись INIT/INIT_RESPONSE и DATA RTO, + DATA_RTT_us=0; BLE-сборщик не регистрировал отбросов. Причина не установлена; + исправление этого сбоя не входит в подтверждённый результат профиля скорости. + +Журналы текущего сеанса: /tmp/ble-speed-android-central-{desktop,logcat}.log, +/tmp/ble-speed-desktop-central-{desktop,logcat}.log, +/tmp/ble-speed-desktop-central-serial-retry-desktop.log и +/tmp/ble-speed-desktop-central-serial-live-logcat.log. +После аппаратных проб обычный desktop VibeChat перезапущен новой сборкой; +Android instrumentation восстановила обычную Bluetooth-политику в finally. diff --git a/doc/tasks.md b/doc/tasks.md index f529d74e..5cc7c6d2 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -4,6 +4,21 @@ ## Текущие задачи +[ ] **BLE/ETCP: повторные INIT и отсутствие DATA ACK при desktop central** — 29.10.2026. + После включения Write Without Response/Notification GATT/MTU517 и PHY2M работают, + но desktop central подтвердил 20/512 echo за 85 с; повтор window=1 — 0/32 за 65 с. + На desktop повторяются INIT/INIT_RESPONSE и DATA RTO с DATA_RTT_us=0; + BLE-сборщик не регистрирует потерь фрагментов. Точная причина не установлена. + Android central: 512/512 echo и приглашения в обе стороны, суммарный goodput 24897 B/s. + Результаты, параметры проб и пути диагностических журналов: [ble_etcp.txt](ble_etcp.txt). + +[+] **BLE: DATA без ATT ACK и профиль скорости desktop/Android** — 29.10.2026. + Write Without Response/Notification для DATA в Android/BlueZ/WinRT; INVITE сохраняет Request/Indication. + Android HIGH/MTU517/2M, Windows ThroughputOptimized с проверкой API и фактическими параметрами. + Сводка скорости/очередей/ожиданий GATT; нагрузочная проба с ограниченным окном и проверкой содержимого. + Общие тесты 230/230 + proxy/burst/load, BLE 5/5, ASAN/UBSAN 3/3, Android unit 770/770. + Desktop и APK собираются; WinRT cross-build PASS. Ограничение обратной роли вынесено выше. + [ ] **Windows: единые UTF-8 пути конфигурации** — 29.10.2026. GUI передаёт toUtf8, а config_parser/config_updater используют fopen и ANSI Win32 API. Перевести эти операции на существующий file_io с wide-путями, проверить профиль в каталоге с кириллицей. diff --git a/tools/cgathui_common/tests/ble_etcp_probe.c b/tools/cgathui_common/tests/ble_etcp_probe.c index c6c0121d..30e860ee 100644 --- a/tools/cgathui_common/tests/ble_etcp_probe.c +++ b/tools/cgathui_common/tests/ble_etcp_probe.c @@ -18,7 +18,7 @@ static unsigned verified; static uint64_t transfer_started, transfer_finished; static int invitations, invitation_sent, invitation_received, invitation_delivered; static char invitation_link[LOCAL_TRANSPORT_DATA_MAX + 1]; -static int failed, radio_ready; +static int failed, radio_ready, connection_closed; static struct ble_etcp* driver; static struct UTUN_INSTANCE* instance; static void event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int len) { @@ -37,8 +37,15 @@ static void event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int } static void receive(struct ETCP_CONN* conn, struct ll_entry* e) { (void)conn; - if (e->len != 1500) failed = 1; - for (unsigned i = 1; i < e->len; i++) if (e->dgram[i] != (uint8_t)(verified + i * 37)) failed = 1; + if (e->len != 1500) { + DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "BLE probe size mismatch packet=%u expected=1500 actual=%u", verified, e->len); + failed = 1; + } + for (unsigned i = 1; i < e->len; i++) if (e->dgram[i] != (uint8_t)(verified + i * 37)) { + DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "BLE probe byte mismatch packet=%u offset=%u expected=%u actual=%u", + verified, i, (uint8_t)(verified + i * 37), e->dgram[i]); + failed = 1; break; + } DEBUG_TRACE(DEBUG_CATEGORY_DISCOVERY, "VERIFY ETCP packet=%u bytes=%u ok=%d", verified, e->len, !failed); verified++; transfer_finished = get_time_us(); queue_dgram_free(e); queue_entry_free(e); @@ -51,6 +58,10 @@ static void status(void* arg, enum ble_state s, const char* reason) { if (ble_advertise(ble_etcp_radio(driver), bytes, n)) failed = 1; } } +static void connection(void* arg, ble_link_id id, uint64_t peer, enum ble_state state, const char* reason) { + (void)arg; (void)id; (void)peer; (void)reason; + if (state == BLE_CLOSED) connection_closed = 1; +} int main(int argc, char** argv) { int seconds = argc > 1 ? atoi(argv[1]) : 60; unsigned packets = argc > 4 ? (unsigned)atoi(argv[4]) : 32; @@ -64,6 +75,10 @@ int main(int argc, char** argv) { snprintf(config, sizeof(config), "[global]\nmy_private_key=%s\nmy_public_key=%s\nauto_sockets=no\n[ntp]\nenabled=no\n", priv, pub); instance = utun_instance_create_from_str(ua, config); if (!instance) { uasync_destroy(ua, 0); return 1; } + if (getenv("UTUN_TEST_DEBUG")) { + debug_set_category_level(DEBUG_CATEGORY_ETCP, DEBUG_LEVEL_TRACE); + debug_set_category_level(DEBUG_CATEGORY_CONNECTION, DEBUG_LEVEL_DEBUG); + } invitations = argc > 3 && !strcmp(argv[3], "invites"); if (invitations) { struct InviteData invite = {.channelId = 0x424c4554455354ULL, .join_key = 123, .addrCount = 24}; @@ -75,7 +90,8 @@ int main(int argc, char** argv) { if (invite_link_encode(&invite, NULL, invitation_link, sizeof(invitation_link)) < 0) failed = 1; chat_event_set_handler(instance, event); chat_setting_set(instance, "bluetooth_invites_enabled", "1"); } - struct ble_config c = {.node_id = instance->node_id, .company_id = 0xffff}; c.callbacks.status = status; + struct ble_config c = {.node_id = instance->node_id, .company_id = 0xffff}; + c.callbacks.status = status; c.callbacks.connection = connection; driver = ble_etcp_create(ua, instance, &c); if (!driver || etcp_bind(instance, ETCP_ID_BLE_PROBE, receive) || ble_start(ble_etcp_radio(driver))) failed = 1; uint64_t end = get_time_us() + (uint64_t)seconds * 1000000, peer = 0, retry = 0; @@ -84,6 +100,12 @@ int main(int argc, char** argv) { while (!failed && (verified < packets || (invitations && (!invitation_received || !invitation_delivered))) && get_time_us() < end) { uasync_poll(ua, 100); if (failed) break; + if (connection_closed) { + connection_closed = 0; + if (transfer_started) { DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "BLE probe connection lost during transfer"); failed = 1; break; } + DEBUG_INFO(DEBUG_CATEGORY_DISCOVERY, "BLE probe connection closed before transfer; retrying discovery"); + peer = 0; retry = get_time_us() + 1000000; + } if (!peer && dial && radio_ready && get_time_us() >= retry) { struct ble_neighbor neighbors[BLE_NEIGHBORS_MAX]; size_t n = ble_etcp_neighbors(driver, neighbors, BLE_NEIGHBORS_MAX); for (size_t i = 0; i < n; i++) if (!strcmp(neighbors[i].identity.name, "ETCP-probe")) { @@ -110,12 +132,18 @@ int main(int argc, char** argv) { if (etcp_send(conn, e)) { queue_dgram_free(e); queue_entry_free(e); failed = 1; } else { if (!transfer_started) transfer_started = get_time_us(); sent++; } } + uint64_t measured_end = verified == packets ? transfer_finished : get_time_us(); + if (verified != packets || (invitations && (!invitation_delivered || invitation_received != 1))) { + DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "BLE probe incomplete target=%u sent=%u verified=%u invite_delivered=%d invite_received=%d", + packets, sent, verified, invitation_delivered, invitation_received); + failed = 1; + } ble_etcp_destroy(driver); utun_instance_destroy(instance); uasync_destroy(ua, 0); printf("RESULT BLE ETCP sent=%u verified=%u bytes=%u failed=%d\n", sent, verified, verified * 1500, failed); - if (transfer_started && transfer_finished > transfer_started) { - double elapsed = (transfer_finished - transfer_started) / 1000000.0; - printf("THROUGHPUT window=%u verified_bytes_each_direction=%u seconds=%.3f aggregate_bytes_per_second=%.0f\n", - window, verified * 1500, elapsed, verified * 3000.0 / elapsed); + if (transfer_started && measured_end > transfer_started) { + double elapsed = (measured_end - transfer_started) / 1000000.0; + printf("THROUGHPUT complete=%d window=%u verified_bytes_each_direction=%u seconds=%.3f aggregate_bytes_per_second=%.0f\n", + !failed, window, verified * 1500, elapsed, verified * 3000.0 / elapsed); } if (invitations) printf("RESULT BLE invitations sent=%d delivered=%d received=%d\n", invitation_sent, invitation_delivered, invitation_received); return failed || verified != packets || (invitations && (!invitation_delivered || invitation_received != 1));