ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Kyanos 调试指南:从构建、日志到 eBPF 探针的完整排查实战

Kyanos 调试指南:从构建、日志到 eBPF 探针的完整排查实战 Kyanos 调试指南从构建、日志到 eBPF 探针的完整排查实战【免费下载链接】kyanosKyanos is a networking analysis tool using eBPF. It can visualize the time packets spend in the kernel, capture requests/responses, makes troubleshooting more efficient.项目地址: https://gitcode.com/GitHub_Trending/ky/kyanos本篇调试指南围绕 Kyanos基于 eBPF 的网络分析工具的开发与排障场景展开系统覆盖构建流程、模块化日志、eBPF 内核态调试与 IDE 断点调试四大环节。读完本文你将掌握修改 eBPF 代码后如何正确重新生成与构建、如何按 agent/conntrack/protocol/uprobe 等模块精准开启 debug 日志、如何利用--debug-output直接观察抓取到的请求、如何在内核态代码中通过bpf_printk定位协议推断问题以及如何在 VS Code 中配置断点调试从而高效定位 Kyanos 的各类疑难问题。构建相关改完 eBPF 代码必须重新生成Kyanos 同时包含用户态 Go 代码与内核态 eBPF 程序.bpf.c其中 eBPF 代码在构建阶段会被编译并通过go generate生成对应的.bpfel.go绑定文件见 Makefile 中的build-bpf目标它会对 amd64、arm64 两个架构分别执行TARGETxxx go generate ./bpf/。因此每次修改 eBPF 相关代码后都需要先重新生成 bpf 相关代码再执行普通构建或在 IDE 里调试make build-bpf # 重新生成 bpf 绑定代码 make # 然后再进行普通构建漏掉make build-bpf直接make改动不会生效——你调试的仍是旧的内核态程序。构建带调试信息的二进制普通make构建出的二进制经过了编译优化对应 Makefile 中的go build变量被优化、函数内联断点调试时会很痛苦。项目为此提供了专门的调试构建目标make kyanos-debug该目标在 Makefile 中实现本质是export CGO_LDFLAGS-Xlinker -rpath. -static go build -gcflags all-N -l其中-gcflags all-N -l的含义是-N禁止编译器优化-l禁止函数内联。这样生成的kyanos二进制保留了完整的调试信息适合配合 Delve 进行断点调试。远程调试组合目标对于需要在目标机器上运行的场景项目还提供了remote-debug目标Makefilemake remote-debug # 等价于 build-bpf kyanos-debug dlv它会依次执行build-bpf、kyanos-debug和dlv。dlv目标Makefile会以 headless 模式启动 Delvechmod x kyanos dlv --headless --listen:2345 --api-version2 --check-go-versionfalse exec ./kyanos即本地生成带调试信息的二进制在目标机器上以 headless 方式监听:2345端口供远程 IDE 连接调试。日志相关分模块、分级别精准定位问题Kyanos 的日志体系是模块化的每个模块拥有独立的 logger见 common/log.go 中定义的AgentLog、BPFEventLog、UprobeLog、ConntrackLog、ProtocolParserLog等。启动时可以分别为每个模块指定日志级别5 为 debug 级别默认是 warn 级别对应 cmd/common.go默认级别不合法时回退到WarnLevel。参数含义--agent-log-level指定 agent 模块日志级别主要是和 Agent 启动等相关的日志--bpf-event-log-level指定 bpf 事件日志级别一些内核上报的和 syscall 层上报的事件会打印出来--conntrack-log-level指定 conntrack 模块日志级别一些连接相关的事件比如连接创建、协议推断、连接关闭等会打印出来--protocol-log-level指定协议模块日志级别主要是具体协议解析相关的日志--uprobe-log-level指定 uprobe 模块日志级别主要是和 ssl 探针相关的日志从参数解析源码cmd/root.go可以看到这五个参数的默认值均为0表示不单独覆盖沿用默认级别并且都属于MarkHidden的隐藏参数——它们面向调试场景不会出现在常规的 help 输出中但可以正常使用。推荐的调试组合调试协议解析相关部分建议加上sudo kyanos watch http --bpf-event-log-level 5 --conntrack-log-level 5 --protocol-log-level 5遇到 eBPF 代码加载失败如 BTF 解析问题、内核版本兼容问题加上sudo kyanos --agent-log-level 5此时会打印 Agent 启动阶段的详细日志通常能看到具体的加载失败原因。级别校验逻辑可参考 cmd/common.go 的isValidLogLevel合法的级别范围是1Fatal到5Debug超出范围的值会被忽略。日志默认落盘到 /tmp所有模块日志默认输出到/tmp下的文件。从 common/log.go 的实现可以看到日志会写入/tmp/kyanos.log.YYYYMMDD格式的文件并通过rotatelog钩子每小时轮转一次、最多保留 24 小时。--debug-output把请求直接打印到控制台在watch命令下加上--debug-output选项定义于 cmd/watch.go日志会改为输出到标准输出同时不再展示 TUI 表格抓取到的请求会直接输出到控制台sudo kyanos watch --debug-output输出形如WARN[0023] [ Request ] GET /health HTTP/1.1 Host: :8080 User-Agent: Go-http-client/1.1 Accept-Encoding: gzip [ Response ] HTTP/1.1 200 OK Date: Wed, 01 Jan 2025 16:20:20 GMT Content-Length: 2 Content-Type: text/plain; charsetutf-8 OK [conn] [pid2252][local addr]127.0.0.1:8080 [remote addr]127.0.0.1:38664 [side]server [ssl]false [total duration] 0.423(ms)(start2025-01-02 00:20:20.095, end2025-01-02 00:20:20.095) [read from sockbuf]0.296(ms) [process internal duration]0.078(ms) [syscall] [read count]1 [read bytes]92 [write count]1 [write bytes]118这段输出极具调试价值除了完整的 HTTP 请求/响应报文还会打印连接四元组pid、本地/远端地址、server/client 角色、是否 SSL、总耗时及各阶段耗时read from sockbuf读取 socket 缓冲耗时、process internal duration内部处理耗时以及 syscall 层的读写次数与字节数——这正是 Kyanos 把内核耗时可视化的核心数据形态。[!TIP]调试协议解析相关代码时使用以下组合就基本足够了sudo kyanos watch http --bpf-event-log-level 5 --conntrack-log-level 5 --protocol-log-level 5 --debug-output从源码看--debug-output本质上控制的是 TUI 渲染与日志输出的切换在 agent/agent.go 中若使用 TUI 则启动加载渲染器并调用common.SetLogToStdout()若加载出错如 agent/agent.go 的logSystemInfo也会强制切回标准输出以打印系统信息辅助排障。eBPF 相关用 bpf_printk 调试内核态代码协议推断等逻辑运行在内核态 eBPF 程序中用户态日志帮不上忙此时可以借助内核提供的bpf_printk在 eBPF 代码中打印日志。在仓库中可以看到大量此类调试写法例如 bpf/pktlatency.bpf.c 中打印 socket key 的端口与地址// bpf_printk(print_sock_key port: sport:%u, dport:%u\n, key-sport, key-dport); // bpf_printk(print_sock_key addr: saddr:%llx, saddr:%llx\n, key-sip[0], key-sip[1]); // bpf_printk(print_sock_key addr: daddr:%llx, daddr:%llx\n, key-dip[0], key-dip[1]); // bpf_printk(print_sock_key family: family:%u, key-family);再如 SSL 协议推断相关的 bpf/data_common.h通过bpf_printk(SSL[protocol infer]:start, bc:%d, bytes_count)观察协议推断入口的字节数以及SSL[protocol infer]: %d, func: %d打印推断出的协议号与调用来源函数// bpf_printk(SSL[protocol infer]:start, bc:%d, bytes_count); // bpf_printk(SSL[protocol infer]: %d, func: %d, conn_info-protocol, args-source_fn);bpf_printk的输出会进入内核的 trace_pipe可以通过sudo cat /sys/kernel/debug/tracing/trace_pipe实时查看。注意使用bpf_printk修改代码后必须重新执行make build-bpf让改动生效回到本文第一节的流程。关于bpf_printk的详细使用技巧可参考nakryiko.com/posts/bpf-tips-printk/这篇经典文章仓库文档 docs/cn/debug-tips.md 中亦有引用。IDE 调试相关VS Code 断点调试VS Code 直接打开项目即可然后在.vscode/launch.json中添加如下配置{ version: 0.2.0, configurations: [ { name: Launch file, type: go, request: launch, mode: debug, program: ${workspaceFolder}, args: [watch, --debug-output] } ] }注意务必添加--debug-output参数。原因有两层调试模式下 TUI 渲染涉及终端原始模式、按键捕获、异步刷新会干扰断点暂停与单步执行关闭 TUI 后程序行为更可控抓取到的请求会以文本形式直接输出到控制台即使不查看 TUI 界面也能验证协议解析结果。启动调试后args中的watch会进入 watch 模式--debug-output则保证输出可读。入口方面Kyanos 的用户态程序从 main.go 进入经由cmd.Execute()cmd/root.go分发命令最终在startAgent()cmd/common.go中完成日志初始化并调用agent.SetupAgent(options)启动探针与渲染管线——可以在这些位置设置断点观察启动流程。提示断点调试依赖上一节提到的调试信息二进制。在 IDE 中直接按 F5 时请确保此前已执行过make kyanos-debug或至少make build-bpf否则可能因优化导致变量不可见、断点位置偏移。源码结构按图索骥定位模块Kyanos 的源码采用清晰的模块化组织详见 docs/cn/debug-tips.md 的源码结构一节调试时可按下面的地图快速定位 agent analysis (聚合分析模块stat 命令用到) conn 连接跟踪模块 protocol协议解析模块 renderTUI 渲染模块 uprobeuprobe 相关主要是 ssl 探针 bpf loader (bpf 程序加载逻辑) pktlatency.bpf.c (内核态核心代码包括系统调用和内核部分的事件上报等逻辑) gotls.bpf.c (gotls 探针相关) protocol_inference.h (协议推断相关) openssl* (openssl 相关) cmd (命令行相关) common (一些工具类) docs (基于 vitepress 的文档)结合前面介绍的日志模块可以形成一套症状 → 模块 → 日志开关的快速映射排查方向相关目录对应日志开关Agent 启动失败、eBPF 加载失败agent/agent.go、bpf/loader--agent-log-level 5内核事件、syscall 事件丢失或异常bpf/pktlatency.bpf.c--bpf-event-log-level 5连接创建/关闭、协议推断异常agent/conn、bpf/protocol_inference.h--conntrack-log-level 5具体协议解析错误agent/protocol--protocol-log-level 5SSL 探针openssl/gotls不生效agent/uprobe、bpf/gotls.bpf.c、bpf/openssl*--uprobe-log-level 5小结一套完整的排障工作流把本文的内容串起来就是一套针对 Kyanos 的完整调试工作流改代码修改用户态代码直接改修改 eBPF 内核态代码后先make build-bpf再构建观察用户态行为按问题域开启对应模块的日志协议解析问题用--bpf-event-log-level 5 --conntrack-log-level 5 --protocol-log-level 5启动问题用--agent-log-level 5需要看请求原文时加--debug-output观察内核态行为在 bpf 目录的.bpf.c/.h代码中埋bpf_printk重新make build-bpf后通过 trace_pipe 查看断点调试make kyanos-debug生成未优化二进制在 VS Code 中按上文配置启动调试配合 Delve本地或remote-debug远程 headless 模式逐步跟踪。这套组合拳覆盖了从构建、日志、内核态到用户态的全链路排查手段足以应对 Kyanos 开发与二次定制中的绝大多数疑难问题。【免费下载链接】kyanosKyanos is a networking analysis tool using eBPF. It can visualize the time packets spend in the kernel, capture requests/responses, makes troubleshooting more efficient.项目地址: https://gitcode.com/GitHub_Trending/ky/kyanos创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进