在蓝牙底层开发中,我们经常需要追踪 bluetoothd 的启动流程------它什么时候初始化了 D-Bus 连接?什么时候创建了 MGMT 套接字?什么时候加载了插件?原生日志机制虽然提供了 info()、DBG() 等函数,但默认情况下 DBG() 被关闭,info() 输出稀少,难以追踪完整启动链路。本文手把手教你修改 BlueZ 5.x 源码,在 bluetoothd 启动的关键节点添加自定义日志,完成从源码修改→编译→部署→验证的全流程实战,为后续深度二次开发打下基础。
一、BlueZ 启动流程 源码 全景
1.1 启动入口定位
bluetoothd 的入口函数位于 src/main.c第 1154 行的 main() 函数。完整启动流程如下:
cpp
int main(int argc, char *argv[])
{
/* ① 初始化默认配置 */
init_defaults(); // 第 1162 行
/* ② 解析命令行参数 (-d/-p/-f/-E/-n 等) */
context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, options, NULL);
g_option_context_parse(context, &argc, &argv, &err); // 第 1167 行
/* ③ 初始化 backtrace 崩溃追踪 */
btd_backtrace_init(); // 第 1185 行
/* ④ 初始化主循环 (GLib mainloop) */
mainloop_init(); // 第 1187 行
/* ⑤ ★ 初始化日志系统 */
__btd_log_init(option_debug, option_detach); // 第 1189 行
/* ⑥ 设置 GLib 日志重定向 */
g_log_set_handler("GLib", ..., log_handler, NULL); // 第 1191 行
/* ⑦ systemd 状态通知 */
mainloop_sd_notify("STATUS=Starting up"); // 第 1195 行
/* ⑧ 加载配置文件 (/etc/bluetooth/main.conf) */
main_conf = load_config(main_conf_file_path); // 第 1202 行
parse_config(main_conf); // 第 1204 行
/* ⑨ 连接 D-Bus 系统总线 */
if (connect_dbus() < 0) { // 第 1206 行
error("Unable to get on D-Bus");
exit(1);
}
/* ⑩ 初始化适配器管理 (创建 MGMT 套接字) */
if (adapter_init() < 0) { // 第 1216 行
error("Adapter handling initialization failed");
exit(1);
}
/* ⑪ 初始化设备/Agent/Profile 子系统 */
btd_device_init(); // 第 1221 行
btd_agent_init(); // 第 1222 行
btd_profile_init(); // 第 1223 行
/* ⑫ 启动 SDP 服务器 (BR/EDR 模式) */
start_sdp_server(sdp_mtu, sdp_flags); // 第 1229 行
/* ⑬ 加载插件 */
plugin_init(option_plugin, option_noplugin); // 第 1245 行
/* ⑭ 初始化 rfkill */
rfkill_init(); // 第 1250 行
/* ⑮ 进入主循环 */
DBG("Entering main loop"); // 第 1252 行
mainloop_sd_notify("STATUS=Running"); // 第 1254 行
mainloop_sd_notify("READY=1"); // 第 1255 行
mainloop_run_with_signal(signal_callback, NULL); // 第 1257 行
/* ---- 以下为关闭流程 ---- */
mainloop_sd_notify("STATUS=Quitting");
plugin_cleanup();
btd_profile_cleanup();
btd_agent_cleanup();
btd_device_cleanup();
adapter_cleanup();
rfkill_exit();
stop_sdp_server();
disconnect_dbus();
info("Exit"); // 第 1282 行
__btd_log_cleanup();
return 0;
}
1.2 启动流程可视化
cpp
┌─────────────────────────────────────────────────────────────────┐
│ bluetoothd 启动流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ main() │
│ │ │
│ ├─① init_defaults() 设置默认配置 │
│ │ │
│ ├─② g_option_context_parse() 解析命令行参数 │
│ │ │
│ ├─③ btd_backtrace_init() 初始化崩溃追踪 │
│ │ │
│ ├─④ mainloop_init() 初始化 GLib 主循环 │
│ │ │
│ ├─⑤ __btd_log_init() ★ 初始化日志系统 │
│ │ └─ info("Bluetooth daemon %s", VERSION) │
│ │ │
│ ├─⑥ g_log_set_handler() GLib 日志重定向 │
│ │ │
│ ├─⑦ mainloop_sd_notify() systemd STATUS=Starting up │
│ │ │
│ ├─⑧ load_config() + parse_config() 加载 /etc/bluetooth/main.conf │
│ │ │
│ ├─⑨ connect_dbus() 连接 D-Bus 系统总线 │
│ │ │
│ ├─⑩ adapter_init() ★ 创建 MGMT 套接字 │
│ │ └─ mgmt_new_default() → MGMT Socket │
│ │ └─ mgmt_send(READ_VERSION) │
│ │ │
│ ├─⑪ btd_device_init() 设备管理初始化 │
│ ├─⑪ btd_agent_init() Agent 管理初始化 │
│ ├─⑪ btd_profile_init() Profile 管理初始化 │
│ │ │
│ ├─⑫ start_sdp_server() SDP 服务器启动 (BR/EDR) │
│ │ │
│ ├─⑬ plugin_init() 加载内置/外部插件 │
│ │ │
│ ├─⑭ rfkill_init() rfkill 初始化 │
│ │ │
│ ├─⑮ mainloop_sd_notify() STATUS=Running, READY=1 │
│ │ │
│ └─ mainloop_run_with_signal() ★ 进入主循环 (阻塞) │
│ │
└─────────────────────────────────────────────────────────────────┘
二、BlueZ 原生日志机制深度解析
2.1 日志系统架构
BlueZ 的日志系统定义在 src/log.c和 src/log.h中,由三部分组成:

