Bluetooth: Host: Add advertising events stress test

More information in the test description.

Why: At the time of writing, this test exposes a bug in the Bluetooth stack
where all the RX buffers are in use, and the controller driver fails to
synchronously allocate a command response buffer.

This is manifested in an assertion failure in `hci_common.c`.

Signed-off-by: Jonathan Rico <jonathan.rico@nordicsemi.no>
This commit is contained in:
Jonathan Rico 2024-09-10 18:15:10 +02:00 committed by Fabio Baltieri
commit edeb529c9f
8 changed files with 295 additions and 0 deletions

View file

@ -40,5 +40,6 @@ run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/privacy/legacy/compil
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/id/settings/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/scan/start_stop/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/scan/slow/compile.sh
wait_for_background_jobs

View file

@ -0,0 +1,27 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
project(slow)
# This contains a variety of helper functions that abstract away common tasks,
# like scanning, setting up a connection, querying the peer for a given
# characteristic, etc..
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
target_link_libraries(app PRIVATE testlib)
# This contains babblesim-specific helpers, e.g. device synchronization.
add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit)
target_link_libraries(app PRIVATE babblekit)
zephyr_include_directories(
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)
target_sources(app PRIVATE
src/main.c
src/peer.c
src/dut.c
)

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
set -eu
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}"
INCR_BUILD=1
source ${ZEPHYR_BASE}/tests/bsim/compile.source
app="$(guess_test_relpath)" compile
wait_for_background_jobs

View file

@ -0,0 +1,21 @@
CONFIG_BT=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_DEVICE_NAME="Scanner Test"
# Enable privacy with frequent address refreshes
CONFIG_BT_SMP=y
CONFIG_BT_PRIVACY=y
CONFIG_BT_RPA_TIMEOUT=2
# Log & debug options
CONFIG_LOG=y
CONFIG_LOG_RUNTIME_FILTERING=y
CONFIG_LOG_THREAD_ID_PREFIX=y
CONFIG_ASSERT=y
CONFIG_THREAD_NAME=y
CONFIG_LOG_THREAD_ID_PREFIX=y
CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y

View file

