第2章 抓取协议日志:hci log、controller trace、app callback 日志采集方法

在多BLE连接写入冲突场景中,协议日志是定位根本原因的必备数据。本章将系统介绍三类关键日志的采集方法:HCI Log(Host-Controller 接口)、Controller Trace(控制器内部状态)、App Callback(应用层回调)。按照问题排查型课程六步法组织:现象识别 → 数据采集 → 原因定位 → 修复动作 → 复测 → 预防。

1. 现象识别

当多连接写入冲突发生时,终端行为会呈现以下典型现象,每种现象对应必须采集的日志类型:

现象需采集日志冲突线索
写入操作返回错误码(如 0x12)HCI Log + App CallbackHCI_LE_Connection_Update 时序异常
对端设备无响应或断开连接Controller Trace + HCI LogController 内部调度冲突
应用层回调延迟或丢失App Callback + Controller Trace回调栈溢出或被冲刷
写入数据出现乱序或覆盖三者全量ATT_WRITE_REQ 乱序
提示: 若无法一次性采集全部日志,优先抓取 HCI Log + App Callback,因为两者覆盖 Host 与 Application 边界,多数冲突体现在该交界处。

2. 数据采集

正确、同步地采集三类日志是后续分析的基础。以下分别说明各日志的采集工具、命令和关键配置。

2.1 HCI Log 采集

HCI Log 记录 Host 与 Controller 之间所有 HCI 命令、事件和 ACL/ISO 数据包。推荐使用 btmon(BlueZ 内置)或 hcidump。

# btmon 实时抓取(默认输出到终端,可重定向到文件)
sudo btmon -w /tmp/hci_冲突场景.log

# 指定控制器(如 hci0)
sudo btmon -i hci0 -w /tmp/hci0_trace.log

# 过滤事件类型(写入冲突关注 ATT 和 L2CAP 事件)
sudo btmon -i hci0 -w /tmp/att_trace.log --filter "type=EVT_LE_META_EVENT"
关键参数: -w 指定输出文件;-i 选择控制器;Android 平台需使用 hci_snoop 或 bt_stack.conf 开启 BTSNOOP。

2.2 Controller Trace 采集

Controller Trace 直接读取 BLE 控制器内部寄存器、调度状态、连接间隔冲突信号。通常通过厂商专用工具获取(如 TI Packet Sniffer、Nordic nRF Sniffer、Dialog SmartBond Trace)。

// 示例:使用 TI CC2540 USB 嗅探器
// 1. 硬件连接:嗅探器天线靠近目标设备
// 2. 打开 TI SmartRF Studio,选择 BLE 模式
// 3. 设置捕获通道(如 37/38/39 + 数据通道)
// 4. 点击 Start Capture,保存为 .pcap 或 .csa 文件

// 或使用 nRF Sniffer for BLE
// 命令行模式(Python 脚本)
python sniffer.py -c 38 -f /tmp/ctrl_trace.pcap
厂商工具输出格式注意事项
NordicnRF Sniffer.pcap需要 nRF 52840 DK 或 USB 嗅探器
TIPacket Sniffer.psd / .csaCC2540USB 硬件
DialogSmartBond Toolbox.csv / .txt需 UART 接口

2.3 App Callback 日志采集

App Callback 日志是应用层收到 BLE 事件(如 onWrite、onConnectionUpdate、onError)时由开发者主动打印的日志。以下为典型代码段:

// Android (Java)
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
                                   BluetoothGattCharacteristic characteristic,
                                   int status) {
    Log.d("BLE_APP", String.format("onWrite connId=%d uuid=%s status=0x%02X",
            gatt.getConnectionId(),
            characteristic.getUuid(),
            status));
}

// iOS (Swift)
func peripheral(_ peripheral: CBPeripheral,
                didWriteValueFor characteristic: CBCharacteristic,
                error: Error?) {
    print("[BLE_APP] didWrite conn=\(peripheral.identifier.uuidString) " +
          "char=\(characteristic.uuid.uuidString) err=\(error?.localizedDescription ?? "nil")")
}

// Zephyr (C)
static void on_write(struct bt_conn *conn,
                     const struct bt_gatt_attr *attr,
                     const void *data, uint16_t len,
                     uint16_t offset, uint8_t flags) {
    printk("[APP_CALLBACK] on_write conn=%p attr=%p len=%d\n", conn, attr, len);
}
注意: App Callback 日志必须包含 连接标识符(connId / peripheral UUID / bt_conn 指针)和 返回状态码,否则无法与 HCI Log / Controller Trace 关联。
日志采集架构与时序同步 APP 层 Host 层 (GATT/GAP) Controller 层 (LL) App Callback 日志 HCI Log (btmon) Controller Trace 时间轴 (μs / ms) t1 t2 t3 需保证各日志时间戳对齐

3. 原因定位(基于日志的初步排查)

采集到日志后,按以下判断规则快速过滤写入冲突相关信号:

日志类型关键字段冲突指示
HCI LogHCI_LE_Connection_Update Complete 参数;ATT_Write_Req / Rsp 序列连接参数更新重叠、ATT 写入序号错乱
Controller TraceConnection Event 调度偏移、Sequence Number 冲突两个连接事件在时间上重叠
App Callbackstatus 值(0x00成功,否则异常);重入次数连续多个 callback 返回 0x12 (insufficient resources)
判断规则: 将三个日志按时间戳对齐,若同一时间窗口内 HCI 命令与 Controller Trace 的事件偏移量 > 0.5 ms,且 App Callback 出现错误码,则高度怀疑写入冲突。

4. 修复动作(日志采集异常时的处理)

  • 问题: HCI Log 文件为空或只有少量事件。
    → 检查 btmon 权限,使用 sudo;或确认蓝牙适配器是否为 HCI 模式。
  • 问题: Controller Trace 无法捕获连接事件。
    → 调整嗅探器天线位置,确保与目标设备在同一频道;或增大抓包缓冲区大小。
  • 问题: App Callback 日志缺少连接标识。
    → 在日志打印中加入 connId、timestamp、status;若使用 NRF Connect,开启详细日志。
  • 问题: 三个日志时间戳无法对齐。
    → 使用 NTP 同步设备时钟;或在每个日志打印时写入同一系统 tick 值。

5. 复测

执行以下验证步骤,确认日志采集正确可用:

  1. 复现一次简单的单连接写入操作,检查 HCI Log 中是否有对应的 ATT_Write_Req 和 Write_Rsp 事件。
  2. 检查 Controller Trace 中是否存在该连接的 Connection Event 记录。
  3. 验证 App Callback 打印的 onWrite 日志与 HCI Log 中该写入的 ACK 时间差在 10 ms 以内。
  4. 进行两次连续写入(间隔 < 7.5ms),观察三个日志中是否出现并行队列而非串行执行。
验证指标: 若上述四步均能正确捕获且时钟偏差 < 1ms,则可进入原因定位阶段(第3章)。否则返回数据采集步骤调整参数。

6. 预防

为避免在问题排查时反复补采日志,建议在项目开发期建立下列规范:

  • 默认开启 btmon 日志轮转(btmon -w /data/ble_trace_%Y%m%d_%H%M%S.log),覆盖写入冲突场景复现。
  • 在固件中增加 写入冲突检测计数器,当检测到重入或超时自动触发 Controller Trace 快照。
  • 制定“日志三件套”同步采集检查单:将 HCI Log、Controller Trace、App Callback 日志文件名、时间戳范围记录在排查报告中。
  • 对 App Callback 日志采用结构化格式(如 JSON 行),便于后续自动化对齐分析。