adapter-poller 线程段错误问题定位与分析报告
adapter-poller 线程段错误问题定位与分析报告 #
执行摘要 #
问题:adapter-poller 线程在 panda-strategy 进程中多次发生段错误(SIGSEGV)。
崩溃位置:std::_Hashtable<...pandas::Adapter::ContractSpec...>::find (hashtable.h:1665)
根本原因:访问 pandas::Adapter::ContractSpec 哈希表时,this 指针无效,对象可能已被销毁。
关键发现:
- 崩溃发生在
adapter-poller线程处理订单更新时 - 调用链:
Adapter.cpp:307→Adapter::pollCommon→std::_Hashtable::find - Binance TD 代码存在类型不匹配和空指针检查缺失问题
修复建议:
- 修复 Binance TD 代码中的类型不匹配(
std::make_unique→std::make_shared) - 添加空指针检查(5 处
m_order_ws->send()调用) - 检查 Adapter 对象的生命周期管理
1. 问题现象 #
1.1 系统日志显示的问题 #
从系统日志 /var/log/messages 中观察到以下问题:
问题一:OOM (Out of Memory) #
Jan 22 20:29:38 ... kernel: Out of memory: Killed process 2150786 (node)
total-vm:45193936kB, anon-rss:28584348kB, file-rss:0kB, shmem-rss:0kB
- 被杀死进程:
node进程(PID: 2150786) - 内存占用:约 28GB (anon-rss: 28584348kB)
- 结论:这是独立问题,与 adapter-poller 段错误无关
问题二:段错误 (Segmentation Fault) #
Jan 23 02:46:49 ... adapter-poller[2546987]: segfault at f0e5 ip 00000000004c72e0
sp 00007f3217ffe340 error 4 likely on CPU 8 (core 8, socket 0)
Jan 23 02:47:00 ... adapter-poller[2547078]: segfault at 7f0ba0c7cc19 ip 00000000004c72e0
sp 00007f0c675fd340 error 4 in panda_strategy-1.0.0[4ac000+246000] likely on CPU 8
Jan 23 02:47:13 ... adapter-poller[2547190]: segfault at 7fe5362ccc18 ip 00000000004c72e0
sp 00007fe2e21fb340 error 4 likely on CPU 8 (core 8, socket 0)
关键信息:
- 崩溃线程:
adapter-poller - 崩溃地址:
0x4c72e0(一致) - 错误代码:4(通常表示访问无效内存地址)
- CPU 绑定:都在 CPU 8 上
- 程序:
panda_strategy-1.0.0
1.2 Coredump 统计 #
通过 coredumpctl list 发现大量段错误记录:
Fri 2026-01-23 02:46:56 UTC 2546660 ... SIGSEGV present ... panda_strategy-1.0.0
Fri 2026-01-23 02:47:07 UTC 2547025 ... SIGSEGV present ... panda_strategy-1.0.0
Fri 2026-01-23 02:47:19 UTC 2547131 ... SIGSEGV present ... panda_strategy-1.0.0
2. 问题定位流程 #
2.0 排查流程图 #
┌─────────────────────────────────────────────────────────────┐
│ 1. 问题发现阶段 │
│ - 系统日志显示 adapter-poller 线程段错误 │
│ - 错误地址一致:0x4c72e0 │
│ - 所有崩溃都在 CPU 8 上 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 2. 初步分析阶段 │
│ - 确认 adapter-poller 线程来源(策略代码) │
│ - 理解进程架构(策略进程 vs TD 进程) │
│ - 确认线程与 Binance TD 的关系 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 3. 堆栈提取阶段 │
│ - coredumpctl dump 导出 coredump │
│ - GDB 分析崩溃线程堆栈 │
│ - 提取寄存器信息 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 4. 源码定位阶段 │
│ - addr2line 定位崩溃地址对应的源码 │
│ - 分析调用链 │
│ - 确定崩溃发生的具体函数 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 5. 代码审查阶段 │
│ - 检查 Adapter 类相关代码 │
│ - 检查 Binance TD 代码 │
│ - 发现类型不匹配和空指针问题 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 6. 根本原因确定 │
│ - Adapter 对象生命周期问题 │
│ - 可能的间接影响(Binance TD 代码问题) │
│ - 制定修复方案 │
└─────────────────────────────────────────────────────────────┘
2.1 初步分析 #
步骤 1:确认线程来源 #
通过代码搜索发现 adapter-poller 线程在策略代码中创建:
// 策略代码中
m_poller = std::thread([this]() {
SPDLOG_LOGGER_WARN(m_logger, "Starting Adapter Poller thread...");
prctl(PR_SET_NAME, "adapter-poller");
// ...
});
步骤 2:理解进程架构 #
进程 A:
panda-strategy(策略进程)- 包含
adapter-poller线程 - 从共享内存读取订单请求
- 通过 MQ 发送到 TD 进程
- 包含
进程 B:TD 进程(独立进程)
- 处理订单请求
- 调用 Binance TD 代码
2.2 堆栈分析 #
步骤 1:提取 Coredump #
coredumpctl dump 2547131 -o /tmp/core_2547131
步骤 2:使用 GDB 分析堆栈 #
gdb -batch \
-ex "file xxx/stg/Strategy/out/build/wsl-profile/panda_strategy-1.0.0" \
-ex "core-file /tmp/core_2547131" \
-ex "thread 1" \
-ex "bt full"
步骤 3:关键堆栈信息 #
崩溃线程堆栈(LWP 2547190 - adapter-poller):
#0 0x00000000004c72e0 in std::_Hashtable<unsigned long,
std::pair<unsigned long const, pandas::Adapter::ContractSpec>, ...>::find
(this=<optimized out>, __k=<optimized out>)
at /usr/local/gcc-13.2.0/include/c++/13.2.0/bits/hashtable.h:1665
__code = <optimized out>
__bkt = <optimized out>
#1 0x00000000004cd58c in operator()() const
at xxx/stg/Strategy/src/Adapter.cpp:307
#2 0x00007fe3574e7764 in execute_native_thread_routine (libstdc++.so.6)
#3 0x00007fe35708a4aa in start_thread (libc.so.6)
#4 0x00007fe35710f510 in __clone3 (libc.so.6)
关键观察:
this=<optimized out>:说明this指针在优化后无法恢复,可能已被销毁__code和__bkt也被优化掉,说明编译器进行了激进优化- 崩溃发生在哈希表查找操作中,访问了无效的内存地址
步骤 4:使用 addr2line 定位源码 #
addr2line -e panda_strategy-1.0.0 -f -C -i 0x4cd58c
输出:
pandadt::LockFreeRingBuffer<pandadt::OrderUpdate, 1024ul>::try_pop
xxx/stg/Strategy/../../panda-datatype/DataType/include/dt/LockFreeRingBuffer.h:35
void pandas::Adapter::pollCommon<pandadt::OrderUpdate, ...>
xxx/stg/Strategy/include/Adapter.h:328
operator()
xxx/stg/Strategy/src/Adapter.cpp:307
2.3 调用链分析 #
完整调用链(从堆栈和 addr2line 结果重建):
adapter-poller 线程启动
↓
std::thread 创建 (execute_native_thread_routine)
↓
Adapter.cpp:307 - operator() (lambda 函数,adapter-poller 主循环)
↓
Adapter.h:328 - pandas::Adapter::pollCommon<OrderUpdate>
↓
LockFreeRingBuffer<OrderUpdate>::try_pop (从环形缓冲区读取订单更新)
↓
std::__atomic_base<unsigned long>::load (原子读取操作)
↓
处理订单更新,需要查找 ContractSpec
↓
访问 pandas::Adapter::ContractSpec 哈希表
↓
std::_Hashtable<unsigned long,
std::pair<unsigned long const, pandas::Adapter::ContractSpec>, ...>::find
(hashtable.h:1665)
↓
【崩溃】访问 this->M_buckets 或 this->M_bucket_count 时
this 指针无效 (this=<optimized out>)
调用链说明:
pollCommon函数处理订单更新消息- 在处理过程中需要查找对应的
ContractSpec(合约规格) - 查找操作访问哈希表时,
this指针指向的对象可能已被销毁
2.4 寄存器分析 #
关键寄存器值(崩溃时):
rax = 0x20b9e628 (通用寄存器)
rbx = 0x7fe2cc000c00 (基址寄存器)
rcx = 0x7fe5362ccc18 ← 可能是无效指针(与错误地址 segfault at 7fe5362ccc18 一致)
rdx = 0x7fe5362ccc00 (数据寄存器)
rsi = 0x7fe2d80008d0 (源索引寄存器)
rdi = 0x7fe2e21fb340 ← 栈指针 (与 sp 00007fe2e21fb340 一致)
rbp = 0x7fe2e21fb450 (基指针)
rsp = 0x7fe2e21fb340 (栈指针)
rip = 0x4c72e0 ← 崩溃地址 (程序计数器)
eflags = 0x10202 [ IF RF ] (中断标志和恢复标志)
寄存器分析:
rcx = 0x7fe5362ccc18:- 与日志中的
segfault at 7fe5362ccc18一致 - 这可能是哈希表节点指针,访问时触发段错误
- 说明该内存地址无效或已被释放
- 与日志中的
this指针状态:this=<optimized out>表示编译器优化后无法恢复- 可能原因:对象已被销毁,或寄存器中的值已被覆盖
错误代码 4:
- 在 x86-64 架构中,错误代码 4 通常表示:
- 用户态访问
- 读操作
- 页面不存在或权限不足
- 在 x86-64 架构中,错误代码 4 通常表示:
栈状态:
rsp和rdi都指向0x7fe2e21fb340,说明栈指针正常- 栈帧看起来是完整的,不是栈溢出问题
3. 根本原因分析 #
3.1 崩溃原因 #
核心问题:在 adapter-poller 线程中访问 pandas::Adapter::ContractSpec 哈希表时,this 指针无效。
崩溃位置:
- 函数:
std::_Hashtable<unsigned long, std::pair<unsigned long const, pandas::Adapter::ContractSpec>, ...>::find - 文件:
/usr/local/gcc-13.2.0/include/c++/13.2.0/bits/hashtable.h:1665 - 问题:
this=<optimized out>表示this指针无效或对象已被销毁
崩溃时的内存访问:
- 根据日志:
segfault at 7fe5362ccc18 - 这个地址存储在
rcx寄存器中 - 可能是哈希表节点的指针,访问时发现内存无效
std::_Hashtable::find 的实现逻辑(简化):
// hashtable.h:1665 附近
iterator find(const key_type& __k) {
// 访问 this->M_buckets (哈希表桶数组)
// 访问 this->M_bucket_count (桶数量)
// 如果 this 指针无效,这里会崩溃
}
可能的原因:
对象生命周期问题:Adapter 对象在
adapter-poller线程访问时已被销毁adapter-poller线程可能持有已销毁对象的引用- 对象析构时未正确等待线程退出
并发访问问题:多个线程同时访问哈希表,导致对象状态不一致
adapter-poller线程与其他线程并发访问- 缺少适当的同步机制
初始化时序问题:哈希表在使用前未正确初始化
ContractSpec哈希表可能在使用时尚未初始化- 初始化与使用的时序存在竞态条件
3.2 与 Binance TD 的关系 #
虽然堆栈显示崩溃发生在 Adapter 类的哈希表访问中,但需要检查:
Binance TD 代码中的潜在问题:
- 类型不匹配:
m_order_ws定义为std::shared_ptr,但使用std::make_unique赋值 - 空指针检查缺失:多处
m_order_ws->send()调用前未检查空指针
- 类型不匹配:
可能的间接影响:
- 如果
adapter-poller线程间接调用了 Binance TD 代码 - 类型不匹配可能导致对象生命周期管理错误
- 进而影响 Adapter 对象的稳定性
- 如果
4. 代码问题定位 #
4.1 问题一:类型不匹配 #
位置:
api/binance_td/include/Binance_AccountBase.h:296-297std::shared_ptr<WsSubsClientWithLatency> m_private_ws = nullptr; std::shared_ptr<WsSubsClientWithLatency> m_order_ws = nullptr;api/binance_td/src/Binance_td.cpp:618, 654m_ubase_account->m_private_ws = std::make_unique<WsSubsClientWithLatency>(cfg); m_ubase_account->m_order_ws = std::make_unique<WsSubsClientWithLatency>(cfg);
问题:
m_order_ws和m_private_ws定义为std::shared_ptr- 但赋值时使用了
std::make_unique - 这会导致类型不匹配,可能引发对象生命周期管理错误
4.2 问题二:空指针检查缺失 #
位置:api/binance_td/include/Binance_UBase.h
- 第 352 行:
m_order_ws->send(reqdata); - 第 395 行:
m_order_ws->send(reqdata); - 第 453 行:
m_order_ws->send(0, reqdata); - 第 479 行:
m_order_ws->send(0, reqdata);
位置:api/binance_td/src/Binance_td.cpp
- 第 691 行:
m_ubase_account->m_order_ws->send(buffer.GetString());
问题:
- 所有
m_order_ws->send()调用前都未检查m_order_ws是否为nullptr - 如果
m_order_ws未初始化,会导致段错误
5. 修复建议 #
5.1 修复类型不匹配 #
修改文件:api/binance_td/src/Binance_td.cpp
修改内容:
// 第 618 行:将 std::make_unique 改为 std::make_shared
m_ubase_account->m_private_ws = std::make_shared<WsSubsClientWithLatency>(cfg);
// 第 654 行:将 std::make_unique 改为 std::make_shared
m_ubase_account->m_order_ws = std::make_shared<WsSubsClientWithLatency>(cfg);
5.2 添加空指针检查 #
修改文件:api/binance_td/include/Binance_UBase.h
修改内容:在所有 m_order_ws->send() 调用前添加检查:
if (!m_order_ws) {
std::cerr << "[错误] m_order_ws 未初始化" << std::endl;
return false;
}
m_order_ws->send(...);
修改文件:api/binance_td/src/Binance_td.cpp
修改内容:在第 691 行添加检查:
if (!m_ubase_account->m_order_ws) {
LERR("[错误] m_order_ws 未初始化");
return -1;
}
m_ubase_account->m_order_ws->send(buffer.GetString());
5.3 Adapter 类问题排查建议 #
虽然堆栈显示崩溃在 Adapter 类中,但建议:
检查 Adapter 对象的生命周期:
- 确保
adapter-poller线程运行时,Adapter 对象未被销毁 - 使用
std::shared_ptr或确保线程退出前对象存在
- 确保
检查 ContractSpec 哈希表的初始化:
- 确保在使用前已正确初始化
- 检查是否有并发访问导致的状态不一致
添加防御性检查:
- 在访问哈希表前检查对象有效性
- 使用互斥锁保护并发访问
6. 排查流程总结 #
6.1 问题发现阶段 #
步骤 1:日志分析
# 查看系统日志中的段错误
grep "adapter-poller.*segfault" /var/log/messages
# 查看 coredump 列表
coredumpctl list | grep panda_strategy
发现:
adapter-poller线程多次段错误- 错误地址一致:
0x4c72e0 - 所有崩溃都发生在 CPU 8 上
- 错误代码:4(访问无效内存地址)
6.2 堆栈提取阶段 #
步骤 1:导出 Coredump
# 导出最新的 coredump
coredumpctl dump 2547131 -o /tmp/core_2547131
步骤 2:GDB 分析
# 使用 GDB 分析堆栈
gdb -batch \
-ex "file /path/to/panda_strategy-1.0.0" \
-ex "core-file /tmp/core_2547131" \
-ex "thread 1" \
-ex "bt full" \
-ex "info registers"
步骤 3:源码定位
# 使用 addr2line 定位源码
addr2line -e panda_strategy-1.0.0 -f -C -i 0x4cd58c
关键发现:
- 崩溃线程:LWP 2547190 (adapter-poller)
- 崩溃函数:
std::_Hashtable::find - 调用来源:
Adapter.cpp:307→Adapter.h:328
6.3 调用链分析阶段 #
分析过程:
- 从堆栈 #0 开始,确认崩溃发生在哈希表查找
- 回溯到 #1,定位到
Adapter.cpp:307的 lambda 函数 - 使用
addr2line确认调用链:Adapter::pollCommon<OrderUpdate>LockFreeRingBuffer::try_popstd::_Hashtable::find
结论:
- 崩溃发生在处理订单更新时
- 访问
ContractSpec哈希表时this指针无效
6.4 代码审查阶段 #
审查范围:
Binance TD 代码:
- 发现类型不匹配:
std::make_uniquevsstd::shared_ptr - 发现空指针检查缺失:5 处
m_order_ws->send()调用
- 发现类型不匹配:
Adapter 类代码(需要进一步检查):
- 对象生命周期管理
- 哈希表的初始化时机
- 并发访问保护
6.5 问题关联分析 #
关键问题:
- 虽然堆栈显示崩溃在 Adapter 类中,但 Binance TD 的代码问题可能导致间接影响
- 类型不匹配可能导致对象生命周期管理错误
- 空指针访问可能导致程序状态异常
排查工具链:
系统日志 (/var/log/messages)
↓
coredumpctl list (查找相关 coredump)
↓
coredumpctl dump (导出 coredump 文件)
↓
GDB 分析 (提取堆栈和寄存器信息)
↓
addr2line (定位源码文件和行号)
↓
源码审查 (分析调用链和代码逻辑)
↓
问题定位 (确定根本原因)
6.6 关键命令总结 #
1. 查看系统日志中的段错误:
grep "adapter-poller.*segfault" /var/log/messages
2. 列出相关 coredump:
coredumpctl list | grep panda_strategy
3. 导出 coredump:
coredumpctl dump <PID> -o /tmp/core_<PID>
4. GDB 分析堆栈:
gdb -batch \
-ex "file /path/to/binary" \
-ex "core-file /tmp/core_<PID>" \
-ex "thread <thread_num>" \
-ex "bt full" \
-ex "info registers"
5. 源码定位:
addr2line -e /path/to/binary -f -C -i <address>
6. 查看汇编代码:
objdump -d /path/to/binary | grep -A 10 "<address>:"
7. 结论与建议 #
7.1 直接原因 #
崩溃原因:
adapter-poller 线程在访问 pandas::Adapter::ContractSpec 哈希表时,this 指针无效,导致段错误。
技术细节:
- 崩溃地址:
0x4c72e0(std::_Hashtable::find函数内部) - 错误类型:SIGSEGV,错误代码 4(访问无效内存地址)
- 线程:adapter-poller (LWP 2547190)
- CPU 绑定:CPU 8
7.2 根本原因分析 #
7.2.1 Adapter 对象生命周期问题(最可能) #
问题:
adapter-poller线程可能在 Adapter 对象销毁后仍在运行- 线程持有已销毁对象的引用
- 访问哈希表时
this指针指向已释放的内存
证据:
- 堆栈显示
this=<optimized out>,说明对象可能已被销毁 - 寄存器
rcx值为0x7fe5362ccc18,可能是无效指针
7.2.2 Binance TD 代码问题(潜在影响) #
问题:
类型不匹配:
m_order_ws定义为std::shared_ptr,但使用std::make_unique赋值- 可能导致对象生命周期管理错误
空指针检查缺失:
- 5 处
m_order_ws->send()调用前未检查空指针 - 可能导致程序状态异常
- 5 处
影响:
- 虽然堆栈显示崩溃在 Adapter 类中,但 Binance TD 的问题可能导致间接影响
- 如果
adapter-poller线程间接调用 Binance TD 代码,这些问题可能触发崩溃
7.3 修复建议 #
7.3.1 立即修复(高优先级) #
1. 修复类型不匹配
// api/binance_td/src/Binance_td.cpp
// 第 618 行和第 654 行
m_ubase_account->m_private_ws = std::make_shared<WsSubsClientWithLatency>(cfg);
m_ubase_account->m_order_ws = std::make_shared<WsSubsClientWithLatency>(cfg);
2. 添加空指针检查
// api/binance_td/include/Binance_UBase.h
// 所有 m_order_ws->send() 调用前
if (!m_order_ws) {
std::cerr << "[错误] m_order_ws 未初始化" << std::endl;
return false;
}
7.3.2 深入排查(中优先级) #
1. 检查 Adapter 对象生命周期
- 确保
adapter-poller线程退出前 Adapter 对象不被销毁 - 使用
std::shared_ptr或确保线程退出顺序正确
2. 检查 ContractSpec 哈希表初始化
- 确认哈希表在使用前已正确初始化
- 检查初始化与使用的时序是否存在竞态条件
3. 添加并发保护
- 使用互斥锁保护哈希表的并发访问
- 检查是否有其他线程同时修改哈希表
7.3.3 防御性改进(低优先级) #
1. 添加更多日志
- 在关键位置添加日志,记录对象状态
- 记录哈希表的访问和修改操作
2. 添加断言
- 在访问哈希表前添加断言检查
- 使用
assert(this != nullptr)等检查
7.4 验证方法 #
1. 修复后验证
# 重新编译并运行
# 观察是否还有段错误
coredumpctl list | grep adapter-poller
# 监控日志
tail -f /var/log/messages | grep "adapter-poller"
2. 压力测试
- 长时间运行,观察是否稳定
- 模拟高负载场景,检查是否触发问题
3. 内存检查
- 使用 Valgrind 或 AddressSanitizer 检查内存问题
- 检查是否有内存泄漏或越界访问
8. 附录 #
8.1 技术术语说明 #
- SIGSEGV:段错误信号,当程序访问无效内存地址时触发
- Coredump:程序崩溃时的内存转储文件,包含崩溃时的完整状态
- LWP (Light Weight Process):轻量级进程,在 Linux 中通常指线程
- this 指针:C++ 中指向当前对象的指针
- std::shared_ptr:C++11 智能指针,支持多个对象共享所有权
- std::unique_ptr:C++11 智能指针,独占所有权
8.2 错误代码说明 #
错误代码 4 的含义(x86-64 架构):
- Bit 0 (P):0 = 页面不存在
- Bit 1 (W/R):0 = 读操作
- Bit 2 (U/S):1 = 用户态访问
- Bit 3 (RSVD):0 = 保留位
说明:程序尝试读取一个不存在的内存页面。
8.3 相关文件路径 #
策略代码(panda-strategy 项目):
xxx/stg/Strategy/src/Adapter.cpp:307xxx/stg/Strategy/include/Adapter.h:328
Binance TD 代码(panda-md-infra 项目):
xxx/infra/api/binance_td/include/Binance_UBase.hxxx/infra/api/binance_td/include/Binance_AccountBase.h:296-297xxx/infra/api/binance_td/src/Binance_td.cpp:618, 654, 691
8.4 排查工具版本 #
- coredumpctl:systemd 工具,用于管理 coredump
- GDB:GNU Debugger,用于调试和分析 coredump
- addr2line:将地址转换为源码位置的工具
- objdump:反汇编工具,用于查看汇编代码
8.5 参考文档 #
报告生成时间:2026-01-23
分析工具:coredumpctl, gdb, addr2line