diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index f0896b0..8ebedce 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vitepress' +import { defineConfig } from "vitepress"; // https://vitepress.dev/reference/site-config export default defineConfig({ @@ -6,57 +6,64 @@ export default defineConfig({ description: "Kyanos official website", head: [["link", { rel: "icon", href: "/kyanos.png" }]], // 浏览器标签页logo locales: { - cn: { label: '简体中文', lang: 'cn' }, - root: { label: 'English', lang: 'en' }, + cn: { label: "简体中文", lang: "cn" }, + root: { label: "English", lang: "en" } }, - appearance: 'dark', + appearance: "dark", themeConfig: { // https://vitepress.dev/reference/default-theme-config logo: "/kyanos.png", nav: [ - { text: 'Home', link: '/' }, - { text: 'Guide', link: './what-is-kyanos' } + { text: "Home", link: "/" }, + { text: "Guide", link: "./what-is-kyanos" } ], sidebar: [ { - text: 'Introduction', + text: "Introduction", items: [ - { text: 'What is Kyanos?', link: './what-is-kyanos' }, - { text: 'Quickstart', link: './quickstart' }, - { text: 'FAQ', link: './faq' }, + { text: "What is Kyanos?", link: "./what-is-kyanos" }, + { text: "Quickstart", link: "./quickstart" }, + { text: "FAQ", link: "./faq" } ] }, { - text: 'Tutorial', + text: "Tutorial", items: [ - - { text: 'Learn kyanos in 5 minutes', link: './how-to' }, - { text: 'How to use watch', link: './watch' }, - { text: 'How to use stat', link: './stat' }, + { text: "Learn kyanos in 5 minutes", link: "./how-to" }, + { text: "How to use watch", link: "./watch" }, + { text: "How to use stat", link: "./stat" } ] }, { - text: 'Development', + text: "Development", items: [ - - { text: 'How to build', link: './how-to-build' }, - { text: 'How to add a new protocol', link: './how-to-add-a-new-protocol' }, + { text: "How to build", link: "./how-to-build" }, + { + text: "How to add a new protocol", + link: "./how-to-add-a-new-protocol" + }, + { + text: "Debug Tips", + link: "./debug-tips" + } ] } ], socialLinks: [ - { icon: 'github', link: 'https://github.com/hengyoush/kyanos' } + { icon: "github", link: "https://github.com/hengyoush/kyanos" } ], - + footer: { - message: 'Released under the Apache-2.0 license.', - copyright: 'Copyright © 2024-present Hengyoush' + message: + 'Released under the Apache-2.0 license.', + copyright: + 'Copyright © 2024-present Hengyoush' }, - + search: { - provider: 'local' - }, + provider: "local" + } } -}) +}); diff --git a/docs/.vitepress/theme/index.mts b/docs/.vitepress/theme/index.mts index 50ccb9e..e98b6f2 100644 --- a/docs/.vitepress/theme/index.mts +++ b/docs/.vitepress/theme/index.mts @@ -1,5 +1,5 @@ import Theme from "vitepress/theme"; import "./style/index.css"; export default { - ...Theme, -}; \ No newline at end of file + ...Theme +}; diff --git a/docs/.vitepress/theme/style/index.css b/docs/.vitepress/theme/style/index.css index c9efd04..2164da9 100644 --- a/docs/.vitepress/theme/style/index.css +++ b/docs/.vitepress/theme/style/index.css @@ -1 +1 @@ -@import "./var.css" \ No newline at end of file +@import "./var.css"; diff --git a/docs/.vitepress/theme/style/var.css b/docs/.vitepress/theme/style/var.css index a53dbec..ba0f6f6 100644 --- a/docs/.vitepress/theme/style/var.css +++ b/docs/.vitepress/theme/style/var.css @@ -1,19 +1,19 @@ :root { - /* 标题 */ - --vp-home-hero-name-color: transparent; - /* 渐变色 */ - --vp-home-hero-name-background: linear-gradient( - 135deg, - #76fddd 10%, - #bbe2fe 100% - ); - /* 纯色 */ - /* --vp-home-hero-name-background: red; */ - --vp-home-hero-image-background-image: linear-gradient( - 135deg, - #bbe2fe 10%, - #76fddd 100% - ); - /* 设置模糊度 */ - --vp-home-hero-image-filter: blur(150px); - } \ No newline at end of file + /* 标题 */ + --vp-home-hero-name-color: transparent; + /* 渐变色 */ + --vp-home-hero-name-background: linear-gradient( + 135deg, + #76fddd 10%, + #bbe2fe 100% + ); + /* 纯色 */ + /* --vp-home-hero-name-background: red; */ + --vp-home-hero-image-background-image: linear-gradient( + 135deg, + #bbe2fe 10%, + #76fddd 100% + ); + /* 设置模糊度 */ + --vp-home-hero-image-filter: blur(150px); +} diff --git a/docs/cn/debug-tips.md b/docs/cn/debug-tips.md new file mode 100644 index 0000000..5789a0f --- /dev/null +++ b/docs/cn/debug-tips.md @@ -0,0 +1,77 @@ +# Debug Tips + +## 构建相关 + +注意每次修改eBPF相关代码后都需要使用`make build-bpf`重新生成一些bpf相关代码,然后再用`make`构建或者在IDE里调试。 + +使用 `kyanos-debug` 来构建带有调试信息的二进制文件, 以便更好地进行调试。 + +```sh +make kyanos-debug +``` + +## 日志相关 + +启动kyanos开启日志,日志分为几个模块可分别开启,5为debug级别,默认是warn级别。如下是每个模块的日志选项: + +| 参数 | 含义 | +| --------------------- | ----------------------------------------------------------------------------------------- | +| --agent-log-level | 指定agent模块日志级别,主要是和Agent启动等相关的日志 | +| --bpf-event-log-level | 指定bpf事件日志级别,一些内核上报的和syscall层上报的事件会打印出来 | +| --conntrack-log-level | 指定conntrack模块日志级别,一些连接相关的事件比如连接创建、协议推断、连接关闭等会打印出来 | +| --protocol-log-level | 指定协议模块日志级别,主要是具体协议解析相关的日志 | +| --uprobe-log-level | 指定uprobe模块日志级别,主要是和ssl探针相关的日志 | + +比如如果你在调试协议解析相关部分的代码建议加上:`--bpf-event-log-level 5 --conntrack-log-level 5 --protocol-log-level 5`。 + +如果你碰到eBPF代码加载失败的情况,可以加上`--agent-log-level 5`日志打印一些Agent启动时的日志。 + +日志输出默认到/tmp下,加上 `--debug-output` +选项可以让日志输出到标准输出,而且不再展示tui会展示的表格,抓取到的请求都会直接输出到控制台: + +``` +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; charset=utf-8 + +OK + +[conn] [pid=2252][local addr]=127.0.0.1:8080 [remote addr]=127.0.0.1:38664 [side]=server [ssl]=false +[total duration] = 0.423(ms)(start=2025-01-02 00:20:20.095, end=2025-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 +``` + +> [!TIP] +> +> 调试协议解析相关代码时可以使用:`--bpf-event-log-level 5 --conntrack-log-level 5 --protocol-log-level 5 --debug-output` +> 选项就基本上足够了。 + +## 源码结构 + +``` +> agent + > analysis (聚合分析模块,stat命令用到) + > conn (连接跟踪模块) + > protocol(协议解析模块) + > render(TUI渲染模块) + > uprobe(uprobe相关主要是ssl探针) +> bpf + > loader (bpf 程序加载逻辑) + pktlatency.bpf.go (内核态核心代码,包括系统调用和内核部分的事件上报等逻辑) + gotls.bpf.go (gotls探针相关) + protocol_inference.h (协议推断相关) + openssl* (openssl相关) +> cmd (命令行相关) +> common (一些工具类) +> docs (基于vitepress的文档) +``` diff --git a/docs/cn/how-to-add-a-new-protocol.md b/docs/cn/how-to-add-a-new-protocol.md index d54df54..7ed0325 100644 --- a/docs/cn/how-to-add-a-new-protocol.md +++ b/docs/cn/how-to-add-a-new-protocol.md @@ -85,12 +85,12 @@ type ParsedMessage interface { } ``` -| 方法名 | 作用 | -| ------------------ | ------------------------------------------------------------------------ | -| `FormatToString()` | 将消息格式化为字符串表示形式。 | -| `TimestampNs()` | 返回消息的时间戳(以纳秒为单位)。 | -| `ByteSize()` | 返回消息的字节大小。 | -| `IsReq()` | 判断消息是否为请求。 | +| 方法名 | 作用 | +| ------------------ | -------------------------------------------------------------------------- | +| `FormatToString()` | 将消息格式化为字符串表示形式。 | +| `TimestampNs()` | 返回消息的时间戳(以纳秒为单位)。 | +| `ByteSize()` | 返回消息的字节大小。 | +| `IsReq()` | 判断消息是否为请求。 | | `Seq()` | 返回消息的字节流序列号, 可以从 `streamBuffer.Head().LeftBoundary()` 获取。 | HTTP 的例子: @@ -137,10 +137,11 @@ ParseStream(streamBuffer *buffer.StreamBuffer, messageType MessageType) ParseRes 注意 eBPF 数据事件可能会无序到达,或者丢失事件,因此数据缓冲区中的数据可能缺少数据块,参数 streamBuffer 中通过 `streamBuffer.Head` -函数获取到目前为止已接收到的缓冲区前面的所有连续数据。因此,此时 **无法保证数据有效或缓冲区与数据包的开头对齐**。 +函数获取到目前为止已接收到的缓冲区前面的所有连续数据。因此,此时 +**无法保证数据有效或缓冲区与数据包的开头对齐**。 -如果返回 `ParseResult` 中的 `state` 为 -`success`,且那么 kyanos 会自动删除掉 `ParseResult.ReadBytes` 个字节的数据;如果返回 `invalid`,那么通过 +如果返回 `ParseResult` 中的 `state` 为 `success`,且那么 kyanos 会自动删除掉 +`ParseResult.ReadBytes` 个字节的数据;如果返回 `invalid`,那么通过 `FindBoundary` 找到下一个可能的 `Message` 边界;如果返回 `needsMoreData`,则不会删除数据,而是稍后重试。 @@ -242,7 +243,8 @@ func init() { ## Step.6-添加 e2e 测试 -在 testdata 目录下添加对应协议的 e2e 测试,可以参考其他协议的写法(比如 `test_redis.sh` 等)。 +在 testdata 目录下添加对应协议的 e2e 测试,可以参考其他协议的写法(比如 +`test_redis.sh` 等)。 ## 其他 @@ -252,7 +254,9 @@ func init() { `common.ProtocolParserLog`. 打开 protocol 解析日志:`--protocol-log-level 5` 设置协议解析相关 log 日志为 debug 级别。 -协议解析框架代码在 conntrack.go 的 `addDataToBufferAndTryParse` 函数里。 +协议解析框架代码在 conntrack.go 的 `addDataToBufferAndTryParse` 函数里。 + +更多调试建议参考:[Debug Tips](./debug-tips) ### 协议解析持久化信息 diff --git a/docs/debug-tips.md b/docs/debug-tips.md new file mode 100644 index 0000000..0c00c92 --- /dev/null +++ b/docs/debug-tips.md @@ -0,0 +1,87 @@ +# Debug Tips + +## Build Related + +Note that every time you modify the eBPF-related code, you need to use +`make build-bpf` to regenerate some bpf-related code, and then use `make` to +build or debug in the IDE. + +Use `kyanos-debug` to build binary files with debug information for better +debugging. + +```sh +make kyanos-debug +``` + +## Log Related + +Start kyanos with logging enabled. Logs are divided into several modules that +can be enabled separately. Level 5 is the debug level, and the default is the +warn level. The following are the log options for each module: + +| Parameter | Meaning | +| --------------------- | -------------------------------------------------------------------------------------- | +| --agent-log-level | Specify the log level for the agent module, mainly related to Agent startup logs | +| --bpf-event-log-level | Specify the log level for bpf events, logs related to kernel and syscall layer events | +| --conntrack-log-level | Specify the log level for the conntrack module, logs related to connection events | +| --protocol-log-level | Specify the log level for the protocol module, mainly related to protocol parsing logs | +| --uprobe-log-level | Specify the log level for the uprobe module, mainly related to ssl probe logs | + +For example, if you are debugging the protocol parsing part of the code, it is +recommended to add: +`--bpf-event-log-level 5 --conntrack-log-level 5 --protocol-log-level 5`. + +If you encounter a situation where the eBPF code fails to load, you can add +`--agent-log-level 5` to print some logs during Agent startup. + +Logs are output to /tmp by default. Adding the `--debug-output` option allows +logs to be output to standard output, and the tables displayed by the TUI will +not be shown. All captured requests will be directly output to the console: + +``` +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; charset=utf-8 + +OK + +[conn] [pid=2252][local addr]=127.0.0.1:8080 [remote addr]=127.0.0.1:38664 [side]=server [ssl]=false +[total duration] = 0.423(ms)(start=2025-01-02 00:20:20.095, end=2025-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 +``` + +> [!TIP] +> +> When debugging protocol parsing related code, you can use: +> `--bpf-event-log-level 5 --conntrack-log-level 5 --protocol-log-level 5 --debug-output` +> This option is generally sufficient. + +## Source Code Structure + +``` +> agent + > analysis (aggregation analysis module, used by the stat command) + > conn (connection tracking module) + > protocol (protocol parsing module) + > render (TUI rendering module) + > uprobe (uprobe related, mainly ssl probe) +> bpf + > loader (bpf program loading logic) + pktlatency.bpf.go (kernel core code, including syscall and kernel event reporting logic) + gotls.bpf.go (gotls probe related) + protocol_inference.h (protocol inference related) + openssl* (openssl related) +> cmd (command line related) +> common (some utility classes) +> docs (documentation based on vitepress) +``` diff --git a/docs/how-to-add-a-new-protocol.md b/docs/how-to-add-a-new-protocol.md index 085b333..df60d42 100644 --- a/docs/how-to-add-a-new-protocol.md +++ b/docs/how-to-add-a-new-protocol.md @@ -300,6 +300,8 @@ protocol parsing-related log levels to debug. The protocol parsing framework code is in the `addDataToBufferAndTryParse` function in conntrack.go. +More debug tips are here:[Debug Tips](./debug-tips) + ### Persisting Protocol Parsing Information In some protocols, if you need to retain some data during the parsing process