#golang

如何追踪golang channel?

2023年就要结束了,算起来距离上一次更新也有很久了。搜肠刮肚,总得在23年结束前再搞两篇总结,算是有始有终。总结今年,总还是绕不过 BPF,golang。既然如此,就对BPF观测golang这个话题再往下挖掘下,先做第一篇文章。下旬如果有时间并且顺利的话,希望能把BPF的原理总结完成。

无侵入观测服务拓扑四元组的一种实现中,笔者有提到追踪golang处理过程的两个无法解决的问题是golang里的channel处理以及goroutine pool。再深究下,这两个问题实际上都可以归纳到对channel的处理,因为很多goroutine pool都离不了channel的使用,比如Jeffail/tunny这个库。
本文将会构建一个channel的追踪的方案。

·4min·李岩
如何追踪golang channel?

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·李岩
无侵入观测服务拓扑四元组的一种实现

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+ 调用规约

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

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

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

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