2.2 日志函数源码
cpp
/* src/log.h --- 宏定义 */
/* info: 始终输出,无文件名/函数名前缀 */
void info(const char *format, ...) __attribute__((format(printf, 1, 2)));
/* error/warn: 始终输出,自动添加文件名和函数名前缀 */
#define error(fmt, arg...) \
btd_error(0xffff, "%s:%s() " fmt, __FILE__, __func__, ## arg)
#define warn(fmt, arg...) \
btd_warn(0xffff, "%s:%s() " fmt, __FILE__, __func__, ## arg)
/* DBG: 默认关闭,需 -d 参数开启,自动添加文件名和函数名前缀 */
#define DBG(fmt, arg...) DBG_IDX(0xffff, fmt, ## arg)
#define DBG_IDX(idx, fmt, arg...) do { \
static struct btd_debug_desc __btd_debug_desc \
__attribute__((used, section("__debug"), aligned(8))) = { \
.file = __FILE__, .flags = BTD_DEBUG_FLAG_DEFAULT, \
}; \
if (__btd_debug_desc.flags & BTD_DEBUG_FLAG_PRINT) \
btd_debug(idx, "%s:%s() " fmt, __FILE__, __func__, ## arg); \
} while (0)
cpp
/* src/log.c --- 函数实现 */
void info(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vsyslog(LOG_INFO, format, ap); /* 输出到 syslog */
va_end(ap);
va_start(ap, format);
monitor_log(HCI_DEV_NONE, LOG_INFO, format, ap); /* 输出到 btmon */
va_end(ap);
}
void btd_debug(uint16_t index, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vsyslog(LOG_DEBUG, format, ap);
va_end(ap);
va_start(ap, format);
monitor_log(index, LOG_DEBUG, format, ap);
va_end(ap);
}
2.3 日志初始化
cpp
/* src/log.c --- __btd_log_init() */
void __btd_log_init(const char *debug, int detach)
{
int option = LOG_NDELAY | LOG_PID;
/* 如果 -d 参数指定了调试过滤,按 ":" / "," / " " 分割 */
if (debug != NULL)
enabled = g_strsplit_set(debug, ":, ", 0);
/* 启用匹配的 DBG 描述符 */
__btd_enable_debug(__start___debug, __stop___debug);
/* 打开 btmon 监控通道 */
bt_log_open();
/* -n (不 detach) 时同时输出到 stderr */
if (!detach)
option |= LOG_PERROR;
openlog(LOG_IDENT, option, LOG_DAEMON); /* LOG_IDENT = "bluetoothd" */
info("Bluetooth daemon %s", VERSION); /* ★ 第一条日志 */
}
2.4 原生日志的缺陷
|-----------------|-----------------------------------------------------------------|
| 缺陷 | 说明 |
| info() 输出稀少 | 整个启动过程只有 2 条 info()(开头 "Bluetooth daemon" + 结尾 "Exit"),无法追踪中间步骤 |
| DBG() 默认关闭 | 需要手动添加 -d 参数才输出,且输出到 syslog 的 DEBUG 级别可能被过滤 |
| 无启动耗时统计 | 没有时间打点,无法知道各阶段耗时 |
| 无环境信息 | 不打印 PID、工作目录、配置文件路径等运行环境信息 |
| 无阶段分隔 | 各子系统初始化日志混在一起,难以区分阶段 |
三、实战目标:添加三类自定义日志
我们将在 bluetoothd 启动流程中添加三类自定义日志:
|-------------|---------------------|-------------------------------------------------------|
| 类型 | 内容 | 插入位置 |
| 开机启动横幅 | 版本号、PID、编译时间、配置文件路径 | __btd_log_init()之后 |
| 设备初始化日志 | 各子系统初始化进度 | adapter_init() / btd_device_init() / plugin_init() 前后 |
| 启动状态打点 | 各阶段时间戳、总耗时 | 关键节点前后 |
四、手把手源码修改
4.1 修改 src/main.c --- 添加启动横幅与阶段日志
打开 src/main.c,在 main() 函数中添加自定义日志。
修改点 1:添加头文件
在 #include <stdio.h> 之后添加:
cpp
#include <time.h>
修改点 2:添加启动横幅
在 __btd_log_init(option_debug, option_detach); 之后添加:
cpp
__btd_log_init(option_debug, option_detach);
/* ===== 自定义开机日志:启动横幅 ===== */
info("========================================");
info("BlueZ Custom Build Starting...");
info(" Version: %s", VERSION);
info(" PID: %d", getpid());
info(" Config file: %s", main_conf_file_path ? main_conf_file_path : "(default)");
info(" Compiled: %s %s", __DATE__, __TIME__);
info("========================================");
修改点 3:添加 D-Bus 连接日志
在 connect_dbus() 调用前后添加:
cpp
/* ===== 自定义启动日志:D-Bus 连接 ===== */
info("[BOOT] Step 1: Connecting to D-Bus system bus...");
if (connect_dbus() < 0) {
error("Unable to get on D-Bus");
exit(1);
}
info("[BOOT] Step 1: D-Bus connected OK");
修改点 4:添加适配器初始化日志
在 adapter_init() 调用前后添加:
cpp
/* ===== 自定义启动日志:适配器初始化 ===== */
info("[BOOT] Step 2: Initializing adapter (MGMT socket)...");
if (adapter_init() < 0) {
error("Adapter handling initialization failed");
exit(1);
}
info("[BOOT] Step 2: Adapter initialized OK");
修改点 5:添加子系统初始化日志
cpp
/* ===== 自定义启动日志:子系统初始化 ===== */
info("[BOOT] Step 3: Initializing device manager...");
btd_device_init();
info("[BOOT] Step 3: Device manager OK");
info("[BOOT] Step 4: Initializing agent manager...");
btd_agent_init();
info("[BOOT] Step 4: Agent manager OK");
info("[BOOT] Step 5: Initializing profile manager...");
btd_profile_init();
info("[BOOT] Step 5: Profile manager OK");
修改点 6:添加插件加载日志(第 1245 行附近)
cpp
/* ===== 自定义启动日志:插件加载 ===== */
info("[BOOT] Step 6: Loading plugins...");
plugin_init(option_plugin, option_noplugin);
info("[BOOT] Step 6: Plugins loaded OK");
修改点 7:添加主循环进入日志
cpp
/* ===== 自定义启动日志:进入主循环 ===== */
info("[BOOT] Step 7: Entering main loop");
info("[BOOT] BlueZ startup complete! All systems go.");
DBG("Entering main loop");
mainloop_sd_notify("STATUS=Running");
mainloop_sd_notify("READY=1");
mainloop_run_with_signal(signal_callback, NULL);
4.2 修改 src/adapter.c --- 添加设备初始化日志
打开 src/adapter.c,在 adapter_init() 函数中添加日志。
修改点:第 10335 行 adapter_init() 函数
cpp
int adapter_init(void)
{
dbus_conn = btd_get_dbus_connection();
/* ===== 自定义日志:MGMT 套接字创建 ===== */
info("[ADAPTER] Creating MGMT management socket...");
mgmt_primary = mgmt_new_default();
if (!mgmt_primary) {
error("Failed to access management interface");
return -EIO;
}
info("[ADAPTER] MGMT socket created successfully");
if (getenv("MGMT_DEBUG"))
mgmt_set_debug(mgmt_primary, mgmt_debug, "mgmt: ", NULL);
DBG("sending read version command");
info("[ADAPTER] Sending MGMT_OP_READ_VERSION...");
if (mgmt_send(mgmt_primary, MGMT_OP_READ_VERSION,
MGMT_INDEX_NONE, 0, NULL,
read_version_complete, NULL, NULL) > 0) {
info("[ADAPTER] MGMT version request sent, waiting for response...");
return 0;
}
error("Failed to read management version information");
return -EIO;
}
4.3 修改 src/plugin.c --- 添加插件加载详细日志
打开 src/plugin.c,在 plugin_init() 函数中添加日志。
修改点:第 102 行 plugin_init() 函数
在 DBG("Loading builtin plugins"); 之后添加:
cpp
DBG("Loading builtin plugins");
/* ===== 自定义日志:插件加载详情 ===== */
info("[PLUGIN] Starting plugin initialization");
info("[PLUGIN] Enabled filter: %s", enable ? enable : "(all)");
info("[PLUGIN] Disabled filter: %s", disable ? disable : "(none)");
for (i = 0; __bluetooth_builtin[i]; i++) {
if (!enable_plugin(__bluetooth_builtin[i]->name, cli_enabled,
cli_disabled))
continue;
info("[PLUGIN] Loading builtin plugin: %s", __bluetooth_builtin[i]->name);
add_plugin(NULL, __bluetooth_builtin[i]);
}
4.4 修改 src/log.c --- 添加启动耗时打点
打开 src/log.c,在 __btd_log_init() 中添加启动时间记录。
修改点:在文件头部添加全局变量
在 #define LOG_IDENT "bluetoothd" 之后添加:
cpp
/* ===== 自定义:启动时间打点 ===== */
static struct timespec boot_start_time;
修改点:在 __btd_log_init() 中记录启动开始时间
cpp
void __btd_log_init(const char *debug, int detach)
{
int option = LOG_NDELAY | LOG_PID;
/* ===== 自定义:记录启动开始时间 ===== */
clock_gettime(CLOCK_MONOTONIC, &boot_start_time);
if (debug != NULL)
enabled = g_strsplit_set(debug, ":, ", 0);
__btd_enable_debug(__start___debug, __stop___debug);
bt_log_open();
if (!detach)
option |= LOG_PERROR;
openlog(LOG_IDENT, option, LOG_DAEMON);
info("Bluetooth daemon %s", VERSION);
info("[BOOT] Log system initialized at monotonic %ld.%09ld",
boot_start_time.tv_sec, boot_start_time.tv_nsec);
}
4.5 修改汇总
|-------------------|----------|----------------|
| 文件 | 修改行数 | 修改内容 |
| src/main.c | +25 行 | 启动横幅 + 7 步阶段日志 |
| src/adapter.c | +6 行 | MGMT 套接字创建日志 |
| src/plugin.c | +4 行 | 插件加载详情日志 |
| src/log.c | +6 行 | 启动时间打点 |
五、编译配置与构建
5.1 编译环境准备
cpp
# Ubuntu/Debian 安装编译依赖
sudo apt-get update
sudo apt-get install -y \
build-essential \
libglib2.0-dev \
libdbus-1-dev \
libudev-dev \
libical-dev \
readline-common \
autoconf \
automake \
libtool \
pkg-config \
bison \
flex \
git
5.2 配置编译选项
cpp
cd bluez_source
# 方式一:最小化配置(只编译 bluetoothd,不含插件)
./configure --prefix=/usr \
--mandir=/usr/share/man \
--sysconfdir=/etc \
--localstatedir=/var \
--enable-library \
--disable-systemd \
--disable-manpages \
--disable-cups \
--disable-mesh \
--disable-btpclient \
--disable-hid2hci \
--disable-a2dp \
--disable-avrcp
# 方式二:完整配置(推荐开发使用)
./configure --prefix=/usr \
--mandir=/usr/share/man \
--sysconfdir=/etc \
--localstatedir=/var \
--enable-library \
--enable-debug \
--enable-testing \
--enable-experimental
5.3 编译
cpp
# 编译(-j 加速)
make -j$(nproc)
# 仅编译 bluetoothd(快速验证修改)
make -j$(nproc) src/bluetoothd
5.4 编译后验证
cpp
# 验证编译产物
ls -la src/bluetoothd
file src/bluetoothd
# 输出: ELF 64-bit LSB shared object, x86-64, dynamically linked
# 验证版本号
./src/bluetoothd --version
# 输出: 5.64
# 验证自定义日志(前台模式运行)
sudo ./src/bluetoothd -n -d
预期输出:
cpp
bluetoothd[12345]: Bluetooth daemon 5.64
bluetoothd[12345]: [BOOT] Log system initialized at monotonic 12345.678901234
bluetoothd[12345]: ========================
bluetoothd[12345]: BlueZ Custom Build Starting...
bluetoothd[12345]: Version: 5.64
bluetoothd[12345]: PID: 12345
bluetoothd[12345]: Config file: /etc/bluetooth/main.conf
bluetoothd[12345]: Compiled: Aug 2 2026 14:30:00
bluetoothd[12345]: ========================
bluetoothd[12345]: [BOOT] Step 1: Connecting to D-Bus system bus...
bluetoothd[12345]: [BOOT] Step 1: D-Bus connected OK
bluetoothd[12345]: [BOOT] Step 2: Initializing adapter (MGMT socket)...
bluetoothd[12345]: [ADAPTER] Creating MGMT management socket...
bluetoothd[12345]: [ADAPTER] MGMT socket created successfully
bluetoothd[12345]: [ADAPTER] Sending MGMT_OP_READ_VERSION...
bluetoothd[12345]: [ADAPTER] MGMT version request sent, waiting for response...
bluetoothd[12345]: [BOOT] Step 2: Adapter initialized OK
bluetoothd[12345]: [BOOT] Step 3: Initializing device manager...
bluetoothd[12345]: [BOOT] Step 3: Device manager OK
bluetoothd[12345]: [BOOT] Step 4: Initializing agent manager...
bluetoothd[12345]: [BOOT] Step 4: Agent manager OK
bluetoothd[12345]: [BOOT] Step 5: Initializing profile manager...
bluetoothd[12345]: [BOOT] Step 5: Profile manager OK
bluetoothd[12345]: [BOOT] Step 6: Loading plugins...
bluetoothd[12345]: [PLUGIN] Starting plugin initialization
bluetoothd[12345]: [PLUGIN] Enabled filter: (all)
bluetoothd[12345]: [PLUGIN] Disabled filter: (none)
bluetoothd[12345]: [PLUGIN] Loading builtin plugin: hostname
bluetoothd[12345]: [PLUGIN] Loading builtin plugin: autopair
bluetoothd[12345]: [PLUGIN] Loading builtin plugin: policy
bluetoothd[12345]: [BOOT] Step 6: Plugins loaded OK
bluetoothd[12345]: [BOOT] Step 7: Entering main loop
bluetoothd[12345]: [BOOT] BlueZ startup complete! All systems go.
六、环境部署与替换
6.1 停止系统 bluetoothd
cpp
# 停止系统服务
sudo systemctl stop bluetooth
# 确认已停止
sudo systemctl status bluetooth
# Active: inactive (dead)
6.2 备份原始 bluetoothd
cpp
# 查找系统 bluetoothd 路径
which bluetoothd
# 通常在: /usr/libexec/bluetooth/bluetoothd
# 备份
sudo cp /usr/libexec/bluetooth/bluetoothd \
/usr/libexec/bluetooth/bluetoothd.orig
6.3 替换 bluetoothd
cpp
# 复制编译产物
sudo cp src/bluetoothd /usr/libexec/bluetooth/bluetoothd
# 确保权限正确
sudo chmod 755 /usr/libexec/bluetooth/bluetoothd
sudo chown root:root /usr/libexec/bluetooth/bluetoothd
6.4 安装共享库(如需要)
如果 bluetoothd 依赖的库也有修改,需同步安装:
cpp
# 安装到系统路径
sudo make install
# 或仅安装库文件
sudo cp lib/.libs/libbluetooth.so* /usr/lib/
sudo ldconfig
6.5 恢复系统服务
cpp
# 启动服务
sudo systemctl start bluetooth
# 检查状态
sudo systemctl status bluetooth
6.6 验证日志输出
cpp
# 方法 1:直接查看 syslog
sudo journalctl -u bluetooth -f --no-pager
# 方法 2:查看 syslog 文件
tail -f /var/log/syslog | grep bluetoothd
# 方法 3:前台调试模式
sudo /usr/libexec/bluetooth/bluetoothd -n -d
七、常见问题排查
7.1 编译报错
问题 1:config.h 找不到
cpp
configure: error: cannot find config.h
原因:未运行 ./configure 或配置失败。
解决:
cpp
# 清理后重新配置
make distclean
./configure --prefix=/usr --enable-library
make -j$(nproc)
问题 2:缺少 glib.h
cpp
fatal error: glib.h: No such file or directory
解决:
cpp
sudo apt-get install libglib2.0-dev
问题 3:缺少 dbus/dbus.h
cpp
fatal error: dbus/dbus.h: No such file or directory
解决:
cpp
sudo apt-get install libdbus-1-dev
问题 4:DATE 和 TIME 警告
cpp
warning: macro "__DATE__" might prevent reproducible builds
说明:这是警告而非错误,可忽略。如需消除,在 Makefile.am 中添加 `-Wno-date-time`。
7.2 日志不输出
问题:运行 bluetoothd 看不到自定义日志
排查步骤:
cpp
# 1. 确认运行的是修改后的 bluetoothd
md5sum /usr/libexec/bluetooth/bluetoothd
md5sum src/bluetoothd
# 两个 MD5 应一致
# 2. 确认 bluetoothd 正在运行
ps aux | grep bluetoothd
# 3. 检查 syslog 配置是否过滤了 INFO 级别
grep -i bluetooth /etc/rsyslog.conf
grep -i bluetooth /etc/rsyslog.d/*.conf
# 4. 使用前台模式直接观察输出
sudo /usr/libexec/bluetooth/bluetoothd -n -d
# -n: 不 detach (前台运行)
# -d: 开启 DBG 调试日志
问题:syslog 中没有 bluetoothd 日志
原因:syslog 可能未配置捕获 LOG_DAEMON facility。
解决:
cpp
# 检查 syslog 配置
cat /etc/rsyslog.conf | grep -i daemon
# 或直接使用 journalctl(systemd 系统)
sudo journalctl -t bluetoothd -f
7.3 权限问题
问题:bluetoothd 启动失败,提示权限不足
cpp
Failed to access management interface
原因:MGMT Socket 需要 root 权限或 CAP_NET_ADMIN 能力。
解决:
cpp
# 方法 1:以 root 运行
sudo ./src/bluetoothd -n -d
# 方法 2:设置 capabilities
sudo setcap cap_net_admin,cap_net_raw+eip ./src/bluetoothd
./src/bluetoothd -n -d
# 方法 3:通过 systemd 运行(自动处理权限)
sudo systemctl start bluetooth
7.4 运行时冲突
问题:bluetoothd 启动后立即退出
cpp
bluetoothd[12345]: D-Bus setup failed: Name already in use
原因:另一个 bluetoothd 实例已在运行。
解决:
cpp
# 查找并杀掉旧进程
sudo pkill bluetoothd
# 或
sudo systemctl stop bluetooth
# 确认无残留进程
ps aux | grep bluetoothd
# 重新启动
sudo ./src/bluetoothd -n -d
7.5 配置文件问题
问题:配置文件路径不对
cpp
bluetoothd[12345]: Parsing /etc/bluetooth/main.conf failed: No such file
解决:
cpp
# 方式 1:使用 -f 指定配置文件
sudo ./src/bluetoothd -n -d -f /path/to/main.conf
# 方式 2:创建默认配置文件
sudo mkdir -p /etc/bluetooth
sudo cp src/main.conf /etc/bluetooth/main.conf
# 方式 3:不使用配置文件(使用默认值)
# 不指定 -f,bluetoothd 会尝试 /etc/bluetooth/main.conf,找不到就用默认值
八、恢复原始 bluetoothd
如需恢复到原始版本:
cpp
# 停止服务
sudo systemctl stop bluetooth
# 恢复备份
sudo cp /usr/libexec/bluetooth/bluetoothd.orig \
/usr/libexec/bluetooth/bluetoothd
# 重启服务
sudo systemctl start bluetooth
# 验证
/usr/libexec/bluetooth/bluetoothd --version
九、自定义日志扩展方法
9.1 添加带时间戳的耗时打点
在 src/log.h中添加辅助宏:
cpp
/* 在文件末尾添加 */
#include <time.h>
/* 耗时打点宏 */
#define BOOT_TIMESTAMP(label) do { \
struct timespec _ts; \
clock_gettime(CLOCK_MONOTONIC, &_ts); \
info("[TIMESTAMP] %s: %ld.%09ld", label, _ts.tv_sec, _ts.tv_nsec); \
} while (0)
/* 耗时统计宏(自动计算与开始时间的差值) */
#define BOOT_ELAPSED(label, start) do { \
struct timespec _ts; \
clock_gettime(CLOCK_MONOTONIC, &_ts); \
double _elapsed = (_ts.tv_sec - start.tv_sec) + \
(_ts.tv_nsec - start.tv_nsec) / 1e9; \
info("[ELAPSED] %s: %.6f sec", label, _elapsed); \
} while (0)
在 main() 中使用:
cpp
struct timespec boot_start;
__btd_log_init(option_debug, option_detach);
clock_gettime(CLOCK_MONOTONIC, &boot_start);
BOOT_TIMESTAMP("log_init_done");
/* ... */
BOOT_TIMESTAMP("before_dbus");
connect_dbus();
BOOT_ELAPSED("dbus_connect", boot_start);
/* ... */
BOOT_TIMESTAMP("before_adapter");
adapter_init();
BOOT_ELAPSED("adapter_init", boot_start);
/* ... */
BOOT_TIMESTAMP("before_mainloop");
BOOT_ELAPSED("total_startup", boot_start);
9.2 添加条件日志开关
在 src/main.c中添加环境变量控制:
cpp
/* 在 main() 开头添加 */
static gboolean boot_log_enabled = FALSE;
/* 检查环境变量 */
if (getenv("BLUEZ_BOOT_LOG"))
boot_log_enabled = TRUE;
/* 使用条件日志 */
if (boot_log_enabled)
info("[BOOT] Connecting to D-Bus...");
connect_dbus();
if (boot_log_enabled)
info("[BOOT] D-Bus connected OK");
运行时通过环境变量控制:
cpp
# 开启启动日志
sudo BLUEZ_BOOT_LOG=1 ./src/bluetoothd -n
# 不开启(正常模式)
sudo ./src/bluetoothd -n
9.3 添加日志输出到文件
如果需要将自定义日志同时输出到独立文件:
cpp
/* 在 __btd_log_init() 中添加 */
static FILE *boot_log_fp = NULL;
void __btd_log_init(const char *debug, int detach)
{
/* ... 原有代码 ... */
/* 自定义:打开启动日志文件 */
boot_log_fp = fopen("/var/log/bluetoothd-boot.log", "a");
if (boot_log_fp) {
fprintf(boot_log_fp, "\n=== bluetoothd boot %s ===\n", VERSION);
fflush(boot_log_fp);
}
}
/* 自定义:带文件输出的 info */
#define boot_info(fmt, arg...) do { \
info(fmt, ## arg); \
if (boot_log_fp) { \
fprintf(boot_log_fp, fmt "\n", ## arg); \
fflush(boot_log_fp); \
} \
} while (0)
9.4 在其他源码文件中添加日志
BlueZ 的日志宏在所有源码文件中都可用(通过 #include "log.h")。例如在设备连接流程中添加日志:
cpp
/* src/device.c --- 在设备连接函数中添加 */
/* 找到 device_connect_le() 或类似的连接函数 */
static int device_connect_le(struct btd_device *device)
{
/* 原有代码... */
info("[DEVICE] LE connect request: %s", addr);
/* 原有代码... */
info("[DEVICE] LE connect sent, waiting for response");
/* 原有代码... */
}
十、源码二次开发通用方法总结
10.1 定位入口的通用方法
|------------|-----------------------------------------------------|
| | |
| 守护进程入口 | grep -n "^int main" src/main.c |
| 子系统初始化 | grep -rn "_init\b" src/*.c |
| D-Bus 方法实现 | grep -rn "method_call|method_func" src/*.c |
| MGMT 命令处理 | grep -rn "mgmt_send|mgmt_register" src/*.c |
| 事件回调 | grep -rn "_cb\b|_callback\b|_complete\b" src/*.c |
| 配置解析 | grep -rn "g_key_file_get|parse_config" src/main.c |
10.2 修改源码的通用步骤
cpp
1. 阅读源码 → 理解函数调用链
2. 定位插入点 → 找到关键函数的入口/出口
3. 添加日志 → 使用 info()/DBG()/error() 宏
4. 编译验证 → make -j$(nproc) src/bluetoothd
5. 前台测试 → sudo ./src/bluetoothd -n -d
6. 部署替换 → cp + systemctl restart
7. 日志验证 → journalctl -u bluetooth -f
10.3 日志宏使用指南
|----------------------|----------|----------|-----------------------------------|
| 宏 | 使用场景 | 输出条件 | 输出格式 |
| info(fmt, ...) | 关键状态信息 | 始终输出 | bluetoothd[PID]: 消息 |
| warn(fmt, ...) | 警告信息 | 始终输出 | bluetoothd[PID]: file:func() 消息 |
| error(fmt, ...) | 错误信息 | 始终输出 | bluetoothd[PID]: file:func() 消息 |
| DBG(fmt, ...) | 调试信息 | -d 开启 | bluetoothd[PID]: file:func() 消息 |
| btd_info(idx, ...) | 带设备索引 | 始终输出 | bluetoothd[PID]: 消息 |
10.4 调试运行参数速查
cpp
# 前台运行 + 全部调试日志
sudo ./src/bluetoothd -n -d
# 前台运行 + 指定模块调试
sudo ./src/bluetoothd -n -d src/adapter.c:src/device.c
# 前台运行 + 指定配置文件
sudo ./src/bluetoothd -n -d -f /etc/bluetooth/main.conf
# 前台运行 + 实验性特性
sudo ./src/bluetoothd -n -d -E
# 前台运行 + 兼容模式
sudo ./src/bluetoothd -n -d -C
# 查看 MGMT 协议交互
sudo MGMT_DEBUG=1 ./src/bluetoothd -n -d
十一、总结
11.1 本文完成的工作
-
定位了 bluetoothd 启动入口:src/main.c 的 main() 函数,15 步启动流程
-
解析了 BlueZ 日志机制:info()/DBG()/error()/warn()宏 → vsyslog() + monitor_log() 双通道输出
-
添加了三类自定义日志:
-
启动横幅(版本/PID/配置文件/编译时间)
-
设备初始化日志(D-Bus/适配器/设备/Agent/Profile/插件 7 步)
-
启动状态打点(时间戳记录)
-
完成了全流程实战:源码修改 → ./configure → make → 部署替换 → 日志验证
-
解决了 5 类常见问题:编译报错、日志不输出、权限不足、运行时冲突、配置文件
11.2 关键经验
-
info() 始终输出,适合用于自定义启动日志;DBG() 需要 -d 参数,适合调试细节
-
-n -d 是开发调试的标配参数:`-n` 前台运行 + LOG_PERROR(输出到 stderr),`-d` 开启所有 DBG()日志
-
journalctl -u bluetooth -f 是查看日志的标准方式(systemd 系统)
-
修改后必须 `make` 重新编译,然后替换 `/usr/libexec/bluetooth/bluetoothd`
-
始终备份原始 `bluetoothd`:`cp bluetoothd bluetoothd.orig`
11.3 扩展方向
掌握了本文的源码修改方法后,可以进一步:
-
在设备配对流程中添加日志(`src/adapter.c` 的 `pair_device` 回调链)
-
在GATT 读写流程中添加日志(`src/gatt-database.c` / `src/gatt-client.c`)
-
在连接管理流程中添加日志(`src/device.c` 的 `device_connect_le` / `device_connect_bredr`)
-
添加性能统计(各阶段耗时、消息计数)
-
添加自定义 MGMT 命令(扩展 MGMT 协议用于私有功能调试)
附录:核心源码文件索引
|-------------------|----------------------------------------------------------|
| 文件 | 关键内容 |
| src/main.c | main() 入口、启动流程、配置解析、命令行参数 |
| src/log.c | info()/btd_log()/btd_error()/__btd_log_init() 实现 |
| src/log.h | info/DBG/error/warn 宏定义、btd_debug_desc 结构 |
| src/adapter.c | adapter_init() MGMT 套接字创建、适配器管理 |
| src/plugin.c | plugin_init() 内置/外部插件加载 |
| src/device.c | btd_device_init() 设备管理初始化 |
| Makefile.am | 编译规则:src_bluetoothd_SOURCES / src_bluetoothd_LDADD |
| configure.ac | 版本号 AC_INIT(bluez, 5.64)、编译选项配置 |
参考文献
-
BlueZ Source Code:
src/main.c,src/log.c,src/log.h -
Linux syslog:
man 3 syslog,man 3 vsyslog -
GLib Main Loop:
man g_main_loop_run -
systemd Journal:
man journalctl