#BPF

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程序的挑战

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

最近有了些时间,继续整理下之前的项目。服务四元组的信息对于故障处置、根因定位等都有重要意义。使用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·李岩
无侵入观测服务拓扑四元组的一种实现

ebpf 采集ebpf 采集tag+tcp五元组

在这里对文章题目作一些说明。笔者想了很长时间也无法给这篇文章想个恰当的表意题目。实际上使用ebpf来进行服务观测是有在进行的,比如获取目前l1s上的常见的四元组。但是本文不是介绍这部分可观测实践的。文章希望阐述的场景是:采集请求触发里的一些信息(诸如trace及其他header等)并和服务请求下游的传输层五元组(protocol, src-ip, src-port, dst-ip, dst-port)进行关联。这也是最近工作中实际遇到的问题。

基于ebpf的丰富的特性能够获取服务很多的信息,不同特性的组合更是可以达到极强的数据整合能力。比如通过uprobe便捷的获取业务信息后,结合kprobe来获取系统调用里的内容,可以获取一般侵入式可观测代码无法获取的内容。笔者最近遇到的一个实际问题是:获取服务A的接口/a响应后,向下游B发起的请求时,所使用的传输层五元组,同时带上结合一些/a触发时的一些内容,比如caller_fun或者traceId
这里值得说明的是,用户态请求的是一个域名。域名的解析是在golanghttp里完成的。但是请注意,golang发起tcp请求时,local port设置的是0,然后由内核态的tpc处理来选择一个空闲的port作为socket里的lport。这部分的信息通过代码的埋点显然是无法获取的(详情可参考TCP连接中客户端的端口号是如何确定的?)。
下面介绍下实现效果及思路。

关于bpftrace使用的介绍,可以参见:bpftrace 无侵入遍历golang链表,关于ebpf来进行数据采集的实践,可以参见ebpf采集mysql请求信息及ebpf对应用安全的思考

·4min·李岩
ebpf 采集ebpf 采集tag+tcp五元组

golang常见类型作为参数的eBPF解析

即将过去的2022年,笔者相当比例的精力都投入在了eBPF上。最初的时候,写了一篇golang 常见类型字节数 ,开启了eBPF+golang的总结性工作。此后陆续整理了一些关于ebpf的使用文章,同时项目也在逐步的推进。eBPF的实际落地有很大的挑战,但是最终还是找到了一些落地的场景。年底了,结合最近的调研工作,笔者整理了这篇文章。既算是对之前文章的呼应,也是对今年整理内容的总结。

eBPF能够提供一种切入服务细节的独特视角。本文即通过实例,对golang常见类型作为函数参数时进行解析,期望读者能够感受这一视角。需要说明的是,本文是基于golang-1.16来整理的。

·7min·李岩
golang常见类型作为参数的eBPF解析