diff --git a/tools/cgathui_common/ble_etcp.c b/tools/cgathui_common/ble_etcp.c index 14985511..7fbe288c 100644 --- a/tools/cgathui_common/ble_etcp.c +++ b/tools/cgathui_common/ble_etcp.c @@ -51,6 +51,8 @@ struct ble_etcp { struct ble_transport* radio; struct ble_config observer; struct ble_neighbor neighbors[BLE_NEIGHBORS_MAX]; + uint64_t broadcast_received, broadcast_accepted, broadcast_self, broadcast_full; + uint64_t broadcast_invalid[1 - BLE_IDENTITY_ERR_UTF8]; /* Индекс — минус код decode; счётчики за жизнь driver. */ uint64_t retry[BLE_NEIGHBORS_MAX]; struct ble_etcp_link* links[BLE_LINKS_MAX]; ble_link_id rejected[BLE_LINKS_MAX]; /* Закрыть вне BLE callback при отказе выделения ctx. */ @@ -197,8 +199,15 @@ static void service_link(struct ble_etcp_link* c) { } static void broadcast(void* arg, const struct ble_endpoint* endpoint, const uint8_t* bytes, size_t len, int rssi) { struct ble_etcp* d = arg; - struct ble_identity peer; - if (!ble_identity_decode(&peer, bytes, len) && peer.node_id != d->observer.node_id) { + struct ble_identity peer = {0}; + int result = ble_identity_decode(&peer, bytes, len); + const char* reason = ble_identity_result_name(result); + uint64_t count; + int rejected = result != BLE_IDENTITY_OK; + d->broadcast_received++; + if (rejected) count = ++d->broadcast_invalid[-result]; + else if (peer.node_id == d->observer.node_id) { reason = "self-node-id"; count = ++d->broadcast_self; } + else { int slot = -1; uint64_t now = get_time_us(); for (int i = 0; i < BLE_NEIGHBORS_MAX; i++) { if (d->neighbors[i].identity.node_id == peer.node_id) { slot = i; break; } @@ -213,10 +222,35 @@ static void broadcast(void* arg, const struct ble_endpoint* endpoint, const uint } if (n->identity.node_id != peer.node_id || strcmp(n->identity.name, peer.name)) d->peers_changed = 1; *n = (struct ble_neighbor){peer, *endpoint, rssi, now}; schedule(d); - } else DEBUG_DEBUG(CAT, "BLE neighbor table full; ignored peer=%016llx", (unsigned long long)peer.node_id); + reason = "accepted"; count = ++d->broadcast_accepted; + } else { reason = "neighbor-table-full"; count = ++d->broadcast_full; rejected = 1; } + } + /* Первые три случая каждой причины видны без TRACE; все повторы учитываются в сводке. */ + if (count <= 3 || debug_should_output(DEBUG_LEVEL_TRACE, CAT)) { + char detail[384]; + snprintf(detail, sizeof(detail), + "BLE discovery packet endpoint=%s rssi=%d bytes=%zu header=%02x%02x version=%d expected=%u peer=%016llx reason=%s count=%llu", + endpoint->id, rssi, len, bytes && len ? bytes[0] : 0, bytes && len > 1 ? bytes[1] : 0, + bytes && len > 2 ? bytes[2] : -1, BLE_IDENTITY_VERSION, (unsigned long long)peer.node_id, + reason, (unsigned long long)count); + if (count > 3) DEBUG_TRACE(CAT, "%s", detail); + else if (rejected) DEBUG_WARN(CAT, "%s", detail); + else DEBUG_DEBUG(CAT, "%s", detail); } if (d->observer.callbacks.broadcast) d->observer.callbacks.broadcast(d->observer.arg, endpoint, bytes, len, rssi); } +static void broadcast_stats(struct ble_etcp* d, const char* phase) { + DEBUG_DEBUG(CAT, "BLE discovery totals phase=%s ready=%d expected=%u received=%llu accepted=%llu self=%llu full=%llu " + "invalid_argument=%llu invalid_length=%llu foreign_header=%llu version_mismatch=%llu zero_node=%llu invalid_utf8=%llu", + phase, d->ready, BLE_IDENTITY_VERSION, (unsigned long long)d->broadcast_received, + (unsigned long long)d->broadcast_accepted, (unsigned long long)d->broadcast_self, (unsigned long long)d->broadcast_full, + (unsigned long long)d->broadcast_invalid[-BLE_IDENTITY_ERR_ARGUMENT], + (unsigned long long)d->broadcast_invalid[-BLE_IDENTITY_ERR_LENGTH], + (unsigned long long)d->broadcast_invalid[-BLE_IDENTITY_ERR_MAGIC], + (unsigned long long)d->broadcast_invalid[-BLE_IDENTITY_ERR_VERSION], + (unsigned long long)d->broadcast_invalid[-BLE_IDENTITY_ERR_NODE], + (unsigned long long)d->broadcast_invalid[-BLE_IDENTITY_ERR_UTF8]); +} static void connection(void* arg, ble_link_id id, uint64_t node, enum ble_state s, const char* reason) { struct ble_etcp* d = arg; DEBUG_INFO(CAT, "BLE driver connection link=%llu peer=%016llx state=%d reason=%s", @@ -502,6 +536,7 @@ static void run(void* arg) { } static void tick(void* arg) { struct ble_etcp* d = arg; d->timer = NULL; + broadcast_stats(d, "periodic"); uint64_t now = get_time_us(); for (int i = 0; i < BLE_NEIGHBORS_MAX; i++) { struct ble_neighbor* n = &d->neighbors[i]; @@ -539,6 +574,7 @@ struct ble_etcp* ble_etcp_create(struct UASYNC* ua, struct UTUN_INSTANCE* inst, struct ble_transport* ble_etcp_radio(struct ble_etcp* d) { return d ? d->radio : NULL; } void ble_etcp_destroy(struct ble_etcp* d) { if (!d) return; + broadcast_stats(d, "shutdown"); d->stopping = 1; if (d->instance) { for (int i = 0; i < INVITE_PENDING; i++) if (d->invitations[i].request) diff --git a/tools/cgathui_common/tests/test_ble_etcp.c b/tools/cgathui_common/tests/test_ble_etcp.c index f87e7f5c..383778be 100644 --- a/tools/cgathui_common/tests/test_ble_etcp.c +++ b/tools/cgathui_common/tests/test_ble_etcp.c @@ -317,8 +317,18 @@ int main(void) { uint8_t bytes[24]; int len = ble_identity_encode(bytes, endpoints[1].inst.node_id, "Android"); struct ble_endpoint ep = {"1"}; - ble_backend_broadcast(backends[0]->owner, &ep, bytes, len, -42); drain(); struct ble_neighbor neighbors[2]; + /* Нераспознанные объявления и собственный ID не создают соседа/соединение. */ + bytes[2] = BLE_IDENTITY_VERSION - 1; + for (int i = 0; i < 5; i++) ble_backend_broadcast(backends[0]->owner, &ep, bytes, len, -42); + bytes[2] = BLE_IDENTITY_VERSION; + bytes[0] = 'X'; ble_backend_broadcast(backends[0]->owner, &ep, bytes, len, -42); bytes[0] = 'V'; + ble_backend_broadcast(backends[0]->owner, &ep, bytes, 2, -42); + uint8_t self[24]; int self_len = ble_identity_encode(self, endpoints[0].inst.node_id, "self"); + ble_backend_broadcast(backends[0]->owner, &ep, self, self_len, -42); drain(); + assert(ble_etcp_neighbors(endpoints[0].driver, neighbors, 2) == 0); + assert(!endpoints[0].inst.connections->head); + ble_backend_broadcast(backends[0]->owner, &ep, bytes, len, -42); drain(); assert(ble_etcp_neighbors(endpoints[0].driver, neighbors, 2) == 1); assert(neighbors[0].identity.node_id == endpoints[1].inst.node_id && neighbors[0].rssi == -42); assert(!strcmp(neighbors[0].endpoint.id, "1"));