Skip to main content

adapter-poller 线程段错误问题定位与分析报告

·7 mins

adapter-poller 线程段错误问题定位与分析报告 #

执行摘要 #

问题adapter-poller 线程在 panda-strategy 进程中多次发生段错误(SIGSEGV)。

崩溃位置std::_Hashtable<...pandas::Adapter::ContractSpec...>::find (hashtable.h:1665)

根本原因:访问 pandas::Adapter::ContractSpec 哈希表时,this 指针无效,对象可能已被销毁。

关键发现

  1. 崩溃发生在 adapter-poller 线程处理订单更新时
  2. 调用链:Adapter.cpp:307Adapter::pollCommonstd::_Hashtable::find
  3. Binance TD 代码存在类型不匹配和空指针检查缺失问题

修复建议

  1. 修复 Binance TD 代码中的类型不匹配(std::make_uniquestd::make_shared
  2. 添加空指针检查(5 处 m_order_ws->send() 调用)
  3. 检查 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:理解进程架构 #

  • 进程 Apanda-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 ] (中断标志和恢复标志)

寄存器分析

  1. rcx = 0x7fe5362ccc18

    • 与日志中的 segfault at 7fe5362ccc18 一致
    • 这可能是哈希表节点指针,访问时触发段错误
    • 说明该内存地址无效或已被释放
  2. this 指针状态

    • this=<optimized out> 表示编译器优化后无法恢复
    • 可能原因:对象已被销毁,或寄存器中的值已被覆盖
  3. 错误代码 4

    • 在 x86-64 架构中,错误代码 4 通常表示:
      • 用户态访问
      • 读操作
      • 页面不存在或权限不足
  4. 栈状态

    • rsprdi 都指向 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 指针无效,这里会崩溃
}

可能的原因

  1. 对象生命周期问题:Adapter 对象在 adapter-poller 线程访问时已被销毁

    • adapter-poller 线程可能持有已销毁对象的引用
    • 对象析构时未正确等待线程退出
  2. 并发访问问题:多个线程同时访问哈希表,导致对象状态不一致

    • adapter-poller 线程与其他线程并发访问
    • 缺少适当的同步机制
  3. 初始化时序问题:哈希表在使用前未正确初始化

    • ContractSpec 哈希表可能在使用时尚未初始化
    • 初始化与使用的时序存在竞态条件

3.2 与 Binance TD 的关系 #

虽然堆栈显示崩溃发生在 Adapter 类的哈希表访问中,但需要检查:

  1. Binance TD 代码中的潜在问题

    • 类型不匹配:m_order_ws 定义为 std::shared_ptr,但使用 std::make_unique 赋值
    • 空指针检查缺失:多处 m_order_ws->send() 调用前未检查空指针
  2. 可能的间接影响

    • 如果 adapter-poller 线程间接调用了 Binance TD 代码
    • 类型不匹配可能导致对象生命周期管理错误
    • 进而影响 Adapter 对象的稳定性

4. 代码问题定位 #

4.1 问题一:类型不匹配 #

位置

  • api/binance_td/include/Binance_AccountBase.h:296-297

    std::shared_ptr<WsSubsClientWithLatency> m_private_ws = nullptr;
    std::shared_ptr<WsSubsClientWithLatency> m_order_ws = nullptr;
    
  • api/binance_td/src/Binance_td.cpp:618, 654

    m_ubase_account->m_private_ws = std::make_unique<WsSubsClientWithLatency>(cfg);
    m_ubase_account->m_order_ws = std::make_unique<WsSubsClientWithLatency>(cfg);
    

问题

  • m_order_wsm_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 类中,但建议:

  1. 检查 Adapter 对象的生命周期

    • 确保 adapter-poller 线程运行时,Adapter 对象未被销毁
    • 使用 std::shared_ptr 或确保线程退出前对象存在
  2. 检查 ContractSpec 哈希表的初始化

    • 确保在使用前已正确初始化
    • 检查是否有并发访问导致的状态不一致
  3. 添加防御性检查

    • 在访问哈希表前检查对象有效性
    • 使用互斥锁保护并发访问

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:307Adapter.h:328

6.3 调用链分析阶段 #

分析过程

  1. 从堆栈 #0 开始,确认崩溃发生在哈希表查找
  2. 回溯到 #1,定位到 Adapter.cpp:307 的 lambda 函数
  3. 使用 addr2line 确认调用链:
    • Adapter::pollCommon<OrderUpdate>
    • LockFreeRingBuffer::try_pop
    • std::_Hashtable::find

结论

  • 崩溃发生在处理订单更新时
  • 访问 ContractSpec 哈希表时 this 指针无效

6.4 代码审查阶段 #

审查范围

  1. Binance TD 代码

    • 发现类型不匹配:std::make_unique vs std::shared_ptr
    • 发现空指针检查缺失:5 处 m_order_ws->send() 调用
  2. 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 指针无效,导致段错误。

技术细节

  • 崩溃地址0x4c72e0std::_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 代码问题(潜在影响) #

问题

  1. 类型不匹配

    • m_order_ws 定义为 std::shared_ptr,但使用 std::make_unique 赋值
    • 可能导致对象生命周期管理错误
  2. 空指针检查缺失

    • 5 处 m_order_ws->send() 调用前未检查空指针
    • 可能导致程序状态异常

影响

  • 虽然堆栈显示崩溃在 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:307
  • xxx/stg/Strategy/include/Adapter.h:328

Binance TD 代码(panda-md-infra 项目):

  • xxx/infra/api/binance_td/include/Binance_UBase.h
  • xxx/infra/api/binance_td/include/Binance_AccountBase.h:296-297
  • xxx/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