@ -0,0 +1,83 @@
/* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/atomic_builtin.h>
#include "testlib/log_utils.h"
#include "babblekit/flags.h"
#include "babblekit/testcase.h"
LOG_MODULE_REGISTER(dut, LOG_LEVEL_DBG);
extern unsigned long runtime_log_level;
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
char addr_str[BT_ADDR_LE_STR_LEN];
k_msleep(500); /* simulate a slow memcpy (or user processing the scan data) */
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
LOG_DBG("Device found: %s (RSSI %d), type %u, AD data len %u", addr_str, rssi, type,
ad->len);
}
#define BT_LE_SCAN_ACTIVE_CONTINUOUS_WITH_DUPLICATES \
BT_LE_SCAN_PARAM(BT_LE_SCAN_TYPE_ACTIVE, 0, BT_GAP_SCAN_FAST_INTERVAL_MIN, \
BT_GAP_SCAN_FAST_WINDOW)
void entrypoint_dut(void)
{
/* Test purpose:
*
* Verifies that the host can handle running out of HCI RX buffers.
*
* To test this, we use a scanner with privacy enabled and sleep a bit
* when we get every advertising report. This sleep period simulates a
* slow "memcpy" operation on actual hardware. In effect, this uses up
* the event buffer pools.
*
* A short RPA timeout is used to prompt the host into periodically
* sending a bunch of commands to the controller. Those commands should
* not fail.
*
* Note: This test only fails by the stack crashing.
*
* It is a regression test for
* https://github.com/zephyrproject-rtos/zephyr/issues/78223
*
* Two devices:
* - `dut`: active-scans with privacy ON
* - `peer`: bog-standard advertiser
*
* [verdict]
* - dut is able to run for a long enough time without triggering asserts
*/
int err;
TEST_START("DUT");
/* Set the log level given by the `log_level` CLI argument */
bt_testlib_log_level_set("dut", runtime_log_level);
err = bt_enable(NULL);
TEST_ASSERT(!err, "Bluetooth init failed (err %d)\n", err);
LOG_DBG("Bluetooth initialised");
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE_CONTINUOUS_WITH_DUPLICATES, device_found);
TEST_ASSERT(!err, "Scanner setup failed (err %d)\n", err);
LOG_DBG("Explicit scanner started");
/* 40 seconds ought to be enough for anyone */
k_sleep(K_SECONDS(40));
TEST_PASS_AND_EXIT("DUT passed");
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "bs_tracing.h"
#include "bstests.h"
#include "babblekit/testcase.h"
#include "testlib/log_utils.h"
extern void entrypoint_dut(void);
extern void entrypoint_peer(void);
extern enum bst_result_t bst_result;
unsigned long runtime_log_level = LOG_LEVEL_INF;
static void test_args(int argc, char *argv[])
{
size_t argn = 0;
const char *arg = argv[argn];
if (strcmp(arg, "log_level") == 0) {
runtime_log_level = strtoul(argv[++argn], NULL, 10);
if (runtime_log_level >= LOG_LEVEL_NONE && runtime_log_level <= LOG_LEVEL_DBG) {
TEST_PRINT("Runtime log level configuration: %d", runtime_log_level);
} else {
TEST_FAIL("Invalid arguments to set log level: %d", runtime_log_level);
}
} else {
TEST_PRINT("Default runtime log level configuration: INFO");
}
}
static void test_end_cb(void)
{
if (bst_result != Passed) {
TEST_PRINT("Test failed.");
}
}
static const struct bst_test_instance entrypoints[] = {
{
.test_id = "dut",
.test_delete_f = test_end_cb,
.test_main_f = entrypoint_dut,
.test_args_f = test_args,
},
{
.test_id = "peer",
.test_delete_f = test_end_cb,
.test_main_f = entrypoint_peer,
.test_args_f = test_args,
},
BSTEST_END_MARKER,
};
static struct bst_test_list *install(struct bst_test_list *tests)
{
return bst_add_tests(tests, entrypoints);
};
bst_test_install_t test_installers[] = {install, NULL};
int main(void)
{
bst_main();
return 0;
}

View file

@ -0,0 +1,48 @@
/* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/atomic_builtin.h>
#include "testlib/log_utils.h"
#include "babblekit/flags.h"
#include "babblekit/testcase.h"
LOG_MODULE_REGISTER(peer, LOG_LEVEL_DBG);
extern unsigned long runtime_log_level;
void entrypoint_peer(void)
{
int err;
TEST_START("peer");
/* Set the log level given by the `log_level` CLI argument */
bt_testlib_log_level_set("peer", runtime_log_level);
err = bt_enable(NULL);
TEST_ASSERT(!err, "Bluetooth init failed (err %d)\n", err);
LOG_DBG("Bluetooth initialised");
struct bt_le_ext_adv *adv;
struct bt_le_adv_param adv_param = BT_LE_ADV_PARAM_INIT(
BT_LE_ADV_OPT_EXT_ADV, BT_GAP_ADV_FAST_INT_MIN_1, BT_GAP_ADV_FAST_INT_MAX_1, NULL);
err = bt_le_ext_adv_create(&adv_param, NULL, &adv);
TEST_ASSERT(!err, "Failed to create advertising set: %d", err);
LOG_DBG("Created extended advertising set.");
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
TEST_ASSERT(!err, "Failed to start extended advertising: %d", err);
LOG_DBG("Started extended advertising.");
TEST_PASS("Tester done");
}

View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
test_name="$(guess_test_long_name)"
test_exe="${BSIM_OUT_PATH}/bin/bs_${BOARD_TS}_${test_name}_prj_conf"
simulation_id=${test_name}
verbosity_level=2
zephyr_log_level=3
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s="${simulation_id}" \
-D=2 -sim_length=100e6
Execute "${test_exe}" \
-v=${verbosity_level} -s="${simulation_id}" -d=1 \
-testid=peer -argstest log_level ${zephyr_log_level}
Execute "${test_exe}" \
-v=${verbosity_level} -s="${simulation_id}" -d=0 \
-testid=dut -argstest log_level ${zephyr_log_level}
wait_for_background_jobs