Open chenBright opened 3 weeks ago
@wwbmmm 有空帮忙评估一下
赞,这个功能打算怎么使用呢?通过内置服务查看调用栈?还是将栈打印到日志中?
综合考虑,选择libunwind。
这个可以考虑做成编译开关,如果不用的话就不需要引入新的编译依赖
赞,这个功能打算怎么使用呢?通过内置服务查看调用栈?还是将栈打印到日志中?
通过BthreadsService查看调用栈。对于不适用内置服务的场景,可以调接口获取调用栈。
综合考虑,选择libunwind。
这个可以考虑做成编译开关,如果不用的话就不需要引入新的编译依赖
嗯,是打算搞个编译开关,按需打开。
Is your feature request related to a problem? (你需要的功能是否与某个问题有关?)
相关isue:#2389
gdb(ptrace)+gdb_bthread_stack.py主要的缺点是要慢和阻塞进程,需要一种高效的追踪bthread调用栈的方法。
bRPC框架的协作式用户态协程无法像Golang内建的抢占式协程一样实现高效的STW(Stop the World),框架也无法干预用户逻辑的执行,所以要追踪bthread调用栈是比较困难的。
在线追踪bthread调用栈需要解决以下问题:
Describe the solution you'd like (描述你期望的解决方法)
bthread状态模型
以下是目前的bthread状态模型。
设计方案
核心思路
为了解决上述两个问题,该方案实现了STB(Stop The Bthread),核心思路可以简单总结为,在追踪bthread调用栈的过程中,状态不能流转到当前追踪方法不支持的状态。STB包含了两种追踪模式:上下文(context)追踪模式和信号追踪模式。
上下文(context)追踪模式
上下文追踪模式可以追踪挂起bthread的调用栈。挂起的bthread栈是稳定的,利用TaskMeta.stack中保存的上下文信息(x86_64下关键的寄存器主要是RIP、RSP、RBP),通过一些可以回溯指定上下文调用栈的库来追踪bthread调用栈。但是挂起的bthread随时可能会被唤醒,执行逻辑(包括jump_stack),则bthread栈会一直变化。不稳定的上下文是不能用来追踪调用栈的,需要在jump_stack前拦截bthread的调度,等到调用栈追踪完成后才继续运行bthread。所以,上下文追踪模式支持就绪、挂起这两个状态。
目前调研到,支持回溯指定上下文调用栈的库有gpertools、abseil-cpp和libunwind,其中:
综合考虑,选择libunwind。
信号追踪模式
信号追踪模式可以追踪运行中bthread的调用栈。运行中bthread是不稳定的,不能使用TaskMeta.stack来追踪bthread调用栈。只能另辟蹊径,使用信号中断bthread运行逻辑,在信号处理函数中回溯bthread调用栈。使用信号有两个问题:
所以,追踪模式只支持运行状态。
小结
jump_stack是bthread挂起或者运行的必经之路,也是STB的拦截点。STB将状态分成三类:
详细流程
以下是引入STB后的bthread状态模型,在原来bthread状态模型的基础上,加入两个状态(拦截点):将运行、挂起中。
为了实现状态流转,在TaskMeta中增加一个
butil::atomic<BthreadStatus> status
,表示bthread状态,状态流转是原子的(CAS)。开始追踪的时候,status=-status,变成负值;结束的时候,status=-status,变会正值,即用status的最高位作为追踪的标识。经过上述分析,总结出STB的流程:
stb(实现STB的一个模块)收到追踪bthread调用栈的请求时,标识正在追踪。追踪完成后,标识追踪完成,并stb调signal()通知可能处于将运行或者挂起中状态的bthread。根据bthread状态,stb执行不同的逻辑:
stb追踪时,bthread根据状态也会执行不同的逻辑:
性能
正常情况下,不会追踪bthread调用栈,该特性只是增加了记录状态流转的原子操作(CAS)。所以,可以先测试一下状态流转CAS的性能消耗:
其他
Describe alternatives you've considered (描述你想到的折衷方案)
Additional context/screenshots (更多上下文/截图)