bpftrace 遍历 golang 链表(go17+)

不出意外的,之前提到的 ELF 文件解析内容又拖延了。目前还不知道什么时候有时间能够把希望完成的几篇文章给搞完。翻一翻目前的博客,已经有很久没有更新了。那就水一篇文章吧。目前算是项目里的低谷期,希望能够重拾程序员的意义。

bpftrace 无侵入遍历golang链表里,笔者展示了使用bpftrace来遍历golang链表的方法。由于go-17go-16的函数调用规约存在不同,因此bpftrace 无侵入遍历golang链表并不适用于go-17。其实这个问题在go-1.17+ 调用规约已经提到了解决方案。本文给一个实例,算是更进一步的延伸这个话题,希望能够起到一些效果。

·2min·李岩
bpftrace 遍历 golang 链表(go17+)

eBPF tail-calls示例

最近在整理一些技术文章。本来希望把涉及ELF的内容整理出来,结果发现太难了。ELF涉及的内容要多很多,如果要把希望整理的内容表述清楚,还需要做一些准备的工作。刚好最近完成了tail-calls 的调研,先把关于eBPF的tail-calls的功能整理下吧。

eBPF程序是事件驱动的,这就意味着当目标事件触发后,程序才能执行。考虑这样一个场景:有几个不同的BPF程序均挂载在相同的hook点上,而执行需要保持一定的顺序。这时就需要借助tail calls的功能来实现。

·4min·李岩
eBPF tail-calls示例

BPF追踪Go程序的挑战

原文地址Challenges of BPF Tracing Go。翻译不尽如人意,继续努力。

BPF追踪Go程序的挑战

当大家对Go 1.17语言调用规约(function calling convention)调整带来的性能优化感到兴奋时,我却遗憾的看到Go 1.17并没有让BPF uretprobe变得可行。事实证明,我还没有完全意识到Go的可调整的栈空间会让事情变得多复杂。

·13min·李岩
BPF追踪Go程序的挑战

PlantUML-文本化绘制UML多类图表

笔者一直都是文本编辑器教派的忠实拥趸:期望将所有的任务都通过文本编辑,而非鼠标/触摸板等,进行实现。从早年的Vim,到现而今的Emacs,对文本化完成需求是越来越习惯,也越来越依赖了。最近刚好有了些时间,把最近的一些实践整理下。

在之前的文章,emacs org-mode 绘制思维导图,中,笔者有提到在探索不跳出Emacs这一文本编辑器的情况下,完成思维导图绘制的需求。在翻了一些文章后,找到了一款神器:PlantUML。其完美的匹配了笔者的需求:

  1. 不仅是思维导图,工程、文档常用的UML图像也能全部支持文本化表示;
  2. 功能强大,颜色、文本等格式均能支持;
  3. Emacs友好,而且可以集成到Org-mode里使用。

而且,PlantUML支持在线使用,意味着能够很方便的获取、使用。这里做下介绍。

·4min·李岩
PlantUML-文本化绘制UML多类图表

无侵入观测服务拓扑四元组的一种实现

最近有了些时间,继续整理下之前的项目。服务四元组的信息对于故障处置、根因定位等都有重要意义。使用eBPF可以做到无侵入用户代码获取服务四元组信息的功能。这一点在工程应用上很有意义。笔者在这方面投入了一些精力,这里做一下简单的总结。

服务四元组指的是[caller, caller_func, callee, callee_func]四元组。如下图是一个调用示例,站在服务A的角度,就存在如下两个四元组: [A, /a, B, /b],[A, /a, C, /c]。站在服务B, C的角度,也存在两个四元组(可能有不同的理解): [B, /b, none, none], [C, /c, none, none]。

                       service call                  
                                                     
     ,-------.          ,-.          ,-.          ,-.
     |outisde|          |A|          |B|          |C|
     `---+---'          `+'          `+'          `+'
         |      /a       |            |            | 
         |-------------->|            |            | 
         |               |            |            | 
         |               |    /b      |            | 
         |               |----------->|            | 
         |               |            |            | 
         |               |           /c            | 
         |               |------------------------>| 
     ,---+---.          ,+.          ,+.          ,+.
     |outisde|          |A|          |B|          |C|
     `-------'          `-'          `-'          `-'

在弄清楚四元组是什么之后,下面进入今天的话题:如何使用BPF来采集四元组。需要说明的是,笔者这里的语言使用的是golang-1.16golang不同语言版本间的区别,见:golang-1.17+调用规约
值得注意的是,关于观测服务数据,是有很多解决方案的。本文仅是笔者实践的一种解决方案,在文末会简单提到这种方案的优缺点。
按照惯例,先看下效果吧:

# 启动采集
bpftrace ./http.bt
Attaching 2 probes...  # 未触发请求前,停止在这里
caller:                # 触发请求后,输出
  	caller_path: /handle
callee:
  	method: GET
  	host: 0.0.0.0:9932
  	url: /echo
  
caller:
  	caller_path: /echo
callee: none
  
# 开始服务
./http_demo &
# 触发请求
curl http://0.0.0.0:9932/handle
·6min·李岩
无侵入观测服务拓扑四元组的一种实现

go-1.17+ 调用规约

go-1.17是一个很不友好的版本,这里我指的是函数调用规约的变更。在此之前,虽然栈传参比较奇怪,但是在掌握了规律后,参数信息很好获取。升级到go-1.17之后,笔者发现变更后的寄存器传值方式并不是系统的调用规约,至少和C/C++的是完全不一致的。这个问题使得笔者在处理ebpf方案时,始终无法覆盖go-1.17+的版本。虽然短期不会造成影响,线上服务使用的大多还在go-1.16以下,但是这始终是一个绕不过去的问题。近期通过查阅资料和参考其他开源项目里对这部分内容的处理,整理了一下go-1.17+的调用规约。

go1.17之前使用的是内存栈来传递参数,这种传参的方式使得golang的语言设计很灵活:golang函数的多返回值能够很容易的实现。同样的,由于golang需要这样灵活的能力,是的系统默认的调用规约方式并不适用。在Proposal: Register-based Go calling convention文章里对这个问题进行了详细的讨论,总结起来是golang的特性使得使用系统默认规约并不能带来多语言交互上的收益,且golang希望保持独特。
本文下面会给出总结的调用规约,并且给出验证程序。本文档的整理所基于的平台是x86_64centos8系统。其他架构下,寄存器名称可能不同。

·4min·李岩
go-1.17+ 调用规约