chenBright opened a new issue, #2797:
URL: https://github.com/apache/brpc/issues/2797

   **Is your feature request related to a problem? (你需要的功能是否与某个问题有关?)**
   
   相关isue:#2389 
   
   gdb+gdb_bthread_stack.py主要的缺点是要慢和阻塞进程,需要一种高效的追踪bthread调用栈的方法。
   
   bRPC框架的协作式用户态协程无法像Golang内建的抢占式协程一样实现高效的STW(Stop the 
World),框架也无法干预用户逻辑的执行,所以要追踪bthread调用栈是比较困难的。
   
   在线追踪bthread调用栈需要解决以下问题:
   1. 追踪挂起bthread的调用栈。
   2. 追踪运行中bthread的调用栈。
   
   **Describe the solution you'd like (描述你期望的解决方法)**
   
   ## bthread状态模型
   
   以下是目前的bthread状态模型。
   
   
![bthread状态模型](https://github.com/user-attachments/assets/1593d6ed-2ad3-417a-96c3-20e7fb46ece2)
   
   ## 设计方案
   
   ### 核心思路
   
   为了解决上述两个问题,该方案实现了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:
   
   1. gpertools:对于已经使用cpu/heap 
profiler的用户,可以不引入额外的库满足回溯指定上下文调用栈的需求。但是在x86_64上测试验证,gpertools 
2.7版本的GetStackTraceWithContext/GetStackFramesWithContext的回溯结果还是当前调用位置的调用栈,不符合预期。此外,用户不一定使用tcmalloc(gpertools),可能使用jemalloc。
   2. abseil-cpp:最新版本要求C++14以上,早起版本支持C++11。实际效果未验证。
   3. libunwind:在x86_64上测试验证符合预期。没有其他依赖。
   
   综合考虑,选择**libunwind**。
   
   #### 信号追踪模式
   
   
信号追踪模式可以追踪运行中bthread的调用栈。运行中bthread是不稳定的,不能使用TaskMeta.stack来追踪bthread调用栈。只能另辟蹊径,使用信号中断bthread运行逻辑,在信号处理函数中回溯bthread调用栈。使用信号有两个问题:
   
   1. 异步信号安全问题。libunwind回溯调用栈的函数是异步信号安全,可以满足需求。
   2. 
信号追踪模式不支持jump_stack。调用栈回溯需要寄存器信息,但jump_stack会操作寄存器,这个过程是不安全的,所以jump_stack不能被信号中断,需要在jump_stack前拦截bthread的调度,等到bthread调用栈追踪完成后才继续挂起bthread。
   
   所以,追踪模式只支持运行状态。
   
   #### 小结
   
   jump_stack是bthread挂起或者运行的必经之路,也是STB的拦截点。STB将状态分成三类:
   1. 上下文追踪模式的状态:就绪、挂起。
   2.  支持信号追踪模式的状态:运行。
   3. 
不支持追踪的状态。jump_stack的过程是不允许使用以上两种调用栈追踪方法,需要在jump_stack前拦截bthread的调度,等到调用栈追踪完成后才继续调度bthread。
   
   #### 详细流程
   
   以下是引入STB后的bthread状态模型,在原来bthread状态模型的基础上,加入两个状态(拦截点):将运行、挂起中。
   
   ![bthread 
STB状态模型](https://github.com/user-attachments/assets/2e3209d8-8241-4ecb-9bc1-c498b2076416)
   
   
   为了实现状态流转,在TaskMeta中增加一个`butil::atomic<BthreadStatus> 
status`,表示bthread状态,状态流转是原子的(CAS)。开始追踪的时候,status=-status,变成负值;结束的时候,status=-status,变会正值,即用status的最高位作为追踪的标识。
   
   ```c++
   enum BthreadStatus {
       BTHREAD_STATUS_CREATED = 0,  // 创建
       BTHREAD_STATUS_READY,        // 就绪
       BTHREAD_STATUS_PRE_RUN,      // 将运行
       BTHREAD_STATUS_RUNNING,      // 运行
       BTHREAD_STATUS_SUSPENDING,   // 挂起中
       BTHREAD_STATUS_SUSPENDED,    // 挂起
       BTHREAD_STATUS_END,          // 销毁
   };
   ```
   
   经过上述分析,总结出STB的流程:
   
   1. 
stb(实现STB的一个模块)收到追踪bthread调用栈的请求时,标识正在追踪。追踪完成后,标识追踪完成,并stb调signal()通知可能处于将运行或者挂起中状态的bthread。根据bthread状态,stb执行不同的逻辑:
     - 创建、就绪但还没分配栈、销毁:直接结束追踪。
     - 挂起、就绪:使用上下文追踪模式追踪bthread的调用栈。
     - 运行:使用信号追踪模式追踪bthread的调用栈。
     - 
将运行、挂起中:stb调wait()等到bthread状态流转到下一个状态(挂起或者运行),bthread调signal()通知stb继续追踪。注:这里只会阻塞stb线程,所以wait/signal使用pthread条件变量实现即可。
       
   2. stb追踪时,bthread根据状态也会执行不同的逻辑:
     - 创建、就绪但还没分配栈、销毁、就绪:不需要额外处理。
     -  挂起、运行:唤醒stb继续追踪。
     - 
将运行、挂起中:bthread调wait()等到stb追踪完成并signal()后才继续执行jump_stack。注:这里竞争的概率比较小,wait/signal使用pthread条件变量实现即可。
   
   ## 性能
   
   正常情况下,不会追踪bthread调用栈,该特性只是增加了记录状态流转的原子操作(cas)。所以,可以先测试一下状态流转cas的性能消耗:
   1. 
纯框架调度场景:32个worker,起100个bthread,循环调bthread_yield,并且计数和计时,cpu没有明细变化,都是接近跑满32个核,cas也没有导致耗时增加。注:bthread_yield会调度下一个bthread,并将当前bthread放到调度队列,消除挂起操作的影响,基本上可以认为cpu都是在执行框架调度逻辑了。
   2. rpc场景:使用multi_threaded_echo_c++,client、server的cpu核数和耗时都基本没有变化。
   
   ## 其他
   
   1. 同一时刻只能追踪一个bthread,即追踪的并发数最大为1。
   2. 目前只支持x86_64架构。后续逐渐支持其他架构。
   
   **Describe alternatives you've considered (描述你想到的折衷方案)**
   
   
   **Additional context/screenshots (更多上下文/截图)**
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@brpc.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@brpc.apache.org
For additional commands, e-mail: dev-h...@brpc.apache.org

Reply via email to