Bladeren bron

Merge branch 'release-euspace-1.7.0' into dev

Carl 6 maanden geleden
bovenliggende
commit
bbab1dbfa3

+ 464 - 0
GO_1.24_SWISS_TABLES_ADAPTATION.md

@@ -0,0 +1,464 @@
+# Go 1.24 Swiss Tables 适配文档
+
+## 为什么有两个 Swiss Tables 函数?
+
+在实现过程中,我们创建了两个函数来处理不同的追踪格式:
+
+### 1. `inject_header_swiss` - W3C Traceparent 格式
+
+**用途**:注入 W3C 标准的 traceparent header
+
+**关键特征**:
+- **参数类型**:`struct span_context*` (W3C 格式的 span context)
+- **Header Key**:`"traceparent"` (W3C 标准)
+- **Header Value 长度**:`W3C_VAL_LENGTH` (通常比 CW 格式短)
+- **字符串转换**:使用 `span_context_to_w3c_string()` 函数
+- **内存分配**:使用 `write_target_data()` (通用内存分配)
+
+**调用路径**:
+```
+inject_header() 
+  → inject_header_swiss()  (Go 1.24+)
+```
+
+### 2. `cw_inject_header_swiss` - CW (Coroot) 内部格式
+
+**用途**:注入项目内部的 CW trace header
+
+**关键特征**:
+- **参数类型**:`struct apm_span_context*` (CW/APM 格式的 span context)
+- **Header Key**:`"cwtrace"` (项目内部格式)
+- **Header Value 长度**:`CW_HEADER_VAL_LENGTH` (123 bytes,包含更多信息)
+- **字符串转换**:使用 `span_context_to_cw_string()` 函数
+- **内存分配**:使用 `cw_write_target_data()` (带 proc_info 的进程隔离分配)
+
+**调用路径**:
+```
+cw_inject_header() 
+  → cw_inject_header_swiss()  (Go 1.24+)
+cw_inject_header2() 
+  → cw_inject_header_swiss()  (Go 1.24+)
+```
+
+### 为什么需要两个函数?
+
+1. **不同的追踪标准**:
+   - W3C Traceparent:行业标准,用于跨系统追踪
+   - CW Trace:项目内部格式,可能包含更多元数据
+
+2. **不同的数据结构**:
+   - `span_context` vs `apm_span_context` - 不同的字段布局
+   - 需要不同的序列化函数
+
+3. **不同的内存分配策略**:
+   - `write_target_data()` - 通用分配
+   - `cw_write_target_data()` - 进程隔离分配(带 proc_info)
+
+### 代码对比
+
+```c
+// W3C 格式
+inject_header_swiss():
+  - key: "traceparent" (W3C_KEY_LENGTH)
+  - value: span_context_to_w3c_string()
+  - alloc: write_target_data()
+
+// CW 格式  
+cw_inject_header_swiss():
+  - key: "cwtrace" (CW_HEADER_KEY_LENGTH)
+  - value: span_context_to_cw_string()
+  - alloc: cw_write_target_data(..., proc_info)
+```
+
+### 总结
+
+两个函数实现了相同的 Swiss Tables 注入逻辑,但:
+- 处理不同的追踪格式(W3C vs CW)
+- 使用不同的数据结构(span_context vs apm_span_context)
+- 使用不同的内存分配方法
+
+这是为了保持向后兼容性,同时支持两种追踪格式。
+
+---
+
+# Go 1.24 Swiss Tables 适配文档
+
+## 问题背景
+
+Go 1.24 引入了新的 map 实现(Swiss Tables),替代了传统的 bucket-based hash table。这导致 `euspace` 项目在监控 Go 1.24 应用时出现不兼容问题。
+
+### 问题发现
+
+在测试过程中发现,Go 1.24 应用的监控功能无法正常工作,具体表现为:
+
+```
+2025-11-04 10:13:11.68|ERROR|3162137|tls.go:291] [AttachGoTlsUprobes] STEP 12.2: Failed to get buckets offset, pid=3152597, version=go1.24.0
+```
+
+**根本原因**:Go 1.24 使用 Swiss Tables 实现,`runtime.hmap` 结构体不再存在 `buckets` 字段。
+
+## 解决方案
+
+### 1. 源码分析
+
+通过查看 Go 1.24 源码(`/root/code/go`),我们发现:
+
+#### 旧实现(Go < 1.24)
+- 使用 `runtime.hmap` 结构体
+- 包含 `buckets unsafe.Pointer` 字段(指向 bucket 数组)
+- 结构体位置:`src/runtime/map_noswiss.go`
+
+```go
+type hmap struct {
+    count     int
+    flags     uint8
+    B         uint8
+    noverflow uint16
+    hash0     uint32
+    buckets   unsafe.Pointer  // ← 旧的字段
+    oldbuckets unsafe.Pointer
+    // ...
+}
+```
+
+#### 新实现(Go 1.24+)
+- 使用 `internal/runtime/maps.Map` 结构体
+- 包含 `dirPtr unsafe.Pointer` 字段(替代 `buckets`)
+- 结构体位置:`src/internal/runtime/maps/map.go`
+
+```go
+type Map struct {
+    used      uint64      // offset 0
+    seed      uintptr     // offset 8
+    dirPtr    unsafe.Pointer  // offset 16 ← 新的字段
+    dirLen    int         // offset 24
+    globalDepth uint8
+    globalShift uint8
+    writing   uint8
+    clearSeq  uint64
+}
+```
+
+### 2. 数据结构差异
+
+#### 小 Map(dirLen == 0)
+- `dirPtr` 直接指向一个 `group`
+- `group` 结构:
+  ```
+  +------------------+
+  | ctrlGroup (8B)   |  ← 控制字,每个字节表示一个 slot 的状态
+  +------------------+
+  | slot[0] (40B)    |  ← key (16B) + elem (24B)
+  | slot[1] (40B)    |
+  | ...              |
+  | slot[7] (40B)    |
+  +------------------+
+  ```
+
+#### 大 Map(dirLen > 0)
+- `dirPtr` 指向 directory(table 指针数组)
+- 需要访问 directory → table → group → slot
+
+### 3. 实现适配
+
+#### 3.1 获取偏移量(Go 层)
+
+**文件**:`ebpftracer/tls.go`
+
+**修改点**:
+1. 尝试获取 `runtime.hmap.buckets`(向后兼容)
+2. 如果失败,尝试获取 `internal/runtime/maps.Map.dirPtr`
+3. 如果 DWARF 查找失败,使用硬编码偏移量 16(64-bit 系统)
+
+```go
+// 尝试获取 buckets 字段
+bucketsOff, ok2 := tracer.GetOffset(tracer.NewID("std", "runtime", "hmap", "buckets"), path)
+
+if !ok2 {
+    // Go 1.24+ Swiss Tables
+    // 尝试获取 maps.Map.dirPtr
+    swissFields := []struct {
+        pkg        string
+        structName string
+        field      string
+    }{
+        {"internal/runtime/maps", "Map", "dirPtr"},
+        {"internal.runtime.maps", "Map", "dirPtr"},
+        {"maps", "Map", "dirPtr"},
+    }
+    
+    for _, sf := range swissFields {
+        swissOff, swissOk := tracer.GetOffset(tracer.NewID("std", sf.pkg, sf.structName, sf.field), path)
+        if swissOk {
+            bucketsOff = swissOff
+            ok2 = true
+            break
+        }
+    }
+    
+    // 如果还是失败,使用硬编码(基于源码分析)
+    if !ok2 && major >= 1 && minor >= 24 {
+        bucketsOff = 16  // used(8) + seed(8) + dirPtr(16)
+        ok2 = true
+    }
+}
+```
+
+**结果**:成功获取到 `dirPtr` 偏移量(16),并正确设置到 `proc_info->buckets_ptr_pos`。
+
+#### 3.2 Header 注入实现(eBPF 层)
+
+**文件**:`ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c`
+
+**新增函数**:
+1. `inject_header_swiss()` - W3C traceparent 格式
+2. `cw_inject_header_swiss()` - CW 格式
+
+**实现逻辑**:
+
+```c
+static __always_inline long inject_header_swiss(...) {
+    // 1. 读取 used 计数(maps.Map 的第一个字段)
+    u64 used = 0;
+    bpf_probe_read_user(&used, sizeof(used), headers_ptr);
+    
+    // 2. 检查 map 是否已满
+    if (used >= 8) return -1;
+    
+    // 3. 读取 dirLen(offset 24)判断 map 类型
+    s32 dirLen = 0;
+    bpf_probe_read_user(&dirLen, sizeof(dirLen), headers_ptr + 24);
+    
+    // 4. 如果是小 map(dirLen == 0),读取 dirPtr
+    void *group_ptr = NULL;
+    if (dirLen == 0) {
+        bpf_probe_read_user(&group_ptr, sizeof(group_ptr), 
+                           headers_ptr + buckets_ptr_pos);
+    } else {
+        // 大 map 暂不支持
+        return -1;
+    }
+    
+    // 5. 读取 control word(group 的前 8 字节)
+    u64 ctrls = 0;
+    bpf_probe_read_user(&ctrls, sizeof(ctrls), group_ptr);
+    
+    // 6. 查找第一个空 slot(ctrl byte == 0x80)
+    u8 slot_idx = 0;
+    for (u8 i = 0; i < 8; i++) {
+        u8 ctrl_byte = (ctrls >> (i * 8)) & 0xFF;
+        if (ctrl_byte == 0x80) {
+            slot_idx = i;
+            break;
+        }
+    }
+    
+    // 7. 计算 slot 偏移量
+    // group 布局:ctrlGroup(8) + slots[8]
+    // 每个 slot:key(16) + elem(24) = 40 bytes
+    u64 slot_offset = 8 + (slot_idx * 40);
+    void *slot_ptr = group_ptr + slot_offset;
+    
+    // 8. 写入 key(go_string_ot)
+    char key[W3C_KEY_LENGTH] = "traceparent";
+    void *key_str_ptr = write_target_data(key, W3C_KEY_LENGTH);
+    struct go_string_ot key_str = {.str = key_str_ptr, .len = W3C_KEY_LENGTH};
+    bpf_probe_write_user(slot_ptr, &key_str, sizeof(key_str));
+    
+    // 9. 写入 value(go_slice_ot)
+    char val[W3C_VAL_LENGTH];
+    span_context_to_w3c_string(propagated_ctx, val);
+    // ... 创建 go_string_ot 和 go_slice_ot
+    void *elem_ptr = slot_ptr + 16;  // 在 key 之后
+    bpf_probe_write_user(elem_ptr, &val_slice, sizeof(val_slice));
+    
+    // 10. 更新 control byte
+    u8 new_ctrl = 0x5A;  // 占位符,应该是 H2 hash
+    u64 ctrl_mask = 0xFFULL << (slot_idx * 8);
+    ctrls = (ctrls & ~ctrl_mask) | (((u64)new_ctrl) << (slot_idx * 8));
+    bpf_probe_write_user(group_ptr, &ctrls, sizeof(ctrls));
+    
+    // 11. 更新 used 计数
+    used += 1;
+    bpf_probe_write_user(headers_ptr, &used, sizeof(used));
+    
+    return 0;
+}
+```
+
+#### 3.3 版本路由
+
+**修改点**:所有 header 注入函数都添加了版本检测
+
+```c
+// 在 inject_header(), cw_inject_header(), cw_inject_header2() 中
+if (proc_info->version >= GO_VERSION(1, 24, 0)) {
+    return inject_header_swiss(...);  // 或 cw_inject_header_swiss(...)
+}
+// 否则使用旧的实现
+```
+
+#### 3.4 调试日志
+
+所有关键步骤都添加了 `bpf_printk` 日志:
+
+- 函数入口:`START`
+- 读取的值:`used`, `dirLen`, `group_ptr`, `ctrls`
+- 查找结果:`Found empty slot at index X`
+- 计算的值:`slot_idx`, `slot_offset`, `slot_ptr`
+- 内存分配:`key_str_ptr`, `val_str_ptr` 等
+- 写入操作:每个步骤的成功/失败状态
+- 更新操作:control word 和 used count 的更新
+- 函数完成:`SUCCESS`
+
+## 技术细节
+
+### Control Byte 格式
+
+Swiss Tables 使用 control byte 标记每个 slot 的状态:
+
+- `0x80` (0b10000000) = 空 slot
+- `0xFE` (0b11111110) = 已删除(tombstone)
+- `0x00-0x7F` = 已使用,低 7 位是 H2 hash
+
+### Slot 布局(map[string][]string)
+
+```
+slot[i] = {
+    key:  go_string_ot {  // offset 0
+        str: *char,        // 8 bytes
+        len: int64         // 8 bytes
+    },
+    elem: go_slice_ot {  // offset 16
+        array: *go_string_ot,  // 8 bytes
+        len:   int64,          // 8 bytes
+        cap:   int64           // 8 bytes
+    }
+}
+// 总大小:40 bytes(可能需要对齐到 48 bytes)
+```
+
+### 内存对齐
+
+在 64-bit 系统上:
+- `uint64`、`uintptr`、指针:8 字节对齐
+- `maps.Map` 结构体:字段按顺序排列,无 padding
+  - `used` (0): 8 bytes
+  - `seed` (8): 8 bytes
+  - `dirPtr` (16): 8 bytes
+  - `dirLen` (24): 4 bytes (int)
+  - ...
+
+## 文件修改清单
+
+### Go 层修改
+
+1. **ebpftracer/tls.go**
+   - 添加 `maps.Map.dirPtr` 字段查找逻辑
+   - 添加硬编码 fallback(Go 1.24+)
+   - 允许在 `buckets` 失败时继续(只要 `goid` 成功)
+
+### eBPF 层修改
+
+1. **ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c**
+   - 新增 `inject_header_swiss()` 函数
+   - 新增 `cw_inject_header_swiss()` 函数
+   - 修改 `inject_header()` - 添加版本路由
+   - 修改 `cw_inject_header()` - 添加版本路由
+   - 修改 `cw_inject_header2()` - 添加版本路由
+   - 修改 `cw_inject_header_half()` - 添加版本检查(暂不支持)
+
+2. **ebpftracer/ebpf/utrace/go/net/server.probe.bpf.c**
+   - 修改所有读取 `buckets_ptr_pos` 的地方,添加版本检查
+
+## 已知限制
+
+1. **仅支持小 Map**:当前实现只支持 `dirLen == 0` 的情况(小 map,≤8 个元素)
+   - HTTP headers 通常是空或很小的 map,所以这个限制可以接受
+   - 如果需要支持大 map,需要实现 directory/table 访问逻辑
+
+2. **Slot 大小假设**:当前使用 40 字节(string 16 + []string 24)
+   - 实际可能需要对齐到 48 字节
+   - 需要根据测试结果调整
+
+3. **H2 Hash**:当前使用固定值 `0x5A` 作为 control byte
+   - 正确实现应该计算 key 的 hash,并使用 H2 位(低 7 位)
+   - 对于 HTTP headers 注入,这个占位符可能足够
+
+4. **并发安全**:Swiss Tables 有 `writing` 标志用于检测并发写入
+   - 当前实现没有检查这个标志
+   - 可能需要添加检查以确保安全
+
+## 测试验证
+
+### 成功指标
+
+1. ✅ 成功获取 `maps.Map.dirPtr` 偏移量(16)
+2. ✅ 代码编译通过
+3. ⏳ 运行时 header 注入成功(需要实际测试)
+4. ⏳ 注入的 header 能被正确读取(需要实际测试)
+
+### 调试方法
+
+查看 eBPF 日志:
+```bash
+# 方法 1:使用 bpftool
+sudo bpftool prog tracelog
+
+# 方法 2:使用 trace_pipe
+sudo cat /sys/kernel/debug/tracing/trace_pipe | grep inject_header_swiss
+```
+
+关键日志点:
+- `inject_header_swiss: START` - 函数开始
+- `inject_header_swiss: used=X` - map 当前元素数
+- `inject_header_swiss: dirLen=X` - map 类型
+- `inject_header_swiss: group_ptr=0x...` - group 指针
+- `inject_header_swiss: Found empty slot at index X` - 找到的空 slot
+- `inject_header_swiss: SUCCESS` - 注入成功
+
+## 参考资源
+
+1. **Go 1.24 源码**:`/root/code/go/src/internal/runtime/maps/`
+   - `map.go` - Map 结构体定义
+   - `group.go` - Group 结构体定义
+   - `table.go` - Table 结构体定义
+
+2. **相关文档**:
+   - [Abseil Swiss Tables](https://abseil.io/about/design/swisstables) - Swiss Tables 设计原理
+   - Go 1.24 release notes - map 实现变更说明
+
+## 后续优化建议
+
+1. **实现正确的 H2 hash 计算**
+   - 使用 Go 的 hash 算法计算 key 的 hash
+   - 提取低 7 位作为 H2 值
+
+2. **支持大 Map**
+   - 实现 directory 访问逻辑
+   - 处理 table 查找和 group 访问
+
+3. **优化 Slot 大小检测**
+   - 通过 DWARF 信息获取实际的 slot 大小
+   - 或通过运行时探测确定
+
+4. **添加并发安全检查**
+   - 检查 `writing` 标志
+   - 处理并发写入冲突
+
+5. **性能优化**
+   - 减少不必要的内存读取
+   - 优化 control word 操作
+
+## 总结
+
+通过分析 Go 1.24 源码,理解了 Swiss Tables 的数据结构,实现了适配的 header 注入逻辑。主要工作包括:
+
+1. ✅ 识别问题:`runtime.hmap.buckets` 字段不存在
+2. ✅ 源码分析:理解 Swiss Tables 结构
+3. ✅ 实现适配:新的 header 注入函数
+4. ✅ 版本路由:自动选择正确的实现
+5. ✅ 调试支持:详细的日志输出
+
+代码已实现并可以编译,下一步需要实际测试验证功能是否正常工作。
+

+ 5 - 4
containers/container.go

@@ -103,10 +103,11 @@ type ActiveConnection struct {
 	FirstWriteTime uint64
 	NewReadTime    uint64
 
-	http2Parser    *l7.Http2Parser
-	postgresParser *l7.PostgresParser
-	mysqlParser    *l7.MysqlParser
-	dmParser       *l7.DmParser
+	http2Parser      *l7.Http2Parser
+	postgresParser   *l7.PostgresParser
+	mysqlParser      *l7.MysqlParser
+	dmParser         *l7.DmParser
+	cassandraParser  *l7.CassandraParser
 }
 
 type ActiveAccept struct {

+ 127 - 40
containers/container_apm.go

@@ -164,14 +164,15 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			}
 			if err == nil {
 				if r.TraceType == 0 {
-					method, requestURI, sn, sport := l7.ParseHttpHost(r.Payload, r.IsTls)
+					method, requestURI, sn, sport, userAgent := l7.ParseHttpHostWithUserAgent(r.Payload, r.IsTls)
+					// userAgent 可以在这里使用,例如传递给 trace.TraceStartEvent
 					ip, _ := netaddr.ParseIP(sn)
 					//codeType := c.GetCodeTypeFromCache(pid)
 					container_id := ""
 					if c.cgroup != nil {
 						container_id = c.cgroup.ContainerId
 					}
-					trace.TraceStartEvent(method, requestURI, sn, sport, r.Status, netaddr.IPPortFrom(ip, sport), pid, c.GetAppInfo(), container_id)
+					trace.TraceStartEvent(method, requestURI, sn, userAgent, sport, r.Status, netaddr.IPPortFrom(ip, sport), pid, c.GetAppInfo(), container_id)
 					c.SendEvent(trace, r.TraceId)
 				} else if r.TraceType == 1 {
 					ipAddr, port, containerID := c.getGrpcServerNetworkInfo()
@@ -198,6 +199,10 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			return nil
 		}
 	}
+
+	/**
+	 * HTTP
+	 */
 	if r.Protocol == l7.ProtocolHTTP {
 		if c.l7Attach && c.valuableTrace(r.TraceId) {
 			method, requestURI, sn, sport := l7.ParseHttpHost(r.Payload, r.IsTls)
@@ -210,46 +215,61 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 		}
 		//return nil
 	}
+
+	/**
+	 * gRPC
+	 */
+	if r.Protocol == l7.ProtocolGrpc {
+		klog.Infoln("conn == nil r.Protocol == l7.ProtocolGrpc")
+		klog.Infoln("enter the l7.ProtocolGrpc")
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				apmTrace.GrpcClientTraceQueryEvent(r)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
+		}
+	}
 	conn := c.connectionsByPidFd[PidFd{Pid: pid, Fd: fd}]
 	//fmt.Println("l7.connectionsByPidFd", conn, pid, fd)
 
 	if conn == nil {
-		if r.Protocol == l7.ProtocolGrpc {
-			klog.Infoln("conn == nil r.Protocol == l7.ProtocolGrpc")
-			klog.Infoln("enter the l7.ProtocolGrpc")
-			if c.l7Attach && c.valuableTrace(r.TraceId) {
-				apmTrace, err := c.getOrInitTrace(r.TraceId)
-				if err == nil {
-					apmTrace.GrpcClientTraceQueryEvent(r)
-					c.SendEvent(apmTrace, r.TraceId)
-				}
-			}
+		conn = &ActiveConnection{
+			Dest:       r.ComponentDAddr,
+			ActualDest: r.ComponentDAddr,
+			Timestamp:  timestamp,
 		}
-		return nil
+		//return nil
 	}
 	if timestamp != 0 && conn.Timestamp != timestamp {
-		if r.Protocol == l7.ProtocolGrpc {
-			klog.Infoln("timestamp != 0 && conn.Timestamp != timestamp r.Protocol == l7.ProtocolGrpc")
-			klog.Infoln("enter the l7.ProtocolGrpc")
-			if c.l7Attach && c.valuableTrace(r.TraceId) {
-				apmTrace, err := c.getOrInitTrace(r.TraceId)
-				if err == nil {
-					apmTrace.GrpcClientTraceQueryEvent(r)
-					c.SendEvent(apmTrace, r.TraceId)
-				}
-			}
-		}
+		//if r.Protocol == l7.ProtocolGrpc {
+		//	klog.Infoln("timestamp != 0 && conn.Timestamp != timestamp r.Protocol == l7.ProtocolGrpc")
+		//	klog.Infoln("enter the l7.ProtocolGrpc")
+		//	if c.l7Attach && c.valuableTrace(r.TraceId) {
+		//		apmTrace, err := c.getOrInitTrace(r.TraceId)
+		//		if err == nil {
+		//			apmTrace.GrpcClientTraceQueryEvent(r)
+		//			c.SendEvent(apmTrace, r.TraceId)
+		//		}
+		//	}
+		//}
 		return nil
 	}
 	stats := c.l7Stats.get(r.Protocol, conn.Dest, conn.ActualDest)
 	//trace := tracing.NewTrace(string(c.id), conn.ActualDest)
 	switch r.Protocol {
+	/**
+	 * HTTP
+	 */
 	case l7.ProtocolHTTP:
 		if c.AppInfo.AppName != "" {
 			klog.Debugf("[%s] ->>>>> curl -> %s payload:[%s]", c.AppInfo.AppName, conn.ActualDest, r.Payload)
 		}
 
 		stats.observe(r.Status.Http(), "", r.Duration)
+	/**
+	 * HTTP2
+	 */
 	case l7.ProtocolHTTP2:
 		if conn.http2Parser == nil {
 			conn.http2Parser = l7.NewHttp2Parser()
@@ -259,6 +279,9 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			stats.observe(req.Status.Http(), "", req.Duration)
 			//trace.Http2Request(req.Method, req.Path, req.Scheme, req.Status, req.Duration)
 		}
+	/**
+	 * PostgreSQL
+	 */
 	case l7.ProtocolPostgres:
 		if r.Method != l7.MethodStatementClose {
 			stats.observe(r.Status.String(), "", r.Duration)
@@ -275,7 +298,7 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			query := conn.postgresParser.Parse(r.Payload)
 			//trace.MysqlQuery(query, r.Status.Error(), r.Duration)
 			if c.AppInfo.AppName != "" {
-				klog.Debugf("[%s] ->>>>> Postgres -> %s payload:[%s]", c.AppInfo.AppName, conn.ActualDest, query)
+				klog.Debugf("[%s] ->>>>> %s -> %s payload:[%s]", c.AppInfo.AppName, r.Protocol.String(), conn.ActualDest, query)
 			}
 			//apmTrace, ok := c.getTrace(r.TraceId)
 			apmTrace, err := c.getOrInitTrace(r.TraceId)
@@ -285,10 +308,13 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			if err == nil {
 				//apmTrace.MysqlTraceQuery(query, r.Status.Error(), r.Duration, conn.ActualDest)
 				//apmTrace.PostGreSqlTraceQueryEvent(query, r, conn.ActualDest)
-				apmTrace.SQLTraceQueryEvent(l7.ProtocolPostgres, semconv.DBSystemPostgreSQL, query, r, conn.ActualDest)
+				apmTrace.SQLTraceQueryEvent(r.Protocol, semconv.DBSystemPostgreSQL, query, r, conn.ActualDest)
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
+	/**
+	 * Mysql
+	 */
 	case l7.ProtocolMysql:
 		if r.Method != l7.MethodStatementClose {
 			stats.observe(r.Status.String(), "", r.Duration)
@@ -300,7 +326,7 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			query := conn.mysqlParser.Parse(r.Payload, r.StatementId)
 			//trace.MysqlQuery(query, r.Status.Error(), r.Duration)
 			if c.AppInfo.AppName != "" {
-				klog.Debugf("[%s] ->>>>> Mysql -> %s payload:[%s]", c.AppInfo.AppName, conn.ActualDest, query)
+				klog.Debugf("[%s] ->>>>> %s -> %s payload:[%s]", c.AppInfo.AppName, r.Protocol.String(), conn.ActualDest, query)
 			}
 			//apmTrace, ok := c.getTrace(r.TraceId)
 			apmTrace, err := c.getOrInitTrace(r.TraceId)
@@ -320,7 +346,9 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
-
+	/**
+	 * DM (达梦数据库)
+	 */
 	case l7.ProtocolDM:
 		//统计dm的query次数
 		stats.observe(r.Status.String(), "", r.Duration)
@@ -331,24 +359,42 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			}
 			query := conn.dmParser.Parse(r.Payload, r.StatementId)
 			if c.AppInfo.AppName != "" {
-				klog.Debugf("[%s] ->>>>> Mysql -> %s DMSQL:[%s]", c.AppInfo.AppName, conn.ActualDest, query)
+				klog.Debugf("[%s] ->>>>> %s -> %s payload:[%s]", c.AppInfo.AppName, r.Protocol.String(), conn.ActualDest, query)
 			}
 
 			apmTrace, err := c.getOrInitTrace(r.TraceId)
 			if err == nil {
 				//apmTrace.DmTraceQueryEvent(query, r, conn.ActualDest)
-				apmTrace.SQLTraceQueryEvent(l7.ProtocolDM, semconv.DBSystemDaMengDB, query, r, conn.ActualDest)
+				apmTrace.SQLTraceQueryEvent(r.Protocol, semconv.DBSystemDaMengDB, query, r, conn.ActualDest)
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
+	/**
+	 * Memcached
+	 */
 	case l7.ProtocolMemcached:
 		stats.observe(r.Status.String(), "", r.Duration)
 		if c.l7Attach && c.valuableTrace(r.TraceId) {
-
+			cmd, items := l7.ParseMemcached(r.Payload)
+			if c.AppInfo.AppName != "" {
+				klog.Debugf("[%s] ->>>>> %s -> %s payload:[%s]", c.AppInfo.AppName, r.Protocol.String(), conn.ActualDest, cmd+" "+strings.Join(items, " "))
+			}
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				statement := cmd
+				if len(items) == 1 {
+					statement += " " + items[0]
+				} else if len(items) > 1 {
+					joined := fmt.Sprintf("[%s]", strings.Join(items, " "))
+					statement += " " + joined
+				}
+				apmTrace.NoSQLTraceQueryEvent(r.Protocol, semconv.DBSystemMemcached, cmd, statement, r, conn.Src, conn.ActualDest)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
 		}
-
-		//cmd, items := l7.ParseMemcached(r.Payload)
-		//trace.MemcachedQuery(cmd, items, r.Status.Error(), r.Duration)
+	/**
+	 * Redis
+	 */
 	case l7.ProtocolRedis:
 		stats.observe(r.Status.String(), "", r.Duration)
 		if c.l7Attach && c.valuableTrace(r.TraceId) {
@@ -357,17 +403,23 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			//fmt.Println("args", args)
 			//apmTrace, ok := c.getTrace(r.TraceId)
 			if c.AppInfo.AppName != "" {
-				klog.Debugf("[%s] ->>>>> Redis -> %s DMSQL:[%s]", c.AppInfo.AppName, conn.ActualDest, cmd)
+				klog.Debugf("[%s] ->>>>> %s -> %s payload:[%s]", c.AppInfo.AppName, r.Protocol.String(), conn.ActualDest, cmd)
 			}
 
 			apmTrace, err := c.getOrInitTrace(r.TraceId)
 			if err == nil {
-				//apmTrace.RedisTraceQuery(cmd, args, r.Status.Error(), r.Duration)
-				apmTrace.RedisTraceQueryEvent(cmd, args, r, conn.ActualDest)
+				statement := cmd
+				if args != "" {
+					statement += " " + args
+				}
+				apmTrace.NoSQLTraceQueryEvent(r.Protocol, semconv.DBSystemRedis, cmd, statement, r, conn.Src, conn.ActualDest)
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
 		//trace.RedisQuery(cmd, args, r.Status.Error(), r.Duration)
+	/**
+	 * gRPC
+	 */
 	case l7.ProtocolGrpc:
 		klog.Debugln("enter the l7.ProtocolGrpc")
 		stats.observe(r.Status.String(), "", r.Duration)
@@ -378,6 +430,9 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
+	/**
+	 * MongoDB
+	 */
 	case l7.ProtocolMongo:
 		stats.observe(r.Status.String(), "", r.Duration)
 		if c.l7Attach && c.valuableTrace(r.TraceId) {
@@ -388,19 +443,51 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 
 			apmTrace, err := c.getOrInitTrace(r.TraceId)
 			if err == nil {
-				//apmTrace.RedisTraceQuery(cmd, args, r.Status.Error(), r.Duration)
-				apmTrace.MongoTraceQueryEvent(query, r, conn.ActualDest)
+				// MongoDB query 格式通常是 JSON,如 {"insert":"users"} 或 {"find":"users","filter":{...}}
+				apmTrace.NoSQLTraceQueryEvent(r.Protocol, semconv.DBSystemMongoDB, "", query, r, conn.Src, conn.ActualDest)
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
-	case l7.ProtocolKafka, l7.ProtocolCassandra:
+	/**
+	 * Cassandra
+	 */
+	case l7.ProtocolCassandra:
+		stats.observe(r.Status.String(), "", r.Duration)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			if conn.cassandraParser == nil {
+				conn.cassandraParser = l7.NewCassandraParser()
+			}
+			var query string
+			query = string(r.Payload)
+			//query := conn.cassandraParser.Parse(r.Payload)
+			if c.AppInfo.AppName != "" {
+				klog.Debugf("[%s] ->>>>> %s -> %s payload:[%s]", c.AppInfo.AppName, r.Protocol.String(), conn.ActualDest, query)
+			}
+
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				apmTrace.NoSQLTraceQueryEvent(r.Protocol, semconv.DBSystemCassandra, "", query, r, conn.Src, conn.ActualDest)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
+		}
+
+	/**
+	 * Kafka
+	 */
+	case l7.ProtocolKafka:
 		stats.observe(r.Status.String(), "", r.Duration)
 		if c.l7Attach && c.valuableTrace(r.TraceId) {
 		}
+	/**
+	 * RabbitMQ / NATS
+	 */
 	case l7.ProtocolRabbitmq, l7.ProtocolNats:
 		stats.observe(r.Status.String(), r.Method.String(), 0)
 		if c.l7Attach && c.valuableTrace(r.TraceId) {
 		}
+	/**
+	 * Dubbo2
+	 */
 	case l7.ProtocolDubbo2:
 		stats.observe(r.Status.String(), "", r.Duration)
 		if c.l7Attach && c.valuableTrace(r.TraceId) {

+ 3 - 2
containers/process.go

@@ -4,13 +4,14 @@ import (
 	"bytes"
 	"context"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/jattach"
-	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"os"
 	"path/filepath"
 	"strings"
 	"time"
 
+	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/jattach"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+
 	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/proc"

+ 2 - 0
containers/registry.go

@@ -521,6 +521,8 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 			case ebpftracer.EventTypeL7Request:
 
 				klog.Debugln("e.L7Request Payload:", string(e.L7Request.Payload))
+				klog.Debugln("e.L7Request Payload:", e.L7Request.Protocol)
+				klog.Debugln("e.L7Request Payload:", e.L7Request.TraceId)
 				if e.L7Request == nil {
 					continue
 				}

+ 3 - 0
ebpftracer/ebpf/ebpf.c

@@ -51,15 +51,18 @@
 #include "tcp/state.c"
 #include "tcp/retransmit.c"
 //#include "l7/uprobe_base_bpf.c"
+#include "l7/apm_trace.c"
 #include "l7/l7.c"
 #include "l7/gotls.c"
 //#include "l7/openssl.c"
+//#include "utrace/go/base.probe.bpf.c"
 #include "utrace/go/net/server.probe.bpf.c"
 #include "utrace/go/net/client.probe.bpf.c"
 #include "utrace/go/net/stack.probe.bpf.c"
 #include "utrace/go/net/jvmstack.probe.bpf.c"
 #include "utrace/go/net/grpc.server.probe.bpf.c"
 #include "utrace/go/net/grpc.client.probe.bpf.c"
+#include "utrace/go/db/gocql.probe.bpf.c"
 
 #include "utrace/java/net/server.probe.bpf.c"
 #include "utrace/java/net/client.probe.bpf.c"

+ 18 - 0
ebpftracer/ebpf/include/apm_trace.h

@@ -60,6 +60,10 @@ struct goid_trace_key_t {
 	__u64 goid;
 };
 
+struct fd_trace_poll_times_t {
+	__u32 start_count;
+	__u32 end_count;
+};
 
 //struct apm_span_context {
 //	unsigned char TraceID[APM_TRACE_ID_SIZE];
@@ -99,6 +103,20 @@ struct apm_trace_info_t {
 //	struct apm_span_context sc;
 };
 
+// ---------- 业务上下文(thread_ctx) ----------
+struct thread_ctx_t {
+	__u64 token;         // 追踪 token / trace_id(入口处写入)
+	__u64 ts_ns;         // 最近一次刷新时间
+	__u64 exp_ns;        // 过期时间(now + TTL)
+	__u32 root_thread;   // 主线程
+	__u32 tgid;          // 所属进程
+	__u32 parent_tid;    // 父线程(用于调试/回溯)
+	__u8  is_main_thread;// 主线程
+	__u16 level;         // 继承层数
+	struct apm_trace_info_t *trace_info;
+	struct apm_trace_key_t trace_key;
+};
+
 static __always_inline void cw_copy_byte_arrays(unsigned char *src, unsigned char *dst, __u32 size) {
 	for (int i = 0; i < size; i++) {
 		dst[i] = src[i];

+ 3 - 0
ebpftracer/ebpf/include/socket_trace_common.h

@@ -171,6 +171,8 @@ enum offsets_index {
 	OFFSET_IDX_FIELDS_HTTP2_META_HEADERS_FRAME,
 	OFFSET_IDX_STREAM_HTTP2_CLIENT_CONN,
 	OFFSET_IDX_STREAM_ID_HTTP2_FRAME_HEADER,
+	OFFSET_IDX_P_GOIDCACHE,  // runtime.p.goidcache
+	OFFSET_IDX_P_RUNNEXT,    // runtime.p.runnext
 	OFFSET_IDX_MAX,
 };
 
@@ -203,6 +205,7 @@ struct ebpf_proc_info {
 	__u64 ctx_ptr_pos;
 	__u64 headers_ptr_pos;
 	__u64 buckets_ptr_pos;
+	__u64 use_swiss_map;
 	//gRPC
 	__u64 httpclient_nextid_pos;
 	__u64 stream_method_ptr_pos;

+ 15 - 0
ebpftracer/ebpf/l7/apm_trace.c

@@ -2,6 +2,21 @@
 // Created by Carl.Guo on 2024/4/1.
 //
 
+struct {
+	__uint(type, BPF_MAP_TYPE_LRU_HASH);
+	__type(key, __u32);
+	__type(value, struct thread_ctx_t);
+	__uint(max_entries, 65535);
+} thread_ctx_map SEC(".maps");
+
+
+struct {
+	__uint(type, BPF_MAP_TYPE_LRU_HASH);
+	__type(key, struct fd_trace_key_t);
+	__type(value, struct fd_trace_poll_times_t);
+	__uint(max_entries, 65535);
+} l7_request_fd_pool_map SEC(".maps");
+
 struct {
 	__uint(type, BPF_MAP_TYPE_LRU_HASH);
 	__uint(key_size, sizeof(struct apm_trace_key_t));

+ 4 - 4
ebpftracer/ebpf/l7/cassandra.c

@@ -6,6 +6,7 @@
 #define CASSANDRA_OPCODE_ERROR      0x00
 #define CASSANDRA_OPCODE_QUERY      0x07
 #define CASSANDRA_OPCODE_RESULT     0x08
+#define CASSANDRA_OPCODE_PREPARE    0x09
 #define CASSANDRA_OPCODE_EXECUTE    0x0A
 #define CASSANDRA_OPCODE_BATCH      0x0D
 
@@ -27,7 +28,7 @@ int is_cassandra_request(char *buf, __u64 buf_size, __s16 *stream_id) {
     if (h.version != CASSANDRA_REQUEST_FRAME) {
         return 0;
     }
-    if (h.opcode == CASSANDRA_OPCODE_QUERY || h.opcode == CASSANDRA_OPCODE_EXECUTE || h.opcode == CASSANDRA_OPCODE_BATCH) {
+    if (h.opcode == CASSANDRA_OPCODE_QUERY || h.opcode == CASSANDRA_OPCODE_EXECUTE || h.opcode == CASSANDRA_OPCODE_BATCH || h.opcode == CASSANDRA_OPCODE_PREPARE) {
         *stream_id = h.stream_id;
         return 1;
     }
@@ -37,7 +38,7 @@ int is_cassandra_request(char *buf, __u64 buf_size, __s16 *stream_id) {
 static __always_inline
 int is_cassandra_response(char *buf, __u64 buf_size, __s16 *stream_id, __u32 *status) {
 	return 0;
-    struct cassandra_header h = {};
+	struct cassandra_header h = {};
     if (buf_size < sizeof(h)) {
         return 0;
     }
@@ -56,5 +57,4 @@ int is_cassandra_response(char *buf, __u64 buf_size, __s16 *stream_id, __u32 *st
         return 1;
     }
     return 0;
-}
-
+}

+ 30 - 6
ebpftracer/ebpf/l7/l7.c

@@ -60,7 +60,6 @@
 #include "dubbo2.c"
 #include "dns.c"
 #include "dm.c"
-#include "apm_trace.c"
 
 // go type l7Event struct && type RequestData struct
 struct l7_event {
@@ -372,6 +371,8 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
     if (load_filter_pid() != 0 && pid != load_filter_pid()) {
         return 0;
     }
+//	bpf_printk("trace_enter_write fd(%d) ,size(%d)", fd, size);
+
     char* payload = buf;
     if (iovlen) {
         payload = bpf_map_lookup_elem(&iovec_buf_heap, &zero);
@@ -421,6 +422,7 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
 		}
         __u64 trace_id = start_trace_info->trace_id;
 	    __u32 event_count = cw_get_event_count(trace_id);
+//	    bpf_printk("[Trace End in l7] count(%d) %llu ", event_count, trace_id);
         cw_bpf_debug("[uprobeThread/pidpidpidpid][Trace End in l7][HTTP]pid:[%d]--[%lld]", tid, bpf_ktime_get_ns());
 	    cw_bpf_debug("[Trace End in l7][Response][HTTP] event_count:%d", event_count);
 	    cw_bpf_debug("[Trace End in l7][Response][HTTP] pid:%d,fd:%d,trace_id:%llu", tid, fd, trace_id);
@@ -598,6 +600,7 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
         cw_bpf_debug("[Enter][Redis]:TGID:%d|type:%s|FD:%d\n",k.pid,"type",k.fd);
         req->protocol = PROTOCOL_REDIS;
     } else if (is_memcached_query(payload, size)) {
+	    cw_bpf_debug("[Enter][MEMCACHE]:TGID:%d|type:%s|FD:%d\n",k.pid,"type",k.fd);
         req->protocol = PROTOCOL_MEMCACHED;
     } else if (is_mysql_query(payload, size, &req->request_type)) {
         cw_bpf_debug("[Enter][Mysql]:thread_id:%d\n",tid);
@@ -672,7 +675,14 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
         send_event(ctx, e, cid, conn);
         return 0;
     } else if (is_cassandra_request(payload, size, &k.stream_id)) {
-        req->protocol = PROTOCOL_CASSANDRA;
+//	    bpf_printk("[cassandra] [start] fd(%d) stream_id(%d) goid(%d)", k.fd, k.stream_id, get_current_goroutine());
+	    __u32 ctx_id =get_current_goroutine();
+	    struct thread_ctx_t *current_ctx = bpf_map_lookup_elem(&thread_ctx_map, &ctx_id);
+	    if (current_ctx) {
+		    req->trace_id  =  current_ctx->token;
+			bpf_map_delete_elem(&thread_ctx_map, &ctx_id);
+		}
+	    req->protocol = PROTOCOL_CASSANDRA;
     } else if (is_kafka_request(payload, size, &req->request_id)) {
         req->protocol = PROTOCOL_KAFKA;
         struct l7_request *prev_req = bpf_map_lookup_elem(&active_l7_requests, &k);
@@ -847,8 +857,8 @@ int trace_exit_read_common(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long in
         e->protocol = PROTOCOL_TRACE;
         e->trace_id = trace_info.trace_id;
 	    cw_bpf_debug("\n");
-		cw_bpf_debug("[Trace Start in l7][HTTP]pid:[%d]--[%lld]--trace_id:%llu\n", tid, bpf_ktime_get_ns(),trace_info.trace_id);
-	    cw_bpf_debug("[Trace Start in l7][Receive][HTTP]pid:[%d]|GOID:[%d]|FD:%d|Trace:%llu\n", tid, trace_info.trace_key.goid,k.fd);
+		cw_bpf_debug("[Trace Start in l7][HTTP]pid:[%d]--[%lld]--trace_id:%llu\n", pid, bpf_ktime_get_ns(),trace_info.trace_id);
+	    cw_bpf_debug("[Trace Start in l7][Receive][HTTP]tid:[%d]|GOID:[%d]|FD:%d\n", tid, trace_info.trace_key.goid,k.fd);
         e->payload_size = ret;
         COPY_PAYLOAD(e->payload, ret, payload);
 
@@ -933,6 +943,15 @@ int trace_exit_read_common(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long in
             send_event(ctx, e, cid, conn);
             bpf_map_delete_elem(&active_l7_requests, &k);
             return 0;
+        } else if (is_cassandra_response(payload, ret, &k.stream_id, &e->status)) {
+//	        bpf_printk("[cassandra end] fd(%d) k.stream_id(%d) goid(%d)", k.fd,k.stream_id,get_current_goroutine());
+
+	        req = bpf_map_lookup_elem(&active_l7_requests, &k);
+	        if (!req) {
+		        return 0;
+	        }
+	        e->trace_id = req->trace_id;
+	        response = 1;
         } else {
 //	        cw_bpf_debug("bb 6:[0x%x] k.pid:%d, k.fd:%d",b[4],k.pid,k.fd);
             return 0;
@@ -995,9 +1014,14 @@ int trace_exit_read_common(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long in
         e->component_dport = conn->dport;
         __builtin_memcpy(&e->component_saddr, &conn->saddr, sizeof(e->component_saddr));
         __builtin_memcpy(&e->component_daddr, &conn->daddr, sizeof(e->component_daddr));
-		response = is_redis_response(payload, ret, &e->status);
+		response = is_redis_response(payload, ret, &e->status, e->error_message);
 	} else if (e->protocol == PROTOCOL_MEMCACHED) {
-		response = is_memcached_response(payload, ret, &e->status);
+		cw_bpf_debug("[Response][MEMCACHE]:thread_id:%d\n", tid);
+		e->component_sport = conn->sport;
+		e->component_dport = conn->dport;
+		__builtin_memcpy(&e->component_saddr, &conn->saddr, sizeof(e->component_saddr));
+		__builtin_memcpy(&e->component_daddr, &conn->daddr, sizeof(e->component_daddr));
+		response = is_memcached_response(payload, ret, &e->status, e->error_message);
 	} else if (e->protocol == PROTOCOL_MYSQL) {
 		cw_bpf_debug("[Response][Mysql]:thread_id:%d\n", tid);
 //		__u64 trace_id = get_apm_trace_id(pid, tid);

+ 34 - 7
ebpftracer/ebpf/l7/memcached.c

@@ -1,7 +1,6 @@
 // https://github.com/memcached/memcached/blob/master/doc/protocol.txt
 static __always_inline
 int is_memcached_query(char *buf, __u64 buf_size) {
-	return 0;
     if (buf_size < 9) {
         return 0;
     }
@@ -50,8 +49,7 @@ int is_memcached_query(char *buf, __u64 buf_size) {
 }
 
 static __always_inline
-int is_memcached_response(char *buf, __u64 buf_size, __u32 *status) {
-	return 0;
+int is_memcached_response(char *buf, __u64 buf_size, __u32 *status, unsigned char *error_message) {
     char r[3];
     bpf_read(buf, r);
     char end[2];
@@ -80,6 +78,13 @@ int is_memcached_response(char *buf, __u64 buf_size, __u32 *status) {
         *status = STATUS_OK;
         return 1;
     }
+    if (r[0] == 'E' && r[1] == 'N' && r[2] == 'D') { //END (key not found for get/gets)
+        *status = STATUS_FAILED;
+        if (error_message) {
+            bpf_probe_read(error_message, 27, "key not found for get/gets");
+        }
+        return 1;
+    }
     if (r[0] == 'E' && r[1] == 'X' && r[2] == 'I') { //EXISTS
         *status = STATUS_OK;
         return 1;
@@ -88,12 +93,34 @@ int is_memcached_response(char *buf, __u64 buf_size, __u32 *status) {
         *status = STATUS_FAILED;
         return 1;
     }
-    if (r[0] == 'C' && r[1] == 'L' && r[2] == 'I') { //CLIENT_ERROR
+    if (r[0] == 'C' && r[1] == 'L' && r[2] == 'I') { //CLIENT_ERROR <error>\r\n
         *status = STATUS_FAILED;
-        return 1;
-    }
-    if (r[0] == 'S' && r[1] == 'E' && r[2] == 'R') { //SERVER_ERROR
+        // 提取错误消息:跳过 "CLIENT_ERROR " (14个字符),到 \r\n 之前
+        if (buf_size > 14 && error_message) {
+            __u64 error_msg_len = buf_size - 16; // 总长度 - "CLIENT_ERROR " (14) - "\r\n" (2)
+            if (error_msg_len > ERROR_MSG_PAYLOAD_SIZE - 1) {
+                error_msg_len = ERROR_MSG_PAYLOAD_SIZE - 1;
+            }
+            if (error_msg_len > 0) {
+                bpf_probe_read(error_message, error_msg_len, buf + 14);
+                error_message[error_msg_len] = '\0';
+            }
+        }
+        return 1;
+    }
+    if (r[0] == 'S' && r[1] == 'E' && r[2] == 'R') { //SERVER_ERROR <error>\r\n
         *status = STATUS_FAILED;
+        // 提取错误消息:跳过 "SERVER_ERROR " (14个字符),到 \r\n 之前
+        if (buf_size > 14 && error_message) {
+            __u64 error_msg_len = buf_size - 16; // 总长度 - "SERVER_ERROR " (14) - "\r\n" (2)
+            if (error_msg_len > ERROR_MSG_PAYLOAD_SIZE - 1) {
+                error_msg_len = ERROR_MSG_PAYLOAD_SIZE - 1;
+            }
+            if (error_msg_len > 0) {
+                bpf_probe_read(error_message, error_msg_len, buf + 14);
+                error_message[error_msg_len] = '\0';
+            }
+        }
         return 1;
     }
     if (r[0] >= '0' && r[0] <= '9') { // incr/decr response: <value>\r\n

+ 13 - 1
ebpftracer/ebpf/l7/redis.c

@@ -23,7 +23,7 @@ int is_redis_query(char *buf, __u64 buf_size) {
 }
 
 static __always_inline
-int is_redis_response(char *buf, __u64 buf_size, __u32 *status) {
+int is_redis_response(char *buf, __u64 buf_size, __u32 *status, unsigned char *error_message) {
     char type;
     bpf_read(buf, type);
     char end[2];
@@ -38,6 +38,18 @@ int is_redis_response(char *buf, __u64 buf_size, __u32 *status) {
     }
     if (type == '-') {
         *status = STATUS_FAILED;
+        // 提取错误消息:跳过开头的 '-',到 \r\n 之前
+        // Redis 错误格式:-ERR <error message>\r\n 或 -WRONGTYPE <error message>\r\n
+        if (buf_size > 1 && error_message) {
+            __u64 error_msg_len = buf_size - 3; // 总长度 - '-' (1) - "\r\n" (2)
+            if (error_msg_len > ERROR_MSG_PAYLOAD_SIZE - 1) {
+                error_msg_len = ERROR_MSG_PAYLOAD_SIZE - 1;
+            }
+            if (error_msg_len > 0) {
+                bpf_probe_read(error_message, error_msg_len, buf + 1);
+                error_message[error_msg_len] = '\0';
+            }
+        }
         return 1;
     }
     return 0;

+ 3 - 4
ebpftracer/ebpf/tcp/state.c

@@ -507,9 +507,8 @@ int sys_exit_accept(struct sys_exit_accept_ctx *ctx)
     }
 
     // 从地图中移除项目,避免泄漏  
-    bpf_map_delete_elem(&socket_map, &pid_tgid);  
-
-    return 0;  
+    bpf_map_delete_elem(&socket_map, &pid_tgid);
+    return 0;
 }
 
 // 在系统调用accept返回时挂钩获取文件描述符  
@@ -591,7 +590,7 @@ int tracepoint__sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
     }
 
     // 从地图中移除项目,避免泄漏  
-    bpf_map_delete_elem(&socket_map, &pid_tgid);  
+    bpf_map_delete_elem(&socket_map, &pid_tgid);
 
     return 0;  
 }  

+ 19 - 9
ebpftracer/ebpf/uprobe_base_bpf.c

@@ -493,7 +493,7 @@ int enter_runtime_newproc1(struct pt_regs *ctx)
 	if (!goid) {
 		return 0;
 	}
-	// debug("[Go] [runtime.newproc1] goid:%llu",goid);
+//	bpf_printk("[Go] [runtime.newproc1] goid:%llu",goid);
 
 	struct go_newproc_caller caller = {
 			.goid = goid,
@@ -552,11 +552,11 @@ int exit_runtime_newproc1(struct pt_regs *ctx)
 		bpf_map_delete_elem(&pid_tgid_callerid_map, &pid_tgid);
 		return 0;
 	}
-	// debug("[Go] [runtime.newproc1.exit] current->goid:%llu",goid);
+//	 bpf_printk("[Go] [runtime.newproc1.exit] current->goid:%llu",goid);
 	// 生成当前协程key
 	struct go_key key = { .tgid = tgid, .goid = goid };
 	goid = caller->goid;
-	// debug("[Go] [runtime.newproc1.exit] caller->goid:%llu",goid);	goid = caller->goid;
+//	bpf_printk("[Go] [runtime.newproc1.exit] caller->goid:%llu",goid);	goid = caller->goid;
 	bpf_map_update_elem(&go_ancerstor_map, &key, &goid, BPF_ANY);
 
 	bpf_map_delete_elem(&pid_tgid_callerid_map, &pid_tgid);
@@ -573,12 +573,6 @@ int enter_runtime_runqget(struct pt_regs *ctx)
 	__u64 pid_tgid = bpf_get_current_pid_tgid();
 	__u32 tgid = pid_tgid >> 32;
 
-	int offset_g_goid = 152;
-
-	int offset_p_goidcache = 384;
-
-	int offset_p_runnext = 2456;
-
 	struct ebpf_proc_info *info =
 			bpf_map_lookup_elem(&proc_info_map, &tgid);
 	if (!info) {
@@ -588,6 +582,15 @@ int enter_runtime_runqget(struct pt_regs *ctx)
 	if(info->version < GO_VERSION(1, 15, 0)){
 		return 0;
 	}
+
+	// 从 proc_info 中获取偏移量,而不是硬编码
+	int offset_g_goid = info->offsets[OFFSET_IDX_GOID_RUNTIME_G];
+	int offset_p_goidcache = info->offsets[OFFSET_IDX_P_GOIDCACHE];
+	int offset_p_runnext = info->offsets[OFFSET_IDX_P_RUNNEXT];
+	// 检查偏移量是否有效
+	if (offset_g_goid < 0 || offset_p_goidcache < 0 || offset_p_runnext < 0) {
+		return 0;
+	}
 	void * sp = (void *)PT_REGS_SP(ctx);
 
 	void *p_ptr;// _p_ *p
@@ -638,6 +641,13 @@ int enter_runtime_runqget(struct pt_regs *ctx)
 
 }
 
+// runtime.goready 用于唤醒一个 goroutine
+// 函数签名: func goready(gp *g, traceskip int)
+// 参数:
+//   - gp *g: 第一个参数,要被唤醒的 goroutine
+//   - traceskip int: 第二个参数,用于堆栈跟踪
+// 在这里建立唤醒者和被唤醒者的关系
+
 // /sys/kernel/debug/tracing/events/sched/sched_process_exit/format
 SEC("tracepoint/sched/sched_process_exit")
 int bpf_func_sched_process_exit(struct sched_comm_exit_ctx *ctx)

+ 446 - 0
ebpftracer/ebpf/utrace/go/db/gocql.probe.bpf.c

@@ -0,0 +1,446 @@
+//
+// Created by Carl.Guo on 2025/11/12.
+//
+
+#include "arguments.h"
+#include "span_context.h"
+#include "go_context.h"
+#include "go_types.h"
+#include "uprobe.h"
+#include "go_common.h"
+
+#define MAX_STMT_LEN 128  // 最大语句长度
+
+// Query 结构体中 stmt 字段的偏移量
+// 如果 stmt 是 Query 结构体的第一个字段,偏移量为 0
+// 可以通过 DWARF 信息动态获取,这里使用可配置的偏移量
+
+// internalQuery 结构体中 originalQuery 字段的偏移量
+// internalQuery.originalQuery 是指向 Query 的指针
+
+// 存储 gocql 查询请求的上下文
+struct gocql_query_context_t {
+    BASE_SPAN_PROPERTIES
+    char stmt[MAX_STMT_LEN];
+    __u64 stmt_size;
+};
+
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct gocql_query_context_t));
+	__uint(max_entries, 1);
+} gocql_query_storage_map SEC(".maps");
+
+struct {
+    __uint(type, BPF_MAP_TYPE_HASH);
+    __type(key, void *);
+    __type(value, struct gocql_query_context_t);
+    __uint(max_entries, MAX_CONCURRENT);
+} gocql_query_events SEC(".maps");
+
+#define MAX_CONCURRENT 50
+
+#define MAX_QUERY_SIZE 256
+
+struct sql_request_t {
+	BASE_SPAN_PROPERTIES
+	char query[MAX_QUERY_SIZE];
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__type(key, void *);
+	__type(value, struct sql_request_t);
+	__uint(max_entries, MAX_CONCURRENT);
+} sql_events SEC(".maps");
+
+// This instrumentation attaches uprobe to the following function:
+// func (s *Session) Query(stmt string, values ...interface{}) *Query
+/*
+SEC("uprobe/Session_Query")
+int uprobe_Session_Query(struct pt_regs *ctx) {
+	cw_bpf_debug("[Session_Query] 0");
+
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 tgid = pid_tgid >> 32;
+    
+    struct ebpf_proc_info *info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+    if (!info) {
+        return 0;
+    }
+
+    // 获取 stmt string 参数
+    // 在 Go 的调用约定中,string 参数被拆分成两个独立的参数:指针和长度
+    // 参数顺序:1 = s *Session (receiver), 2 = stmt 指针, 3 = stmt 长度
+    void *stmt_ptr = get_argument(ctx, 2);
+    __u64 stmt_len = (__u64)get_argument(ctx, 3);
+    
+    cw_bpf_debug("[Session_Query] stmt_ptr=%llx", (__u64)stmt_ptr);
+    cw_bpf_debug("[Session_Query] stmt_len=%llu", stmt_len);
+    
+    if (stmt_ptr == 0 || stmt_len == 0) {
+        return 0;
+    }
+    
+    if (stmt_len > MAX_STMT_LEN) {
+        stmt_len = MAX_STMT_LEN;
+    }
+
+    char stmt_buf[MAX_STMT_LEN + 1] = {0};
+
+    // 使用固定大小读取,验证器可以确定读取大小的上界
+    // sizeof(stmt_buf) - 1 是编译时常量,验证器可以验证
+    long res = bpf_probe_read(stmt_buf, sizeof(stmt_buf) - 1, stmt_ptr);
+    if (res != 0) {
+        return 0;
+    }
+
+    // 确保字符串以 null 结尾
+    stmt_buf[MAX_STMT_LEN] = '\0';
+    
+    // 打印 stmt
+    cw_bpf_debug("[Session_Query] stmt: %s", stmt_buf);
+
+    return 0;
+}
+*/
+
+// This instrumentation attaches uprobe to the following function:
+// func (s *Session) executeQuery(qry *Query) (it *Iter)
+SEC("uprobe/Session_executeQuery")
+int uprobe_Session_executeQuery(struct pt_regs *ctx) {
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 tgid = pid_tgid >> 32;
+    
+//    cw_bpf_debug("[gocql] enter executeQuery");
+//    cw_bpf_debug("[gocql] tgid=%u", tgid);
+    
+    struct ebpf_proc_info *info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+    if (!info) {
+        cw_bpf_debug("[gocql] no proc_info");
+        return 0;
+    }
+
+    // 获取第二个参数 qry *Query
+    // 参数顺序:1 = s *Session (receiver), 2 = qry *Query
+    void *qry_ptr = get_argument(ctx, 2);
+//    cw_bpf_debug("[gocql] qry_ptr=%llx", (__u64)qry_ptr);
+    if (qry_ptr == 0) {
+        cw_bpf_debug("[gocql] qry_ptr is null");
+        return 0;
+    }
+
+    // 获取 consistent key (使用 goroutine 地址作为 key)
+    void *key = (void *)GOROUTINE(ctx);
+//    cw_bpf_debug("[gocql] key=%llx", (__u64)key);
+    
+    // 检查是否已经存在该请求
+    struct gocql_query_context_t *existing = bpf_map_lookup_elem(&gocql_query_events, &key);
+    if (existing != NULL) {
+        cw_bpf_debug("[gocql] already exists");
+        return 0;
+    }
+
+
+
+	// 从 Query 结构体中读取 stmt 字段
+    // stmt 是一个 string 类型,在 Go 中是 go_string_ot 结构体
+    struct go_string_ot stmt_str = {0};
+    void *stmt_ptr = (void *)((char *)qry_ptr + 0);
+//    cw_bpf_debug("[gocql] stmt_ptr=%llx", (__u64)stmt_ptr);
+
+    // 读取 string 结构体
+    long res = bpf_probe_read(&stmt_str, sizeof(stmt_str), stmt_ptr);
+//    cw_bpf_debug("[gocql] read res=%ld", res);
+//    cw_bpf_debug("[gocql] read ken=%ld", stmt_str.len);
+//    cw_bpf_debug("[gocql] sql : %s", stmt_str.str);
+//	return 0;
+
+    if (res != 0) {
+        return 0;
+    }
+//    cw_bpf_debug("[gocql] stmt len=%lld", stmt_str.len);
+
+    // 分配存储空间
+	u32 zero = 0;
+	struct gocql_query_context_t *query_ctx = bpf_map_lookup_elem(&gocql_query_storage_map, &zero);
+	if (query_ctx == NULL) {
+		cw_bpf_debug("grpc:client:ClientConn_Invoke: failed to get storage");
+		return -1;
+	}
+
+	__builtin_memset(query_ctx, 0, sizeof(struct gocql_query_context_t));
+
+	query_ctx->start_time = bpf_ktime_get_ns();
+	query_ctx->stmt_size = stmt_str.len;
+
+    // 读取 stmt 字符串内容
+//    __u64 stmt_size = stmt_str.len > MAX_STMT_LEN ? MAX_STMT_LEN : stmt_str.len;
+//    cw_bpf_debug("[gocql] stmt_size=%llu", stmt_size);
+    res = bpf_probe_read(query_ctx->stmt, sizeof(query_ctx->stmt), stmt_str.str);
+//    cw_bpf_debug("[gocql] read stmt res=%ld", res);
+    if (res != 0) {
+        cw_bpf_debug("[gocql] read stmt failed");
+        return 0;
+    }
+
+//    // 确保字符串以 null 结尾
+//    if (stmt_size < MAX_STMT_LEN) {
+//        query_ctx->stmt[stmt_size] = '\0';
+//    } else {
+//        query_ctx->stmt[MAX_STMT_LEN - 1] = '\0';
+//    }
+
+    // 获取 context
+//    void *context_ptr_val = get_Go_context(ctx, 2, 0, true);
+//    cw_bpf_debug("[gocql] context_ptr=%llx", (__u64)context_ptr_val);
+//    if (context_ptr_val != 0) {
+//        start_tracking_span(context_ptr_val, &query_ctx.sc);
+//    }
+
+    // 保存到 map
+     bpf_map_update_elem(&gocql_query_events, &key, query_ctx, 0);
+//    cw_bpf_debug("[gocql] save res=%ld", update_res);
+//    cw_bpf_debug("[gocql] stmt_size=%llu", query_ctx->stmt_size);
+//    cw_bpf_debug("[gocql] stmt=%s", query_ctx->stmt);
+
+//    if (query_ctx.stmt_size > 0 && query_ctx.stmt_size <= 64) {
+//        cw_bpf_debug("[gocql] stmt: %s", query_ctx.stmt);
+//    }
+
+    return 0;
+}
+
+// This instrumentation attaches uprobe to the return of executeQuery
+SEC("uprobe/Session_executeQuery")
+int uprobe_Session_executeQuery_Returns(struct pt_regs *ctx) {
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 tgid = pid_tgid >> 32;
+    __u32 pid = pid_tgid;
+    
+    cw_bpf_debug("[gocql] enter Returns");
+//    cw_bpf_debug("[gocql] tgid=%u", tgid);
+//    return 0;
+    struct ebpf_proc_info *info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+    if (!info) {
+        cw_bpf_debug("[gocql] Returns no info");
+        return 0;
+    }
+
+    // 获取 consistent key
+    void *key = (void *)GOROUTINE(ctx);
+//    cw_bpf_debug("[gocql] Returns key=%llx", (__u64)key);
+    
+    // 查找对应的请求
+    struct gocql_query_context_t *query_ctx = bpf_map_lookup_elem(&gocql_query_events, &key);
+    if (query_ctx == 0) {
+        cw_bpf_debug("[gocql] Returns no ctx");
+        return 0;
+    }
+//    cw_bpf_debug("[gocql] Returns found ctx");
+
+    // 更新结束时间
+    query_ctx->end_time = bpf_ktime_get_ns();
+//    cw_bpf_debug("[gocql] Returns stmt_size=%llu", query_ctx->stmt_size);
+//    cw_bpf_debug("[gocql] Returns duration=%llu", query_ctx->end_time - query_ctx->start_time);
+//
+//	cw_bpf_debug("[gocql] Returns stmt=%s", query_ctx->stmt);
+
+    // 停止跟踪 span
+//    stop_tracking_span(&query_ctx->sc, &query_ctx->psc);
+
+    __u32 zero = 0;
+    struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+    if (!e) {
+        cw_bpf_debug("[gocql] Returns no event");
+        bpf_map_delete_elem(&gocql_query_events, &key);
+        return 0;
+    }
+
+    // 设置事件属性
+    e->protocol = PROTOCOL_CASSANDRA;
+    e->pid = tgid;
+    e->start_at = query_ctx->start_time;
+    e->end_at = query_ctx->end_time;
+    e->duration = e->end_at - e->start_at;
+    e->payload_size = query_ctx->stmt_size;
+//    cw_bpf_debug("[gocql] Returns payload_size=%llu", e->payload_size);
+    
+    COPY_PAYLOAD(e->payload, query_ctx->stmt_size, query_ctx->stmt);
+
+    struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
+    struct apm_trace_info_t *trace_info = get_apm_trace_info_by_trace_key(trace_key);
+    
+    if (trace_info == 0) {
+        trace_info = get_apm_trace_info_v3(trace_key, pid_tgid, tgid, pid_tgid);
+    }
+    
+    if (trace_info) {
+        e->trace_id = trace_info->trace_id;
+    }
+    
+    if (e->trace_id == 0) {
+        e->trace_id = get_apm_trace_id(tgid, pid_tgid);
+    }
+    // 发送事件
+    long error = bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
+    if (error == 0) {
+        cw_add_event_count(e->trace_id);
+        cw_bpf_debug("[gocql] Returns success");
+    }
+    // 清理
+    bpf_map_delete_elem(&gocql_query_events, &key);
+    cw_bpf_debug("[gocql] Returns done %llu",e->trace_id);
+    return 0;
+}
+
+// This instrumentation attaches uprobe to the following function:
+// func (s *Session) executeQuery(qry *internalQuery) (it *Iter)
+// internalQuery 包含 originalQuery *Query 字段
+SEC("uprobe/Session_executeQuery_cassandra")
+int uprobe_Session_executeQuery_cassandra(struct pt_regs *ctx) {
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 tgid = pid_tgid >> 32;
+    
+    bpf_printk("[cassandra] enter Session.executeQuery");
+    
+    struct ebpf_proc_info *info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+    if (!info) {
+        cw_bpf_debug("[cassandra] no proc_info");
+        return 0;
+    }
+
+    // 获取第二个参数 qry *internalQuery
+    // 参数顺序:1 = s *Session (receiver), 2 = qry *internalQuery
+    void *internal_qry_ptr = get_argument(ctx, 2);
+    if (internal_qry_ptr == 0) {
+        cw_bpf_debug("[cassandra] internal_qry_ptr is null");
+        return 0;
+    }
+
+    // 从 internalQuery 中读取 originalQuery *Query 指针
+    void *original_query_ptr = 0;
+    void *original_query_ptr_addr = (void *)((char *)internal_qry_ptr + 0);
+    long res = bpf_probe_read(&original_query_ptr, sizeof(original_query_ptr), original_query_ptr_addr);
+    if (res != 0 || original_query_ptr == 0) {
+        cw_bpf_debug("[cassandra] failed to read originalQuery pointer");
+        return 0;
+    }
+
+    // 获取 consistent key (使用 goroutine 地址作为 key)
+    void *key = (void *)GOROUTINE(ctx);
+    
+    // 检查是否已经存在该请求
+    struct gocql_query_context_t *existing = bpf_map_lookup_elem(&gocql_query_events, &key);
+    if (existing != NULL) {
+        cw_bpf_debug("[cassandra] already exists");
+        return 0;
+    }
+
+    // 从 Query 结构体中读取 stmt 字段
+    struct go_string_ot stmt_str = {0};
+    void *stmt_ptr = (void *)((char *)original_query_ptr + 0);
+    
+    // 读取 string 结构体
+    res = bpf_probe_read(&stmt_str, sizeof(stmt_str), stmt_ptr);
+    if (res != 0) {
+        cw_bpf_debug("[cassandra] failed to read stmt_str");
+        return 0;
+    }
+    // 分配存储空间
+    u32 zero = 0;
+    struct gocql_query_context_t *query_ctx = bpf_map_lookup_elem(&gocql_query_storage_map, &zero);
+    if (query_ctx == NULL) {
+        cw_bpf_debug("[cassandra] failed to get storage");
+        return -1;
+    }
+
+    __builtin_memset(query_ctx, 0, sizeof(struct gocql_query_context_t));
+
+    query_ctx->start_time = bpf_ktime_get_ns();
+    query_ctx->stmt_size = stmt_str.len;
+
+    // 读取 stmt 字符串内容
+    res = bpf_probe_read(query_ctx->stmt, sizeof(query_ctx->stmt), stmt_str.str);
+    if (res != 0) {
+        cw_bpf_debug("[cassandra] read stmt failed");
+        return 0;
+    }
+
+    // 保存到 map
+    bpf_map_update_elem(&gocql_query_events, &key, query_ctx, 0);
+
+    return 0;
+}
+
+// This instrumentation attaches uprobe to the return of Session.executeQuery
+SEC("uprobe/Session_executeQuery_cassandra")
+int uprobe_Session_executeQuery_cassandra_Returns(struct pt_regs *ctx) {
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 tgid = pid_tgid >> 32;
+
+    cw_bpf_debug("[cassandra] enter Session.executeQuery Returns");
+    struct ebpf_proc_info *info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+    if (!info) {
+        cw_bpf_debug("[cassandra] Returns no info");
+        return 0;
+    }
+
+    // 获取 consistent key
+    void *key = (void *)GOROUTINE(ctx);
+    
+    // 查找对应的请求
+    struct gocql_query_context_t *query_ctx = bpf_map_lookup_elem(&gocql_query_events, &key);
+    if (query_ctx == 0) {
+        cw_bpf_debug("[cassandra] Returns no ctx");
+        return 0;
+    }
+
+    // 更新结束时间
+    query_ctx->end_time = bpf_ktime_get_ns();
+
+    __u32 zero = 0;
+    struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+    if (!e) {
+        cw_bpf_debug("[cassandra] Returns no event");
+        bpf_map_delete_elem(&gocql_query_events, &key);
+        return 0;
+    }
+
+    // 设置事件属性
+    e->protocol = PROTOCOL_CASSANDRA;
+    e->pid = tgid;
+    e->start_at = query_ctx->start_time;
+    e->end_at = query_ctx->end_time;
+    e->duration = e->end_at - e->start_at;
+    e->payload_size = query_ctx->stmt_size;
+    
+    COPY_PAYLOAD(e->payload, query_ctx->stmt_size, query_ctx->stmt);
+
+    struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
+    struct apm_trace_info_t *trace_info = get_apm_trace_info_by_trace_key(trace_key);
+    
+    if (trace_info == 0) {
+        trace_info = get_apm_trace_info_v3(trace_key, pid_tgid, tgid, pid_tgid);
+    }
+    
+    if (trace_info) {
+        e->trace_id = trace_info->trace_id;
+    }
+    
+    if (e->trace_id == 0) {
+        e->trace_id = get_apm_trace_id(tgid, pid_tgid);
+    }
+    // 发送事件
+    long error = bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
+    if (error == 0) {
+        cw_add_event_count(e->trace_id);
+        cw_bpf_debug("[cassandra] Returns success");
+    }
+    // 清理
+    bpf_map_delete_elem(&gocql_query_events, &key);
+    cw_bpf_debug("[cassandra] Returns done %llu", e->trace_id);
+    return 0;
+}

+ 36 - 36
ebpftracer/ebpf/utrace/go/net/grpc.client.probe.bpf.c

@@ -94,7 +94,7 @@ u64 status_message_pos = 48;        //使用固定值48即可,不再处理多
 // func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error
 SEC("uprobe/ClientConn_Invoke")
 int uprobe_ClientConn_Invoke(struct pt_regs *ctx) {
-    // bpf_printk("enter the uprobe_ClientConn_Invoke \n");
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke \n");
     // positions
     u64 clientconn_pos = 1;
     u64 method_ptr_pos = 4;
@@ -110,10 +110,10 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) {
 
     // Get key
     void *key = (void *)GOROUTINE(ctx);
-    // bpf_printk("enter the uprobe_ClientConn_Invoke key is 0x%llx\n", (u64)key);
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke key is 0x%llx\n", (u64)key);
     void *grpcReq_ptr = bpf_map_lookup_elem(&grpc_client_events, &key);
     if (grpcReq_ptr != NULL) {
-        bpf_printk("uprobe/ClientConn_Invoke already tracked with the current context");
+        cw_bpf_debug("uprobe/ClientConn_Invoke already tracked with the current context");
         return 0;
     }
 
@@ -121,7 +121,7 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) {
     u32 zero = 0;
     struct grpc_client_request_t *grpcReq = bpf_map_lookup_elem(&grpc_client_storage_map, &zero);
     if (grpcReq == NULL) {
-        bpf_printk("grpc:client:ClientConn_Invoke: failed to get storage");
+        cw_bpf_debug("grpc:client:ClientConn_Invoke: failed to get storage");
         return -1;
     }
     
@@ -142,7 +142,7 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) {
     if (!get_go_string_from_user_ptr((void *)(clientconn_ptr + clientconn_target_ptr_pos),
                                      grpcReq->target,
                                      sizeof(grpcReq->target))) {
-        bpf_printk("target write failed, aborting ebpf probe");
+        cw_bpf_debug("target write failed, aborting ebpf probe");
         return 0;
     }
 
@@ -155,7 +155,7 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) {
         cw_save_parent_tracking_span(cw_psc);
 	}
 
-    // bpf_printk("grpcReq->target is %s\n", grpcReq->target);
+    // cw_bpf_debug("grpcReq->target is %s\n", grpcReq->target);
 
     // start_span_params_t start_span_params = {
     //     .ctx = ctx,
@@ -170,7 +170,7 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) {
     // Write event
     bpf_map_update_elem(&grpc_client_events, &key, grpcReq, 0);
     start_tracking_span(context_ptr_val, &grpcReq->sc);
-    // bpf_printk("enter the uprobe_ClientConn_Invoke start_tracking_span\n");
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke start_tracking_span\n");
     return 0;
 }
 
@@ -178,11 +178,11 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) {
 // func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error
 SEC("uprobe/ClientConn_Invoke")
 int uprobe_ClientConn_Invoke_Returns(struct pt_regs *ctx) {
-    // bpf_printk("enter the uprobe_ClientConn_Invoke_Returns \n");
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke_Returns \n");
     void *key = (void *)GOROUTINE(ctx);
     struct grpc_client_request_t *grpc_span = bpf_map_lookup_elem(&grpc_client_events, &key);
     if (grpc_span == NULL) {
-        bpf_printk("event is NULL in ret probe");
+        cw_bpf_debug("event is NULL in ret probe");
         return 0;
     }
 
@@ -224,7 +224,7 @@ int uprobe_ClientConn_Invoke_Returns(struct pt_regs *ctx) {
         (void *)(s_ptr + status_message_pos), grpc_span->err_msg, sizeof(grpc_span->err_msg));
 
 done:
-    // bpf_printk("switch to the done position\n");
+    // cw_bpf_debug("switch to the done position\n");
     grpc_span->end_time = bpf_ktime_get_ns();
     // output_span_event(ctx, grpc_span, sizeof(*grpc_span), &grpc_span->sc);
     stop_tracking_span(&grpc_span->sc, &grpc_span->psc);
@@ -245,7 +245,7 @@ done:
     struct apm_trace_info_t * trace_info = get_apm_trace_info_by_trace_key(trace_key);
 
     if (trace_info == NULL) {
-        bpf_printk("enter the trace_info is NULL\n");
+        cw_bpf_debug("enter the trace_info is NULL\n");
         trace_info = get_apm_trace_info_v3(trace_key,pid_tgid, pid, pid_tgid);
     }
     
@@ -265,12 +265,12 @@ done:
     e->statement_id = 0;
     e->payload_size = grpc_span->method_size;
     if (trace_info) {
-        // bpf_printk("trace_info->trace_id is %llu\n", trace_info->trace_id);
+        // cw_bpf_debug("trace_info->trace_id is %llu\n", trace_info->trace_id);
         e->trace_id = trace_info->trace_id;
     }
     if(e->trace_id == 0){
         e->trace_id = get_apm_trace_id(pid,pid_tgid);
-        bpf_printk("e->trace_id is %llu\n", e->trace_id);
+        cw_bpf_debug("e->trace_id is %llu\n", e->trace_id);
     }
     // 使用固定长度读取 target 信息,避免验证器复杂性
     if (grpc_span->target_size > 0) {
@@ -283,10 +283,10 @@ done:
     }
     COPY_PAYLOAD(e->payload, grpc_span->method_size, grpc_span->method);
 
-    // bpf_printk("e->payload is %s\n", e->payload);
-    // bpf_printk("grpc_span->target is %s\n", grpc_span->target);
+    // cw_bpf_debug("e->payload is %s\n", e->payload);
+    // cw_bpf_debug("grpc_span->target is %s\n", grpc_span->target);
     // COPY_PAYLOAD(e->payload + grpc_span->method_size, grpc_span->target_size, grpc_span->target);
-    // bpf_printk("e->payload is %s\n", e->payload);
+    // cw_bpf_debug("e->payload is %s\n", e->payload);
     // e->payload_size += grpc_span->target_size;
     
     struct  apm_span_context * sc = cw_get_current_tracking_span(trace_info);
@@ -298,14 +298,14 @@ done:
     e->end_at = bpf_ktime_get_ns();
     e->start_at = grpc_span->start_time;
     e->duration = e->end_at - e->start_at;
-    // bpf_printk("send_event trace_id is444 %llu\n", e->trace_id);
+    // cw_bpf_debug("send_event trace_id is444 %llu\n", e->trace_id);
     long error = bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
 	if (error ==0){
 	    cw_add_event_count(e->trace_id);
 	}
 
 
-    // bpf_printk("enter the uprobe_ClientConn_Invoke_Returns done\n");
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke_Returns done\n");
     return 0;
 }
 
@@ -316,19 +316,19 @@ cw_append_item_to_slice(void *new_item, u32 item_size, void *slice_user_ptr) {
     struct go_slice_ot slice = {0};
     long res = bpf_probe_read_user(&slice, sizeof(slice), slice_user_ptr);
     if (res != 0) {
-        bpf_printk("cw_append_item_to_slice: failed to read slice descriptor, res=%ld\n", res);
+        cw_bpf_debug("cw_append_item_to_slice: failed to read slice descriptor, res=%ld\n", res);
         return;
     }
 
-    // bpf_printk("cw_append_item_to_slice len is %d\n", slice.len);
-    // bpf_printk("cw_append_item_to_slice cap is %d\n", slice.cap);
-    // bpf_printk("cw_append_item_to_slice array is %p\n", slice.array);
+    // cw_bpf_debug("cw_append_item_to_slice len is %d\n", slice.len);
+    // cw_bpf_debug("cw_append_item_to_slice cap is %d\n", slice.cap);
+    // cw_bpf_debug("cw_append_item_to_slice array is %p\n", slice.array);
 
     u64 slice_len = slice.len;
     u64 slice_cap = slice.cap;
     if (slice_len < slice_cap && slice.array != NULL) {
         // Room available on current array, append to the underlying array
-        // bpf_printk("enter the cw_append_item_to_slice11111\n");
+        // cw_bpf_debug("enter the cw_append_item_to_slice11111\n");
         res = bpf_probe_write_user(slice.array + (item_size * slice_len), new_item, item_size);
     } else {
         // No room on current array - try to copy new one of size item_size * (len + 1)
@@ -354,7 +354,7 @@ cw_append_item_to_slice(void *new_item, u32 item_size, void *slice_user_ptr) {
         // Append to buffer
         if (slice.array != NULL) {
             bpf_probe_read_user(new_slice_array, alloc_size, slice.array);
-            // bpf_printk("append_item_to_slice: copying %d bytes to new array from address 0x%llx",
+            // cw_bpf_debug("append_item_to_slice: copying %d bytes to new array from address 0x%llx",
                     //    alloc_size,
                     //    slice.array);
         }
@@ -365,7 +365,7 @@ cw_append_item_to_slice(void *new_item, u32 item_size, void *slice_user_ptr) {
 
         void *new_array = write_target_data(new_slice_array, new_array_size);
         if (new_array == NULL) {
-            bpf_printk("append_item_to_slice: failed to copy new array to userspace");
+            cw_bpf_debug("append_item_to_slice: failed to copy new array to userspace");
             return;
         }
 
@@ -373,15 +373,15 @@ cw_append_item_to_slice(void *new_item, u32 item_size, void *slice_user_ptr) {
         slice.array = new_array;
         slice.cap++;
 
-        // bpf_printk("enter the cw_append_item_to_slice222222\n");
+        // cw_bpf_debug("enter the cw_append_item_to_slice222222\n");
     }
 
     // Update len
     slice.len++;
-    // bpf_printk("after cw_append_item_to_slice len is %d\n", slice.len);
+    // cw_bpf_debug("after cw_append_item_to_slice len is %d\n", slice.len);
     long success = bpf_probe_write_user(slice_user_ptr, &slice, sizeof(slice));
     if (success != 0) {
-        bpf_printk("append_item_to_slice: failed to update slice in userspace");
+        cw_bpf_debug("append_item_to_slice: failed to update slice in userspace");
         return;
     }
 }
@@ -389,7 +389,7 @@ cw_append_item_to_slice(void *new_item, u32 item_size, void *slice_user_ptr) {
 SEC("uprobe/loopyWriter_headerHandler")
 int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
     void *headerFrame_ptr = get_argument(ctx, 2);
-    // bpf_printk("enter the get header handler storage\n");
+    // cw_bpf_debug("enter the get header handler storage\n");
 
     __u64 pid_tgid = bpf_get_current_pid_tgid();
 	__u32 tgid = pid_tgid >> 32;
@@ -414,7 +414,7 @@ int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
     struct header_handler_storage *storage = bpf_map_lookup_elem(&header_handler_storage_map, &zero);
     if (!storage) {
         bpf_map_delete_elem(&streamid_to_span_contexts, &stream_id);
-        bpf_printk("Failed to get header handler storage\n");
+        cw_bpf_debug("Failed to get header handler storage\n");
         return 0;
     }
     
@@ -435,7 +435,7 @@ int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
     __builtin_memset(grpcClientReq, 0, sizeof(struct grpc_client_request_t));
     grpcClientReq->start_time = bpf_ktime_get_ns();
 
-    // bpf_printk("enter the uprobe_LoopyWriter_HeaderHandler444444\n");
+    // cw_bpf_debug("enter the uprobe_LoopyWriter_HeaderHandler444444\n");
 	// struct apm_span_context *cw_psc = cw_get_parent_tracking_span();
     struct apm_span_context *cw_psc = cw_get_parent_tracking_span_by_trace_key(*sc_ptr);
 	if(cw_psc){
@@ -453,7 +453,7 @@ int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
 
     copy_byte_arrays(proc_info->instance_id, grpcClientReq->apm_sc.instance_id, APM_APP_ID_SIZE);
 	copy_byte_arrays(proc_info->app_id, grpcClientReq->apm_sc.app_id, APM_APP_ID_SIZE);
-    // bpf_printk("grpcClientReq->apm_sc.app_id is %s\n", grpcClientReq->apm_sc.app_id);
+    // cw_bpf_debug("grpcClientReq->apm_sc.app_id is %s\n", grpcClientReq->apm_sc.app_id);
 
     bpf_map_update_elem(&apm_current_span_context_map, sc_ptr, &grpcClientReq->apm_sc, BPF_ANY);
 	// cw_save_current_tracking_span(&grpcClientReq->apm_sc);
@@ -464,7 +464,7 @@ int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
     char tp_key[CW_HEADER_KEY_LENGTH] = CW_HEADER_KEY_VAL;
     char *key_data_addr = cw_write_target_data((void *)tp_key, sizeof(tp_key), proc_info);
     if (key_data_addr == NULL) {
-        bpf_printk("Key data write failed\n");
+        cw_bpf_debug("Key data write failed\n");
         return 0;
     }
     
@@ -476,7 +476,7 @@ int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
     // Write value using cw_write_target_data (it will automatically allocate next available position)
     char *val_data_addr = cw_write_target_data((void *)val, sizeof(val), proc_info);
     if (val_data_addr == NULL) {
-        bpf_printk("Val data write failed\n");
+        cw_bpf_debug("Val data write failed\n");
         return 0;
     }
     
@@ -498,14 +498,14 @@ int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
 SEC("uprobe/http2Client_NewStream")
 // func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error)
 int uprobe_http2Client_NewStream(struct pt_regs *ctx) {
-    // bpf_printk("enter the uprobe_http2Client_NewStream \n");
+    // cw_bpf_debug("enter the uprobe_http2Client_NewStream \n");
     __u32 tgid = (__u32)(bpf_get_current_pid_tgid() >> 32);
 	struct ebpf_proc_info *info =
 		bpf_map_lookup_elem(&proc_info_map, &tgid);
 	if (!info) {
 		return -1;
 	}
-    // bpf_printk("info->httpclient_nextid_pos is %d\n", info->httpclient_nextid_pos);
+    // cw_bpf_debug("info->httpclient_nextid_pos is %d\n", info->httpclient_nextid_pos);
 
     struct go_iface go_context = {0};
     get_Go_context(ctx, 2, 0, true);

+ 49 - 49
ebpftracer/ebpf/utrace/go/net/grpc.server.probe.bpf.c

@@ -103,19 +103,19 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
 		return -1;
 	}
     if (go_context == NULL) {
-        bpf_printk("grpc:server:handleStream: NULL go_context");
+        cw_bpf_debug("grpc:server:handleStream: NULL go_context");
         return -1;
     }
-    // bpf_printk("info->stream_method_ptr_pos is %d\n", info->stream_method_ptr_pos);
+    // cw_bpf_debug("info->stream_method_ptr_pos is %d\n", info->stream_method_ptr_pos);
     if (stream_ptr == NULL) {
-        bpf_printk("grpc:server:handleStream: NULL stream_ptr");
+        cw_bpf_debug("grpc:server:handleStream: NULL stream_ptr");
         return -1;
     }
 
     void *key = (void *)GOROUTINE(ctx);
     void *grpcReq_event_ptr = bpf_map_lookup_elem(&grpc_events, &key);
     if (grpcReq_event_ptr != NULL) {
-        bpf_printk("grpc:server:handleStream: event already tracked");
+        cw_bpf_debug("grpc:server:handleStream: event already tracked");
         return 0;
     }
 
@@ -125,7 +125,7 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
     long rc =
         bpf_probe_read_user(&stream_id, sizeof(stream_id), (void *)(stream_ptr + stream_id_pos));
     if (rc != 0) {
-        bpf_printk("grpc:server:handleStream: failed to read stream ID");
+        cw_bpf_debug("grpc:server:handleStream: failed to read stream ID");
         return -2;
     }
 
@@ -135,7 +135,7 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
         u32 zero = 0;
         grpcReq = bpf_map_lookup_elem(&grpc_storage_map, &zero);
         if (grpcReq == NULL) {
-            bpf_printk("grpc:server:handleStream: failed to get grpcReq");
+            cw_bpf_debug("grpc:server:handleStream: failed to get grpcReq");
             return 0;
         }
     }
@@ -159,12 +159,12 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
     bool parsed_method =
         get_go_string_from_user_ptr(method_ptr, grpcReq->method, sizeof(grpcReq->method));
     if (!parsed_method) {
-        bpf_printk("grpc:server:handleStream: failed to read gRPC method from stream");
+        cw_bpf_debug("grpc:server:handleStream: failed to read gRPC method from stream");
         bpf_map_delete_elem(&streamid_to_grpc_events, &stream_id);
         return -3;
     }
 
-    // bpf_printk("grpc:server:handleStream: get the method is %s\n", grpcReq->method);
+    // cw_bpf_debug("grpc:server:handleStream: get the method is %s\n", grpcReq->method);
 
     // if (server_addr_supported) {
     //     void *http2server = get_argument(ctx, 3);
@@ -175,14 +175,14 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
     //             &local_addr_ptr, sizeof(local_addr_ptr), get_go_interface_instance(local_addr_pos));
     //         get_tcp_net_addr_from_tcp_addr(ctx, &grpcReq->local_addr, (void *)(local_addr_ptr));
     //     } else {
-    //         bpf_printk("grpc:server:handleStream: failed to get http2server arg");
+    //         cw_bpf_debug("grpc:server:handleStream: failed to get http2server arg");
     //     }
     // }
 
     // Write event
     rc = bpf_map_update_elem(&grpc_events, &key, grpcReq, 0);
     if (rc != 0) {
-        bpf_printk("grpc:server:handleStream: failed to update event");
+        cw_bpf_debug("grpc:server:handleStream: failed to update event");
         return -4;
     }
     start_tracking_span(go_context->data, &grpcReq->sc);
@@ -197,7 +197,7 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
         return 0;
     }
 
-    // bpf_printk("start get apm data\n");
+    // cw_bpf_debug("start get apm data\n");
 
 
     // struct apm_span_context *cw_parent_span_context = bpf_map_lookup_elem(&apm_span_context_heap3, &zero);
@@ -227,7 +227,7 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
     
     // 拷贝 grpcReq->method 到 payload 并设置 payload_size
     // 手动计算字符串长度(在 eBPF 中不能使用 strlen)
-    u32 method_len = 0;
+    u64 method_len = 0;
     for (int i = 0; i < MAX_SIZE; i++) {
         if (grpcReq->method[i] == '\0') {
             method_len = i;
@@ -243,7 +243,7 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
     e->payload_size = method_len;
     COPY_PAYLOAD(e->payload, method_len, grpcReq->method);
 
-    // bpf_printk("grpc:server:handleStream: get the payload size is %d\n", e->payload_size);
+    // cw_bpf_debug("grpc:server:handleStream: get the payload size is %d\n", e->payload_size);
 
     struct apm_trace_info_t trace_info = cw_save_trace_info(id,pid, k.fd);
     
@@ -269,7 +269,7 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
 // Returns 0 on success, otherwise a negative error value in case of failure.
 // static __always_inline int writeStatus(struct pt_regs *ctx, void *status_ptr) {
 //     if (status_ptr == NULL) {
-//         bpf_printk("grpc:server:writeStatus: NULL status_ptr");
+//         cw_bpf_debug("grpc:server:writeStatus: NULL status_ptr");
 //         return -1;
 //     }
 
@@ -277,14 +277,14 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
 
 //     struct grpc_request_t *req_ptr = bpf_map_lookup_elem(&grpc_events, &key);
 //     if (req_ptr == NULL) {
-//         bpf_printk("grpc:server:handleStream: failed to lookup grpc request");
+//         cw_bpf_debug("grpc:server:handleStream: failed to lookup grpc request");
 //         return -2;
 //     }
 
 //     void *s_ptr = 0;
 //     long rc = bpf_probe_read_user(&s_ptr, sizeof(s_ptr), (void *)(status_ptr + status_s_pos));
 //     if (rc != 0) {
-//         bpf_printk("grpc:server:handleStream: failed to read Status.s");
+//         cw_bpf_debug("grpc:server:handleStream: failed to read Status.s");
 //         return -3;
 //     }
 
@@ -292,7 +292,7 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
 //     rc = bpf_probe_read_user(
 //         &req_ptr->status_code, sizeof(req_ptr->status_code), (void *)(s_ptr + status_code_pos));
 //     if (rc != 0) {
-//         bpf_printk("grpc:server:handleStream: failed to read status code");
+//         cw_bpf_debug("grpc:server:handleStream: failed to read status code");
 //         return -4;
 //     }
 //     req_ptr->has_status = true;
@@ -306,10 +306,10 @@ handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context)
 // This is only compatible with versions < 1.69.0 of the Server.
 SEC("uprobe/server_handleStream")
 int uprobe_server_handleStream(struct pt_regs *ctx) {
-    // bpf_printk("enter the uprobe_server_handleStream");
+    // cw_bpf_debug("enter the uprobe_server_handleStream");
     u64 stream_pos = 4;
     void *stream_ptr = get_argument(ctx, stream_pos);
-    // bpf_printk("enter uprobe_server_handleStream\n");
+    // cw_bpf_debug("enter uprobe_server_handleStream\n");
     // Get key
     __u64 pid_tgid = bpf_get_current_pid_tgid();
 	__u32 tgid = pid_tgid >> 32;
@@ -317,7 +317,7 @@ int uprobe_server_handleStream(struct pt_regs *ctx) {
 			bpf_map_lookup_elem(&proc_info_map, &tgid);
     if(!proc_info)
     {
-	    bpf_printk("[uprobe_HandlerFunc_ServeHTTP] no proc info");
+	    cw_bpf_debug("[uprobe_HandlerFunc_ServeHTTP] no proc info");
         return 0;
     }
     struct go_iface go_context = {0};
@@ -329,7 +329,7 @@ int uprobe_server_handleStream(struct pt_regs *ctx) {
 // UPROBE_RETURN(server_handleStream, struct grpc_request_t, grpc_events) 
 SEC("uprobe/server_handleStream")
 int uprobe_server_handleStream_Returns(struct pt_regs *ctx) {  
-    // bpf_printk("enter the uprobe_server_handleStream return");     
+    // cw_bpf_debug("enter the uprobe_server_handleStream return");     
     void *key = (void *)GOROUTINE(ctx);
     __u64 id = bpf_get_current_pid_tgid();
 	__u32 zero = 0;
@@ -339,7 +339,7 @@ int uprobe_server_handleStream_Returns(struct pt_regs *ctx) {
 
 	pid = id >> 32;
 	tid =  (__u32)id;
-    // bpf_printk("enter uprobe_server_handleStream_Returns\n");
+    // cw_bpf_debug("enter uprobe_server_handleStream_Returns\n");
 
     struct l7_request_key k = {};
     k.pid = pid;
@@ -348,7 +348,7 @@ int uprobe_server_handleStream_Returns(struct pt_regs *ctx) {
     k.stream_id = -1;    
     struct grpc_request_t *event = bpf_map_lookup_elem(&grpc_events, &key);
     if (event == NULL) {
-        bpf_printk("grpc:server:uprobe/server_handleStream2Return: event is NULL");
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2Return: event is NULL");
         return -5;
     }
     event->end_time = bpf_ktime_get_ns();
@@ -429,7 +429,7 @@ int uprobe_server_handleStream_Returns(struct pt_regs *ctx) {
     //不发送payload
     bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
 
-    // bpf_printk("stop get apm data\n");
+    // cw_bpf_debug("stop get apm data\n");
     return 0;                                                                                  
 }
 
@@ -447,12 +447,12 @@ int uprobe_server_handleStream2(struct pt_regs *ctx) {
 	if (!info) {
 		return -1;
 	}
-    bpf_printk("info->stream_ctx_pos is %d\n", info->stream_ctx_pos);
+    cw_bpf_debug("info->stream_ctx_pos is %d\n", info->stream_ctx_pos);
     u64 server_stream_pos = 4;
-    // bpf_printk("enter uprobe_server_handleStream2\n");
+    // cw_bpf_debug("enter uprobe_server_handleStream2\n");
     void *server_stream_ptr = get_argument(ctx, server_stream_pos);
     if (server_stream_ptr == NULL) {
-        bpf_printk("grpc:server:uprobe/server_handleStream2: failed to get ServerStream arg");
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to get ServerStream arg");
         return -1;
     }
 
@@ -460,7 +460,7 @@ int uprobe_server_handleStream2(struct pt_regs *ctx) {
     long rc = bpf_probe_read_user(
         &stream_ptr, sizeof(stream_ptr), (void *)(server_stream_ptr + server_stream_stream_pos));
     if (rc != 0) {
-        bpf_printk("grpc:server:uprobe/server_handleStream2: failed to read stream_ptr");
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to read stream_ptr");
         return -2;
     }
 
@@ -468,7 +468,7 @@ int uprobe_server_handleStream2(struct pt_regs *ctx) {
     rc = bpf_probe_read_user(
         &go_context.type, sizeof(go_context.type), (void *)(stream_ptr + info->stream_ctx_pos));
     if (rc != 0) {
-        bpf_printk("grpc:server:uprobe/server_handleStream2: failed to read context type");
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to read context type");
         return -3;
     }
 
@@ -476,7 +476,7 @@ int uprobe_server_handleStream2(struct pt_regs *ctx) {
                              sizeof(go_context.data),
                              get_go_interface_instance(stream_ptr + info->stream_ctx_pos));
     if (rc != 0) {
-        bpf_printk("grpc:server:uprobe/server_handleStream2: failed to read context data");
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to read context data");
         return -4;
     }
 
@@ -503,7 +503,7 @@ int uprobe_server_handleStream2_Returns(struct pt_regs *ctx) {
     long rc = bpf_probe_read_user(
         &stream_ptr, sizeof(stream_ptr), (void *)(server_stream_ptr + server_stream_stream_pos));
     if (rc != 0) {
-        bpf_printk("grpc:server:uprobe/server_handleStream2Return: failed to read stream_ptr");
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2Return: failed to read stream_ptr");
         return -2;
     }
 
@@ -511,7 +511,7 @@ lookup:
     key = (void *)GOROUTINE(ctx);
     struct grpc_request_t *event = bpf_map_lookup_elem(&grpc_events, &key);
     if (event == NULL) {
-        bpf_printk("grpc:server:uprobe/server_handleStream2Return: event is NULL");
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2Return: event is NULL");
         return -5;
     }
     event->end_time = bpf_ktime_get_ns();
@@ -527,7 +527,7 @@ lookup:
 
 	pid = id >> 32;
 	tid =  (__u32)id;
-    // bpf_printk("enter uprobe_server_handleStream_Returns\n");
+    // cw_bpf_debug("enter uprobe_server_handleStream_Returns\n");
 
     struct l7_request_key k = {};
     k.pid = pid;
@@ -608,7 +608,7 @@ lookup:
     //不发送payload
     bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
 
-    // bpf_printk("stop get apm data\n");
+    // cw_bpf_debug("stop get apm data\n");
     return 0;
 }
 
@@ -617,7 +617,7 @@ lookup:
 // func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeadersFrame, handle func(*Stream)) error
 SEC("uprobe/http2Server_operateHeader")
 int uprobe_http2Server_operateHeader(struct pt_regs *ctx) {
-    // bpf_printk("enter the uprobe_http2Server_operateHeader");
+    // cw_bpf_debug("enter the uprobe_http2Server_operateHeader");
     __u32 tgid = (__u32)(bpf_get_current_pid_tgid() >> 32);
 	struct ebpf_proc_info *info =
 		bpf_map_lookup_elem(&proc_info_map, &tgid);
@@ -633,7 +633,7 @@ int uprobe_http2Server_operateHeader(struct pt_regs *ctx) {
     char key[CW_HEADER_KEY_LENGTH + 1] = "cwtrace";
     // 确保字符串以 null 结尾
     key[CW_HEADER_KEY_LENGTH] = '\0';
-    // bpf_printk("enter the uprobe_http2Server_operateHeader\n");
+    // cw_bpf_debug("enter the uprobe_http2Server_operateHeader\n");
     
     __u32 zero = 0;
     struct apm_span_context *cw_parent_span_context = bpf_map_lookup_elem(&apm_span_context_heap3, &zero);
@@ -654,35 +654,35 @@ int uprobe_http2Server_operateHeader(struct pt_regs *ctx) {
         if (hf.name.len != CW_HEADER_KEY_LENGTH || hf.value.len != CW_HEADER_VAL_LENGTH) {
             continue;
         }
-        // bpf_printk("found traceparent header name is %s", hf.name.str);
-        // bpf_printk("found traceparent header value is %s", hf.value.str);
+        // cw_bpf_debug("found traceparent header name is %s", hf.name.str);
+        // cw_bpf_debug("found traceparent header value is %s", hf.value.str);
         
         char current_key[CW_HEADER_KEY_LENGTH + 1];
         if (bpf_probe_read(current_key, sizeof(current_key), hf.name.str) != 0) {
             continue;
         }
-        // bpf_printk("---found traceparent header name is %s", hf.name.str);
-        // bpf_printk("+++found traceparent header value is %s", hf.value.str);
+        // cw_bpf_debug("---found traceparent header name is %s", hf.name.str);
+        // cw_bpf_debug("+++found traceparent header value is %s", hf.value.str);
 
         current_key[CW_HEADER_KEY_LENGTH] = '\0';
 
-        // bpf_printk("---11111found cwtrace key is %s", key);
-        // bpf_printk("+++11111found cwtrace current_key is %s", current_key);
+        // cw_bpf_debug("---11111found cwtrace key is %s", key);
+        // cw_bpf_debug("+++11111found cwtrace current_key is %s", current_key);
         // 简化字符串比较
         // if (bpf_memcmp(key, current_key, 6) == 0) {
         if (current_key[0] == 'c' && current_key[1] == 'w' && current_key[2] == 't' && current_key[3] == 'r' && current_key[4] == 'a' && current_key[5] == 'c' && current_key[6] == 'e') {
             find_w3c = 1;
-            // bpf_printk("found traceparent header");
+            // cw_bpf_debug("found traceparent header");
             // 执行字符串到span context的转换
             cw_string_to_span_context(hf.value.str, cw_parent_span_context);
-            // bpf_printk("11111found traceparent header value is %s", hf.value.str);
+            // cw_bpf_debug("11111found traceparent header value is %s", hf.value.str);
             break; // 找到后立即退出
         }
     }
     if (find_w3c == 0)
     {
         generate_random_bytes(cw_parent_span_context->trace_id, TRACE_ID_SIZE);
-        bpf_printk("enter uprobe_http2Server_operateHeader, generate the traceid\n");
+        cw_bpf_debug("enter uprobe_http2Server_operateHeader, generate the traceid\n");
     }
     cw_save_parent_tracking_span(cw_parent_span_context);
     return 0;
@@ -694,7 +694,7 @@ int uprobe_http2Server_operateHeader(struct pt_regs *ctx) {
 // This is only compatible with versions > 1.40 and < 1.69.0 of the Server.
 // SEC("uprobe/http2Server_WriteStatus")
 // int uprobe_http2Server_WriteStatus(struct pt_regs *ctx) {
-//     // bpf_printk("enter uprobe_http2Server_WriteStatus\n");
+//     // cw_bpf_debug("enter uprobe_http2Server_WriteStatus\n");
 //     void *status_ptr = get_argument(ctx, 3);
 //     return writeStatus(ctx, status_ptr);
 // }
@@ -705,11 +705,11 @@ int uprobe_http2Server_operateHeader(struct pt_regs *ctx) {
 // // This is only compatible with versions > 1.69.0 of the Server.
 // SEC("uprobe/http2Server_WriteStatus2")
 // int uprobe_http2Server_WriteStatus2(struct pt_regs *ctx) {
-//     // bpf_printk("enter uprobe_http2Server_WriteStatus2\n");
+//     // cw_bpf_debug("enter uprobe_http2Server_WriteStatus2\n");
 //     u64 server_stream_pos = 2;
 //     void *server_stream_ptr = get_argument(ctx, server_stream_pos);
 //     if (server_stream_ptr == NULL) {
-//         bpf_printk("grpc:server:uprobe/http2Server_WriteStatus2: failed to get ServerStream arg");
+//         cw_bpf_debug("grpc:server:uprobe/http2Server_WriteStatus2: failed to get ServerStream arg");
 //         return -1;
 //     }
 
@@ -717,7 +717,7 @@ int uprobe_http2Server_operateHeader(struct pt_regs *ctx) {
 //     long rc = bpf_probe_read_user(
 //         &stream_ptr, sizeof(stream_ptr), (void *)(server_stream_ptr + server_stream_stream_pos));
 //     if (rc != 0) {
-//         bpf_printk("grpc:server:uprobe/http2Server_WriteStatus2: failed to read stream_ptr");
+//         cw_bpf_debug("grpc:server:uprobe/http2Server_WriteStatus2: failed to read stream_ptr");
 //         return -2;
 //     }
 

+ 63 - 5
ebpftracer/ebpf/utrace/go/net/server.probe.bpf.c

@@ -103,6 +103,12 @@ struct {
 	__uint(max_entries, 8);
 } header_keys_map SEC(".maps");
 
+struct {
+	__uint(type, BPF_MAP_TYPE_LRU_HASH);
+	__uint(key_size, sizeof(u64));
+	__uint(value_size, sizeof(char [CW_HEADER_VAL_LENGTH]));
+	__uint(max_entries, MAX_CONCURRENT);
+} http_server_context_headers SEC(".maps");
 
 // Injected in init
 // volatile const u64 ctx_ptr_pos;
@@ -118,7 +124,7 @@ struct {
 //}
 //}
 
-static __always_inline struct span_context *extract_context_from_req_headers(void *headers_ptr_ptr) {
+static __always_inline struct span_context *extract_context_from_req_headers_del(void *headers_ptr_ptr) {
 	void *headers_ptr;
 	long res;
 	res = bpf_probe_read(&headers_ptr, sizeof(headers_ptr), headers_ptr_ptr);
@@ -147,6 +153,14 @@ static __always_inline struct span_context *extract_context_from_req_headers(voi
     {
         return 0;
     }
+	
+	// Check if Go version >= 1.24 (Swiss Tables)
+	if (proc_info->version >= GO_VERSION(1, 24, 0)) {
+		// Swiss Tables uses dirPtr instead of buckets, structure is incompatible
+		// Skip header reading for Go 1.24+
+		return NULL;
+	}
+	
 	void *header_buckets;
 	res = bpf_probe_read(&header_buckets, sizeof(header_buckets), (void *) (headers_ptr + proc_info->buckets_ptr_pos));
 	if (res < 0) {
@@ -304,6 +318,17 @@ static __always_inline struct map_bucket *get_map_bucket(void *headers_ptr_ptr)
 
 MAP_BUCKET_DEFINITION(go_string_ot,  go_slice_ot)
 
+static __always_inline char *
+extract_context_from_req_headers_pre_parsed(void *key) {
+	char *header_val =
+			bpf_map_lookup_elem(&http_server_context_headers, &key);
+	if (!header_val) {
+		return NULL;
+	}
+
+	return header_val;
+}
+
 static __always_inline char *extract_context_from_req_headers_go_map(void *headers_ptr_ptr)
 {
 	void *headers_ptr;
@@ -407,6 +432,15 @@ static __always_inline char *extract_context_from_req_headers_go_map(void *heade
 	}
 	return NULL;
 }
+
+static __always_inline char *
+extract_context_from_req_headers(void *key, void *headers_ptr_ptr, __u64 use_swiss) {
+	if (use_swiss == 1) {
+		return extract_context_from_req_headers_pre_parsed(key);
+	}
+	return extract_context_from_req_headers_go_map(headers_ptr_ptr);
+}
+
 // 获取 header_val
 static __always_inline char *get_header_val(struct map_bucket *map_value,u32 off,u32 count) {
 	for (u32 i = off; i < count; i++) {
@@ -495,8 +529,10 @@ int uprobe_HandlerFunc_ServeHTTP(struct pt_regs *ctx) {
 	void *req_ptr = get_argument(ctx, 4);
 //	struct map_bucket * map_bucket_p = get_map_bucket((void *) (req_ptr + proc_info->headers_ptr_pos));
 
-	char *traceparent_header_value = extract_context_from_req_headers_go_map(
-			(void *) (req_ptr + proc_info->headers_ptr_pos));
+	char *traceparent_header_value = extract_context_from_req_headers(key,
+	                                                                  (void *) (req_ptr + proc_info->headers_ptr_pos),
+	                                                                  proc_info->use_swiss_map);
+
 //	bpf_printk("111 %s",traceparent_header_value);
 
 //	char * traceparent_header_value = get_header_val_off(map_bucket_p);
@@ -581,10 +617,11 @@ int uprobe_HandlerFunc_ServeHTTP_Returns(struct pt_regs *ctx) {
 	struct uprobe_data_t *uprobe_data = bpf_map_lookup_elem(&http_server_uprobes, &key);
 	if (uprobe_data == NULL) {
 		cw_bpf_debug("uprobe/HandlerFunc_ServeHTTP_Returns: entry_state is NULL");
+		bpf_map_delete_elem(&http_server_context_headers, &key);
 		return 0;
 	}
 	bpf_map_delete_elem(&http_server_uprobes, &key);
-
+	bpf_map_delete_elem(&http_server_context_headers, &key);
 	struct http_server_span_t *http_server_span = &uprobe_data->span;
 //	void *resp_ptr = (void *) uprobe_data->resp_ptr;
 //	void *req_ptr = NULL;
@@ -616,4 +653,25 @@ int uprobe_HandlerFunc_ServeHTTP_Returns(struct pt_regs *ctx) {
 	clear_parent_span_context();
 	cw_bpf_debug("HTTP_END");
 	return 0;
-}
+}
+
+// This instrumentation attaches uprobe to the following function:
+// func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) {
+SEC("uprobe/textproto_Reader_readContinuedLineSlice")
+int uprobe_textproto_Reader_readContinuedLineSlice_Returns(struct pt_regs *ctx) {
+	void *key = (void *) GOROUTINE(ctx);
+	u64 len = (u64) GO_PARAM2(ctx);
+	u8 *buf = (u8 *) GO_PARAM1(ctx);
+	if (len == (CW_HEADER_KEY_LENGTH + CW_HEADER_VAL_LENGTH + 2)) {
+		long cw_header_native = 0x3A65636172747763LL; // 小端序下的 "cwtrace:"
+		__u64 key64 = 0;
+		bpf_probe_read_user(&key64, sizeof(key64), buf);
+		if (key64 == cw_header_native) {
+			char header_val[CW_HEADER_VAL_LENGTH] = {};
+			bpf_probe_read_user(header_val, sizeof(header_val), buf + CW_HEADER_KEY_LENGTH + 2);
+			cw_bpf_debug("Swiss Map:%s", header_val);
+			bpf_map_update_elem(&http_server_context_headers, &key, &header_val, BPF_ANY);
+		}
+	}
+	return 0;
+}

+ 427 - 0
ebpftracer/l7/cassandra.go

@@ -0,0 +1,427 @@
+package l7
+
+import (
+	"encoding/binary"
+	"fmt"
+)
+
+const (
+	CassandraHeaderSize = 9 // version(1) + flags(1) + stream_id(2) + opcode(1) + length(4)
+
+	CassandraOpcodeQuery   = 0x07
+	CassandraOpcodeExecute = 0x0A
+	CassandraOpcodeBatch   = 0x0D
+	CassandraOpcodePrepare = 0x09
+	CassandraOpcodeClose   = 0x04
+)
+
+type CassandraParser struct {
+	preparedStatements map[string]string // statement_id (hex string) -> query
+	pendingPrepares    map[int16]string  // stream_id -> query (用于关联 PREPARE 请求和响应)
+	lastPrepareQuery   string            // 最近一次 PREPARE 的查询语句(备用方案)
+}
+
+func NewCassandraParser() *CassandraParser {
+	return &CassandraParser{
+		preparedStatements: make(map[string]string),
+		pendingPrepares:    make(map[int16]string),
+		lastPrepareQuery:   "",
+	}
+}
+
+// ParseCassandra 解析 Cassandra CQL 查询语句
+// 支持 QUERY、EXECUTE、BATCH、PREPARE 操作
+func ParseCassandra(payload []byte) string {
+	if len(payload) < CassandraHeaderSize {
+		return ""
+	}
+
+	// 读取 header
+	version := payload[0]
+	if version != 0x04 { // REQUEST_FRAME
+		return ""
+	}
+
+	opcode := payload[4]
+	bodyLength := int(binary.BigEndian.Uint32(payload[5:9]))
+
+	if len(payload) < CassandraHeaderSize+bodyLength {
+		// 数据不完整,但尝试解析
+		bodyLength = len(payload) - CassandraHeaderSize
+	}
+
+	body := payload[CassandraHeaderSize:]
+	if len(body) > bodyLength {
+		body = body[:bodyLength]
+	}
+
+	switch opcode {
+	case CassandraOpcodeQuery:
+		return parseQuery(body)
+	case CassandraOpcodeExecute:
+		return parseExecute(body)
+	case CassandraOpcodeBatch:
+		return parseBatch(body)
+	default:
+		return ""
+	}
+}
+
+// parseQuery 解析 QUERY 操作
+// QUERY 格式: query string (long string) + consistency (2 bytes) + flags (1 byte) + ...
+// long string 格式: 4 bytes (length, big-endian) + string bytes (NOT null-terminated)
+func parseQuery(body []byte) string {
+	if len(body) < 4 {
+		return ""
+	}
+
+	// 读取 long string 的长度(4 bytes, big-endian)
+	queryLen := int(binary.BigEndian.Uint32(body[0:4]))
+	if queryLen <= 0 {
+		return ""
+	}
+
+	// 检查是否有足够的数据
+	availableData := len(body) - 4
+	if queryLen > availableData {
+		// 数据被截断,只读取可用的部分
+		if availableData > 0 {
+			queryBytes := body[4 : 4+availableData]
+			// 尝试找到字符串的结束位置(可能是null terminator或有效字符串的末尾)
+			// 但Cassandra协议中long string不是null-terminated,所以直接读取
+			query := string(queryBytes)
+			// 检查是否包含可打印字符
+			if isPrintableString(queryBytes) {
+				return query + "..."
+			}
+			return ""
+		}
+		return ""
+	}
+
+	// 读取完整的查询字符串
+	queryBytes := body[4 : 4+queryLen]
+
+	// Cassandra的long string不是null-terminated,但某些实现可能会添加null terminator
+	// 检查并移除末尾的null terminator(如果存在)
+	if len(queryBytes) > 0 && queryBytes[len(queryBytes)-1] == 0 {
+		queryBytes = queryBytes[:len(queryBytes)-1]
+	}
+
+	// 验证是否为有效的字符串(包含可打印字符)
+	if !isPrintableString(queryBytes) {
+		return ""
+	}
+
+	query := string(queryBytes)
+	return query
+}
+
+// isPrintableString 检查字节数组是否包含可打印字符
+func isPrintableString(b []byte) bool {
+	if len(b) == 0 {
+		return false
+	}
+	printableCount := 0
+	for _, c := range b {
+		// 允许可打印ASCII字符、换行符、制表符等
+		if c >= 32 && c <= 126 || c == '\n' || c == '\r' || c == '\t' {
+			printableCount++
+		}
+	}
+	// 如果至少80%的字符是可打印的,认为是有效字符串
+	return printableCount*100/len(b) >= 80
+}
+
+// parseExecute 解析 EXECUTE 操作
+// EXECUTE 格式: statement id (short bytes) + ...
+func parseExecute(body []byte) string {
+	if len(body) < 2 {
+		return ""
+	}
+
+	// 读取 short bytes (2 bytes length + bytes)
+	stmtIdLen := int(binary.BigEndian.Uint16(body[0:2]))
+	if stmtIdLen < 0 || stmtIdLen > len(body)-2 {
+		return ""
+	}
+
+	if stmtIdLen == 0 {
+		return ""
+	}
+
+	stmtIdBytes := body[2 : 2+stmtIdLen]
+	stmtId := fmt.Sprintf("%x", stmtIdBytes)
+
+	return fmt.Sprintf("EXECUTE %s /* unknown */", stmtId)
+}
+
+// parseBatch 解析 BATCH 操作
+// BATCH 格式: batch type (1 byte) + queries count (2 bytes) + queries...
+func parseBatch(body []byte) string {
+	if len(body) < 3 {
+		return ""
+	}
+
+	batchType := body[0]
+	queriesCount := int(binary.BigEndian.Uint16(body[1:3]))
+
+	if queriesCount == 0 {
+		return "BATCH (empty)"
+	}
+
+	offset := 3
+	var queries []string
+
+	for i := 0; i < queriesCount && offset < len(body); i++ {
+		if offset >= len(body) {
+			break
+		}
+
+		// 每个查询: kind (1 byte) + query string 或 statement id
+		if offset+1 > len(body) {
+			break
+		}
+
+		kind := body[offset]
+		offset++
+
+		switch kind {
+		case 0: // QUERY
+			if offset+4 > len(body) {
+				break
+			}
+			queryLen := int(binary.BigEndian.Uint32(body[offset : offset+4]))
+			offset += 4
+			if queryLen > 0 && offset+queryLen <= len(body) {
+				queryBytes := body[offset : offset+queryLen]
+				// 移除null terminator(如果存在)
+				if len(queryBytes) > 0 && queryBytes[len(queryBytes)-1] == 0 {
+					queryBytes = queryBytes[:len(queryBytes)-1]
+				}
+				// 验证是否为有效字符串
+				if isPrintableString(queryBytes) {
+					queries = append(queries, string(queryBytes))
+				} else {
+					queries = append(queries, "<binary>")
+				}
+				offset += queryLen
+			} else if queryLen > 0 && offset < len(body) {
+				// 数据被截断
+				availableData := len(body) - offset
+				queryBytes := body[offset : offset+availableData]
+				if isPrintableString(queryBytes) {
+					queries = append(queries, string(queryBytes)+"...")
+				}
+				offset = len(body) // 到达末尾
+			}
+		case 1: // EXECUTE (prepared statement)
+			if offset+2 > len(body) {
+				break
+			}
+			stmtIdLen := int(binary.BigEndian.Uint16(body[offset : offset+2]))
+			offset += 2
+			if stmtIdLen > 0 && offset+stmtIdLen <= len(body) {
+				stmtIdBytes := body[offset : offset+stmtIdLen]
+				stmtId := fmt.Sprintf("%x", stmtIdBytes)
+				queries = append(queries, fmt.Sprintf("EXECUTE %s", stmtId))
+				offset += stmtIdLen
+			}
+		default:
+			// 跳过未知类型
+			break
+		}
+	}
+
+	batchTypeStr := "BATCH"
+	if batchType == 0 {
+		batchTypeStr = "BATCH LOGGED"
+	} else if batchType == 1 {
+		batchTypeStr = "BATCH UNLOGGED"
+	} else if batchType == 2 {
+		batchTypeStr = "BATCH COUNTER"
+	}
+
+	if len(queries) == 0 {
+		return batchTypeStr + " (empty)"
+	}
+
+	if len(queries) == 1 {
+		return fmt.Sprintf("%s: %s", batchTypeStr, queries[0])
+	}
+
+	return fmt.Sprintf("%s: [%d queries]", batchTypeStr, len(queries))
+}
+
+// Parse 方法用于支持预编译语句(需要维护状态)
+func (p *CassandraParser) Parse(payload []byte) string {
+	if len(payload) < CassandraHeaderSize {
+		return ""
+	}
+
+	version := payload[0]
+	if version != 0x04 {
+		return ""
+	}
+
+	// 提取 stream_id (在 payload[2:4] 位置,big-endian)
+	streamId := int16(binary.BigEndian.Uint16(payload[2:4]))
+	opcode := payload[4]
+	bodyLength := int(binary.BigEndian.Uint32(payload[5:9]))
+
+	if len(payload) < CassandraHeaderSize+bodyLength {
+		bodyLength = len(payload) - CassandraHeaderSize
+	}
+
+	body := payload[CassandraHeaderSize:]
+	if len(body) > bodyLength {
+		body = body[:bodyLength]
+	}
+
+	switch opcode {
+	case CassandraOpcodeQuery:
+		return parseQuery(body)
+	case CassandraOpcodeExecute:
+		return p.parseExecuteWithStatements(body)
+	case CassandraOpcodeBatch:
+		return parseBatch(body)
+	case CassandraOpcodePrepare:
+		return p.parsePrepare(body, streamId)
+	case CassandraOpcodeClose:
+		return p.parseClose(body)
+	default:
+		return ""
+	}
+}
+
+// parseExecuteWithStatements 解析 EXECUTE 操作,尝试从预编译语句映射中查找
+func (p *CassandraParser) parseExecuteWithStatements(body []byte) string {
+	if len(body) < 2 {
+		return ""
+	}
+
+	stmtIdLen := int(binary.BigEndian.Uint16(body[0:2]))
+	if stmtIdLen < 0 || stmtIdLen > len(body)-2 {
+		return ""
+	}
+
+	if stmtIdLen == 0 {
+		return ""
+	}
+
+	stmtIdBytes := body[2 : 2+stmtIdLen]
+	stmtId := fmt.Sprintf("%x", stmtIdBytes)
+
+	statement, ok := p.preparedStatements[stmtId]
+	if ok {
+		return fmt.Sprintf("EXECUTE: %s", statement)
+	}
+
+	// 如果找不到预编译语句,尝试使用最近一次 PREPARE 的查询语句(备用方案)
+	if p.lastPrepareQuery != "" {
+		// 使用 lastPrepareQuery,即使 statement id 不匹配
+		return fmt.Sprintf("EXECUTE: %s", p.lastPrepareQuery)
+	}
+
+	// 如果还是找不到,显示简化的 statement id
+	if len(stmtId) > 16 {
+		return fmt.Sprintf("EXECUTE [%s...]", stmtId[:16])
+	}
+	return fmt.Sprintf("EXECUTE [%s]", stmtId)
+}
+
+// parsePrepare 解析 PREPARE 操作,保存预编译语句
+func (p *CassandraParser) parsePrepare(body []byte, streamId int16) string {
+	query := parseQuery(body)
+	if query == "" {
+		return ""
+	}
+
+	// 保存查询语句和 stream_id 的映射,等待响应时关联 statement id
+	p.pendingPrepares[streamId] = query
+	// 同时保存为最近一次 PREPARE 的查询语句(备用方案)
+	p.lastPrepareQuery = query
+	return fmt.Sprintf("PREPARE: %s", query)
+}
+
+// parseClose 解析 CLOSE 操作,清除预编译语句
+func (p *CassandraParser) parseClose(body []byte) string {
+	if len(body) < 2 {
+		return ""
+	}
+
+	stmtIdLen := int(binary.BigEndian.Uint16(body[0:2]))
+	if stmtIdLen < 0 || stmtIdLen > len(body)-2 {
+		return ""
+	}
+
+	if stmtIdLen == 0 {
+		return ""
+	}
+
+	stmtIdBytes := body[2 : 2+stmtIdLen]
+	stmtId := fmt.Sprintf("%x", stmtIdBytes)
+
+	delete(p.preparedStatements, stmtId)
+	return fmt.Sprintf("CLOSE %s", stmtId)
+}
+
+// GetLastPrepareQuery 返回最近一次 PREPARE 的查询语句(用于调试)
+func (p *CassandraParser) GetLastPrepareQuery() string {
+	return p.lastPrepareQuery
+}
+
+// ParseResponse 解析 Cassandra 响应,用于关联 PREPARE 请求和响应
+// 响应格式: version(1) + flags(1) + stream_id(2) + opcode(1) + length(4) + body
+func (p *CassandraParser) ParseResponse(payload []byte) {
+	if len(payload) < CassandraHeaderSize {
+		return
+	}
+
+	version := payload[0]
+	if version != 0x84 { // RESPONSE_FRAME
+		return
+	}
+
+	// 提取 stream_id
+	streamId := int16(binary.BigEndian.Uint16(payload[2:4]))
+	opcode := payload[4]
+	bodyLength := int(binary.BigEndian.Uint32(payload[5:9]))
+
+	if len(payload) < CassandraHeaderSize+bodyLength {
+		bodyLength = len(payload) - CassandraHeaderSize
+	}
+
+	body := payload[CassandraHeaderSize:]
+	if len(body) > bodyLength {
+		body = body[:bodyLength]
+	}
+
+	// 只处理 RESULT 类型的响应(PREPARE 响应是 RESULT 类型)
+	if opcode == 0x08 { // CASSANDRA_OPCODE_RESULT
+		// 检查是否是 PREPARE 响应
+		// PREPARE 响应的 body 格式: result kind (4 bytes) + statement id (short bytes) + ...
+		if len(body) >= 4 {
+			resultKind := binary.BigEndian.Uint32(body[0:4])
+			// result kind = 0x0004 表示 PREPARED
+			if resultKind == 0x0004 {
+				// 提取 statement id
+				if len(body) >= 6 {
+					stmtIdLen := int(binary.BigEndian.Uint16(body[4:6]))
+					if stmtIdLen > 0 && len(body) >= 6+stmtIdLen {
+						stmtIdBytes := body[6 : 6+stmtIdLen]
+						stmtId := fmt.Sprintf("%x", stmtIdBytes)
+
+						// 从 pendingPrepares 中查找对应的查询语句
+						if query, ok := p.pendingPrepares[streamId]; ok {
+							// 关联 statement id 和查询语句
+							p.preparedStatements[stmtId] = query
+							// 删除临时映射
+							delete(p.pendingPrepares, streamId)
+						}
+					}
+				}
+			}
+		}
+	}
+}

+ 55 - 18
ebpftracer/l7/http.go

@@ -21,13 +21,43 @@ func ParseHttp(payload []byte) (string, string) {
 	return string(method), string(uri)
 }
 
-func ParseHttpHost(payload []byte, isTls bool) (string, string, string, uint16) {
+// parseHttpHeader 解析 HTTP 头部字段的值
+func parseHttpHeader(payload []byte, headerName string) string {
+	headerBytes := []byte(headerName)
+	headerIdx := bytes.Index(payload, headerBytes)
+	if headerIdx == -1 {
+		return ""
+	}
+
+	headerStart := headerIdx + len(headerBytes)
+	if headerStart >= len(payload) {
+		return ""
+	}
+
+	// 查找头部值的结束位置(\r\n)
+	headerEnd := bytes.Index(payload[headerStart:], []byte("\r\n"))
+	if headerEnd == -1 {
+		// 如果没有找到 \r\n,使用剩余数据的长度
+		headerEnd = len(payload) - headerStart
+	}
+
+	if headerEnd <= 0 {
+		return ""
+	}
+
+	headerValue := payload[headerStart : headerStart+headerEnd]
+	return string(bytes.TrimSpace(headerValue))
+}
+
+// parseHttpHostCommon 解析 HTTP 请求的公共逻辑,返回 method, requestURI, host, port, rest
+// rest 是解析完 method 和 uri 后剩余的 payload 部分,用于后续解析其他头部
+func parseHttpHostCommon(payload []byte, isTls bool) (string, string, string, uint16, []byte) {
 	method, rest, ok := bytes.Cut(payload, space)
 	if !ok {
-		return "", "", "", 0
+		return "", "", "", 0, nil
 	}
 	if !isHttpMethod(string(method)) {
-		return "", "", "", 0
+		return "", "", "", 0, nil
 	}
 
 	uri, rest, ok := bytes.Cut(rest, space)
@@ -35,21 +65,12 @@ func ParseHttpHost(payload []byte, isTls bool) (string, string, string, uint16)
 		uri = append(uri, []byte("...")...)
 	}
 
-	//hostStart := bytes.Index(rest, []byte("Host:")) + len("Host:")
-	hostHeader := "Host:"
-	hostIdx := bytes.Index(rest, []byte(hostHeader))
-	if hostIdx == -1 {
-		return string(method), string(uri), "", 0
-	}
-	hostStart := hostIdx + len(hostHeader)
-	hostEnd := bytes.Index(rest[hostStart:], []byte("\r\n"))
-	var hostPortBts []byte
-	if hostEnd == -1 {
-		hostPortBts = rest[hostStart:]
-	} else {
-		hostPortBts = rest[hostStart : hostStart+hostEnd]
+	// 解析 Host 头部
+	hostPort := parseHttpHeader(rest, "Host:")
+	if hostPort == "" {
+		return string(method), string(uri), "", 0, rest
 	}
-	hostPort := string(bytes.TrimSpace(hostPortBts))
+
 	hostParts := strings.Split(hostPort, ":")
 	host := hostParts[0]
 
@@ -69,5 +90,21 @@ func ParseHttpHost(payload []byte, isTls bool) (string, string, string, uint16)
 		}
 	}
 
-	return string(method), string(uri), host, port
+	return string(method), string(uri), host, port, rest
+}
+
+// ParseHttpHost 解析 HTTP 请求,返回 method, requestURI, host, port
+// 不解析 User-Agent,性能更优
+func ParseHttpHost(payload []byte, isTls bool) (string, string, string, uint16) {
+	method, uri, host, port, _ := parseHttpHostCommon(payload, isTls)
+	return method, uri, host, port
+}
+
+// ParseHttpHostWithUserAgent 解析 HTTP 请求,返回 method, requestURI, host, port, userAgent
+// 包含 User-Agent 解析,适用于需要 User-Agent 信息的场景
+func ParseHttpHostWithUserAgent(payload []byte, isTls bool) (string, string, string, uint16, string) {
+	method, uri, host, port, rest := parseHttpHostCommon(payload, isTls)
+	// 解析 User-Agent 头部
+	userAgent := parseHttpHeader(rest, "User-Agent:")
+	return method, uri, host, port, userAgent
 }

+ 1 - 1
ebpftracer/l7/l7.go

@@ -92,7 +92,7 @@ func (p Protocol) ServiceNameString() string {
 	case ProtocolKafka:
 		return "Kafka"
 	case ProtocolCassandra:
-		return "Cassandra"
+		return "CASSANDRA"
 	case ProtocolRabbitmq:
 		return "Rabbitmq"
 	case ProtocolNats:

+ 353 - 193
ebpftracer/tls.go

@@ -12,6 +12,7 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/proc"
@@ -29,6 +30,7 @@ const (
 	goExecute                      = "runtime.execute"
 	goNewproc1                     = "runtime.newproc1"
 	goRunqget                      = "runtime.runqget"
+	goGoready                      = "runtime.goready"
 	goServeHTTP                    = "net/http.serverHandler.ServeHTTP"
 	goTransport                    = "net/http.(*Transport).roundTrip"
 	goHeaderWriteSubset            = "net/http.Header.writeSubset"
@@ -38,6 +40,9 @@ const (
 	goGrpcClientConnInvoke         = "google.golang.org/grpc.(*ClientConn).Invoke"
 	goGrpcClientLoopyHeaderHandler = "google.golang.org/grpc/internal/transport.(*loopyWriter).headerHandler"
 	goGrpcHttp2ClientNewStream     = "google.golang.org/grpc/internal/transport.(*http2Client).NewStream"
+	goReadContinuedLineSlice       = "net/textproto.(*Reader).readContinuedLineSlice"
+	goGocqlSessionExecuteQuery     = "github.com/gocql/gocql.(*Session).executeQuery"
+	goGocqlSessionExecuteQueryV2   = "github.com/apache/cassandra-gocql-driver/v2.(*Session).executeQuery"
 )
 
 var (
@@ -141,6 +146,8 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 	appID := appInfo.AppIdHash.HashtVal
 	var err error
 	var name, version string
+	var major, minor, revision int
+
 	log := func(msg string, err error) {
 		if err != nil {
 			for _, s := range []string{"not a Go executable", "no such file or directory", "no such process", "permission denied"} {
@@ -182,7 +189,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 	symbols, err := ef.Symbols()
 	if err != nil {
 		if errors.Is(err, elf.ErrNoSymbols) {
-			log("no symbol section", nil)
+			log("no symbol section", err)
 			return nil, err
 		}
 		log("failed to read symbols", err)
@@ -191,7 +198,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 
 	textSection := ef.Section(".text")
 	if textSection == nil {
-		log("no text section", nil)
+		log("no text section", err)
 		return nil, err
 	}
 	textSectionData, err := textSection.Data()
@@ -248,12 +255,130 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 	}
 
 	offset, ok := tracer.GetOffset(tracer.NewID("std", "runtime", "g", "goid"), path)
+	if ok {
+		klog.Infof("[AttachGoTlsUprobes] STEP 11.1: Successfully got goid offset=%d", offset)
+	} else {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 11.2: Failed to get goid offset, pid=%d, version=%s", pid, bi.GoVersion)
+	}
+
+	// 获取 runtime.p.goidcache 偏移量
+	klog.Infof("[AttachGoTlsUprobes] STEP 11.3: Getting offset for runtime.p.goidcache")
+	goidcacheOffset, okGoidcache := tracer.GetOffset(tracer.NewID("std", "runtime", "p", "goidcache"), path)
+	if okGoidcache {
+		klog.Infof("[AttachGoTlsUprobes] STEP 11.4: Successfully got goidcache offset=%d", goidcacheOffset)
+	} else {
+		klog.Warnf("[AttachGoTlsUprobes] STEP 11.5: Failed to get goidcache offset, pid=%d, version=%s, using fallback", pid, bi.GoVersion)
+		goidcacheOffset = 384 // fallback value
+	}
+
+	// 获取 runtime.p.runnext 偏移量
+	klog.Infof("[AttachGoTlsUprobes] STEP 11.6: Getting offset for runtime.p.runnext")
+	runnextOffset, okRunnext := tracer.GetOffset(tracer.NewID("std", "runtime", "p", "runnext"), path)
+	if okRunnext {
+		klog.Infof("[AttachGoTlsUprobes] STEP 11.7: Successfully got runnext offset=%d", runnextOffset)
+	} else {
+		klog.Warnf("[AttachGoTlsUprobes] STEP 11.8: Failed to get runnext offset, pid=%d, version=%s, using fallback", pid, bi.GoVersion)
+		runnextOffset = 2456 // fallback value
+	}
+
+	klog.Infof("[AttachGoTlsUprobes] STEP 12: Getting offset for runtime.hmap.buckets")
 	bucketsOff, ok2 := tracer.GetOffset(tracer.NewID("std", "runtime", "hmap", "buckets"), path)
 
-	if ok && ok2 {
+	// Go 1.24+ 使用新的 map 实现(Swiss Tables),使用 internal/runtime/maps.Map 而不是 runtime.hmap
+	if !ok2 {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 12.2: Failed to get buckets offset, pid=%d, version=%s", pid, bi.GoVersion)
+		klog.Infof("[AttachGoTlsUprobes] STEP 12.3: Trying Swiss Tables maps.Map.dirPtr for Go 1.24+")
+
+		// Go 1.24+ Swiss Tables 使用 internal/runtime/maps.Map 结构体
+		// 结构体字段:used uint64, seed uintptr, dirPtr unsafe.Pointer (相当于旧的 buckets)
+		// 尝试获取 maps.Map.dirPtr 的偏移量
+		// 注意:DWARF 中的包路径可能是 "internal/runtime/maps" 或 "internal/runtime/maps.Map"
+		swissFields := []struct {
+			pkg        string
+			structName string
+			field      string
+		}{
+			{"internal/runtime/maps", "Map", "dirPtr"},
+			{"internal.runtime.maps", "Map", "dirPtr"},
+			{"maps", "Map", "dirPtr"},
+		}
+
+		for _, sf := range swissFields {
+			// 尝试不同的包路径格式
+			swissOff, swissOk := tracer.GetOffset(tracer.NewID("std", sf.pkg, sf.structName, sf.field), path)
+			if swissOk {
+				klog.Infof("[AttachGoTlsUprobes] STEP 12.4: Found Swiss Tables field '%s.%s.%s' with offset=%d", sf.pkg, sf.structName, sf.field, swissOff)
+				bucketsOff = swissOff
+				ok2 = true
+				break
+			} else {
+				klog.Debugf("[AttachGoTlsUprobes] STEP 12.4: Trying Swiss Tables field '%s.%s.%s' not found", sf.pkg, sf.structName, sf.field)
+			}
+		}
+
+		// 如果还是找不到,尝试旧的 hmap 字段作为备选
+		if !ok2 {
+			klog.Infof("[AttachGoTlsUprobes] STEP 12.5: Trying alternative hmap field names")
+			alternativeFields := []string{"table", "swiss", "swissTable", "buckets1", "oldbuckets", "bmap", "extra"}
+			for _, fieldName := range alternativeFields {
+				altOff, altOk := tracer.GetOffset(tracer.NewID("std", "runtime", "hmap", fieldName), path)
+				if altOk {
+					klog.Infof("[AttachGoTlsUprobes] STEP 12.6: Found alternative field '%s' with offset=%d", fieldName, altOff)
+					bucketsOff = altOff
+					ok2 = true
+					break
+				}
+			}
+		}
+
+		if !ok2 {
+			klog.Errorf("[AttachGoTlsUprobes] STEP 12.7: All attempts failed, Go 1.24+ Swiss Tables map structure not found")
+			// 根据源码分析,maps.Map 结构体布局(64-bit):
+			// - used uint64 (8 bytes, offset 0)
+			// - seed uintptr (8 bytes, offset 8)
+			// - dirPtr unsafe.Pointer (8 bytes, offset 16) <- 相当于旧的 buckets
+			// 如果 DWARF 查找失败,使用硬编码的偏移量作为 fallback
+			// 注意:这需要确认目标系统是 64-bit,且结构体对齐正确
+			klog.Warnf("[AttachGoTlsUprobes] STEP 12.8: Using hardcoded offset for maps.Map.dirPtr (offset=16 on 64-bit)")
+			klog.Warnf("[AttachGoTlsUprobes] STEP 12.9: This assumes: used(uint64@0) + seed(uintptr@8) + dirPtr(unsafe.Pointer@16)")
+
+			// 检查 Go 版本是否 >= 1.24
+			realVersion := strings.Replace(bi.GoVersion, "go", "", 1)
+			parts := strings.Split(realVersion, ".")
+			if len(parts) >= 2 {
+				major, _ = strconv.Atoi(parts[0])
+				minor, _ = strconv.Atoi(parts[1])
+				if major > 1 || (major == 1 && minor >= 24) {
+					// Go 1.24+ 使用 Swiss Tables,maps.Map.dirPtr 在 offset 16 (64-bit)
+					// 假设是 64-bit 系统(大多数生产环境)
+					bucketsOff = 16
+					ok2 = true
+					klog.Infof("[AttachGoTlsUprobes] STEP 12.10: Using hardcoded offset=%d for Go %s (Swiss Tables)", bucketsOff, bi.GoVersion)
+				} else {
+					klog.Errorf("[AttachGoTlsUprobes] STEP 12.11: Go version < 1.24 but buckets not found, this is unexpected")
+					bucketsOff = 0
+				}
+			} else {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 12.12: Failed to parse Go version: %s", bi.GoVersion)
+				bucketsOff = 0
+			}
+		}
+	} else {
+		klog.Infof("[AttachGoTlsUprobes] STEP 12.1: Successfully got buckets offset=%d", bucketsOff)
+	}
+
+	klog.Infof("[AttachGoTlsUprobes] STEP 13: Checking if both offsets are valid, goid_ok=%v, buckets_ok=%v", ok, ok2)
+	// Go 1.24+ 兼容:如果 goid 成功但 buckets 失败,仍然继续(但记录警告)
+	if ok {
+		if !ok2 {
+			klog.Warnf("[AttachGoTlsUprobes] STEP 13.0: buckets offset missing for Go 1.24+, but continuing with goid only")
+			// 对于 Go 1.24,可能需要调整后续逻辑,暂时允许继续
+		}
+		klog.Infof("[AttachGoTlsUprobes] STEP 13.1: Both offsets valid, proceeding with version encoding")
+		klog.Infof("[AttachGoTlsUprobes] STEP 14: Parsing Go version string")
 		realVersion := strings.Replace(bi.GoVersion, "go", "", 1)
+		klog.Infof("[AttachGoTlsUprobes] STEP 14.1: Real version string=%s", realVersion)
 		parts := strings.Split(realVersion, ".")
-		var major, minor, revision int
 		if len(parts) >= 2 {
 			major, err = strconv.Atoi(parts[0])
 			if err != nil {
@@ -272,24 +397,40 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 				}
 			}
 
+			klog.Infof("[AttachGoTlsUprobes]  Parsed version, major=%d, minor=%d, revision=%d", major, minor, revision)
 			goVersion := ((major & 0xFF) << 16) + ((minor & 0xFF) << 8) + min(revision, 255)
 
+			klog.Infof("[AttachGoTlsUprobes] Initializing EbpfProcInfo structure")
 			info := EbpfProcInfo{}
 			info.Version = uint32(goVersion)
 			info.Offsets[OFFSET_IDX_GOID_RUNTIME_G] = uint16(offset)
+			info.Offsets[OFFSET_IDX_P_GOIDCACHE] = uint16(goidcacheOffset)
+			info.Offsets[OFFSET_IDX_P_RUNNEXT] = uint16(runnextOffset)
 			info.NetTCPConnItab = uint64(0)
 			info.CryptoTLSConnItab = uint64(0)
 			info.CredentialsSyscallConnItab = uint64(0)
 			info.InstanceId = instanceID
 			info.AppId = appID
 			info.CodeType = codeType
+			if major == 1 && minor >= 24 {
+				info.UseSwissMap = uint64(1)
+			}
 			if grpcMajorVersion >= 1 && grpcMinorVersion >= 60 {
 				info.IsNewFramePos = 1
+				klog.Infof("[AttachGoTlsUprobes] Using new frame position for gRPC >= 1.60")
 			} else {
 				info.IsNewFramePos = 0
+				klog.Infof("[AttachGoTlsUprobes] Using old frame position for gRPC < 1.60")
 			}
 			// go
 			info.BucketsPtrPos = bucketsOff
+			if bucketsOff == 0 {
+				klog.Warnf("[AttachGoTlsUprobes] STEP 15.3: BucketsPtrPos=0 (Go 1.24+ may not use buckets field)")
+			} else {
+				klog.Infof("[AttachGoTlsUprobes] STEP 15.3: Basic info initialized, BucketsPtrPos=%d", bucketsOff)
+			}
+
+			klog.Infof("[AttachGoTlsUprobes] STEP 16: Getting offsets for HTTP and gRPC fields")
 			fields := map[*uint64]tracer.ID{
 				&info.MethodPtrPos:        tracer.NewID("std", "net/http", "Request", "Method"),
 				&info.UrlPtrPos:           tracer.NewID("std", "net/http", "Request", "URL"),
@@ -306,26 +447,37 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 				&info.IoWriterNPos:        tracer.NewID("std", "bufio", "Writer", "n"),
 			}
 
+			successCount := 0
+			failCount := 0
 			for field, id := range fields {
 				off, ok := tracer.GetOffset(id, path)
 				if !ok {
-					klog.Warnf("failed to get offset for ID: %v", id)
+					klog.Warnf("[AttachGoTlsUprobes] STEP 16.1: Failed to get offset for ID: %v (PkgPath=%s, Struct=%s, Field=%s)", id, id.PkgPath, id.Struct, id.Field)
+					failCount++
+				} else {
+					successCount++
+					klog.Debugf("[AttachGoTlsUprobes] STEP 16.2: Got offset for %s.%s.%s = %d", id.PkgPath, id.Struct, id.Field, off)
 				}
 				*field = off
 			}
+			klog.Infof("[AttachGoTlsUprobes]  Field offset collection completed, success=%d, failed=%d", successCount, failCount)
 
 			// 获取内存地址
 			if appInfo.GoProcCache.StartAddr == 0 && appInfo.GoProcCache.EndAddr == 0 {
+				klog.Infof("[AttachGoTlsUprobes] Cache empty, calling Allocate")
 				allocDetails, allocErr := tracer.Allocate(int(pid))
 				if allocErr != nil {
 					return nil, allocErr
 				}
 				if allocDetails != nil {
-					//info.StartAddr = allocDetails.StartAddr
-					//info.EndAddr = allocDetails.EndAddr
 					appInfo.GoProcCache.StartAddr = allocDetails.StartAddr
 					appInfo.GoProcCache.EndAddr = allocDetails.EndAddr
+					klog.Infof("[AttachGoTlsUprobes] Allocate succeeded, StartAddr=0x%x, EndAddr=0x%x", allocDetails.StartAddr, allocDetails.EndAddr)
+				} else {
+					klog.Warnf("[AttachGoTlsUprobes] Allocate returned nil")
 				}
+			} else {
+				klog.Infof("[AttachGoTlsUprobes] Using cached addresses, StartAddr=0x%x, EndAddr=0x%x", appInfo.GoProcCache.StartAddr, appInfo.GoProcCache.EndAddr)
 			}
 			info.StartAddr = appInfo.GoProcCache.StartAddr
 			info.EndAddr = appInfo.GoProcCache.EndAddr
@@ -335,26 +487,69 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 			klog.Debugln("goVersion", goVersion)
 			klog.WithField("pid", pid).Debugln("info.StartAddr", info.StartAddr)
 			klog.WithField("pid", pid).Debugln("info.EndAddr", info.EndAddr)
+
+			klog.Infof("[AttachGoTlsUprobes] Updating proc_info map")
 			_, err = tracer.UpdateProcInfoToMap(t.collection, pid, info)
 			if err != nil {
 				klog.Error("failed to update program info", err)
 				return nil, err
 			}
+			klog.Infof("[AttachGoTlsUprobes] Proc_info map updated successfully")
 			appInfo.EBPFProcInfo = &info
+		} else {
+			klog.Errorf("[AttachGoTlsUprobes] Skipping proc_info initialization due to missing offsets, goid_ok=%v, buckets_ok=%v", ok, ok2)
+			if !ok {
+				klog.Errorf("[AttachGoTlsUprobes] runtime.g.goid offset missing - this is critical!")
+			}
+			if !ok2 {
+				klog.Errorf("[AttachGoTlsUprobes] runtime.hmap.buckets offset missing - Go 1.24+ may use new map implementation")
+			}
 		}
 	}
 
+	klog.Infof("[AttachGoTlsUprobes] Starting symbol matching and uprobe attachment, total symbols=%d", len(symbols))
 	var links []link.Link
-	for _, s := range symbols {
+	matchedSymbols := 0
+	for i, s := range symbols {
 		if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 {
 			continue
 		}
 		switch s.Name {
 		case goTlsWriteSymbol, goTlsReadSymbol:
-		case goExecute, goNewproc1, goRunqget, goServeHTTP, goTransport, goHeaderWriteSubset, goGrpcHttp2OperateHeader, goGrpcServerHandleStream, goGrpcServerWritestatus, goGrpcClientConnInvoke, goGrpcClientLoopyHeaderHandler, goGrpcHttp2ClientNewStream:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.1: Matched TLS symbol: %s (index=%d)", s.Name, i)
+		case goExecute:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.2: Matched runtime.execute symbol (index=%d)", i)
+		case goNewproc1:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.3: Matched runtime.newproc1 symbol (index=%d)", i)
+		case goRunqget:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.4: Matched runtime.runqget symbol (index=%d)", i)
+		case goServeHTTP:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.6: Matched net/http.serverHandler.ServeHTTP symbol (index=%d)", i)
+		case goTransport, goHeaderWriteSubset:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.7: Matched net/http.Transport.roundTrip writeSubset symbol (index=%d)", i)
+		case goGrpcClientConnInvoke:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.8: Matched gRPC ClientConn.Invoke symbol (index=%d)", i)
+		case goGrpcHttp2OperateHeader, goGrpcServerHandleStream, goGrpcServerWritestatus, goGrpcClientLoopyHeaderHandler, goGrpcHttp2ClientNewStream:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.9: Matched gRPC symbol: %s (index=%d)", s.Name, i)
+		case goGocqlSessionExecuteQuery:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.10: Matched gocql Session.executeQuery symbol (index=%d)", i)
+		case goGocqlSessionExecuteQueryV2:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.11: Matched cassandra Session.executeQuery symbol (index=%d)", i)
+		case goReadContinuedLineSlice:
 		default:
 			continue
 		}
+		klog.Debugf("[AttachGoTlsUprobes] Processing symbol %s, Value=0x%x, Size=%d", s.Name, s.Value, s.Size)
 		address := s.Value
 		for _, p := range ef.Progs {
 			if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 {
@@ -370,125 +565,71 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 
 		switch s.Name {
 		case goExecute:
-			l, err := exe.Uprobe(s.Name, t.uprobes["runtime_execute"], &link.UprobeOptions{Address: address})
+			klog.Infof("[AttachGoTlsUprobes] STEP 20: Attaching uprobe for runtime.execute, address=0x%x", address)
+			l, err := attachUprobe(exe, s.Name, "runtime_execute", t.uprobes, address, "failed to attach write_enter uprobe", true)
 			if err != nil {
-				log("failed to attach write_enter uprobe", err)
 				klog.Infoln("runtime.execute no")
 				return nil, err
-			} else {
-				klog.Infoln("runtime.execute ok")
 			}
+			klog.Infof("[AttachGoTlsUprobes] STEP 20.2: Successfully attached runtime.execute uprobe")
+			klog.Infoln("runtime.execute ok")
 			links = append(links, l)
 
 		case goNewproc1:
-			l, err := exe.Uprobe(s.Name, t.uprobes["enter_runtime_newproc1"], &link.UprobeOptions{Address: address})
+			klog.Infof("[AttachGoTlsUprobes] STEP 21: Attaching uprobe for runtime.newproc1, address=0x%x", address)
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "enter_runtime_newproc1", "exit_runtime_newproc1", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach newproc1 uprobe", true)
 			if err != nil {
 				log("failed to attach newproc1 uprobe", err)
 				return nil, err
 			}
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			if sEnd > textSectionLen {
-				continue
-			}
-			sBytes := textSectionData[sStart:sEnd]
-			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
-			if len(returnOffsets) == 0 {
-				log("failed to attach enter_runtime_newproc1 uprobe", fmt.Errorf("no return offsets found"))
-				return nil, err
-			}
-			for _, offset := range returnOffsets {
-				l, err := exe.Uprobe(s.Name, t.uprobes["exit_runtime_newproc1"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
-				if err != nil {
-					log("failed to attach exit_runtime_newproc1 uprobe", err)
-					return nil, err
-				}
-				links = append(links, l)
-			}
+			links = append(links, retLinks...)
+			klog.Infof("[AttachGoTlsUprobes] STEP 21.2: Successfully attached enter_runtime_newproc1 uprobe")
 
 		case goRunqget:
-			l, err := exe.Uprobe(s.Name, t.uprobes["enter_runtime_runqget"], &link.UprobeOptions{Address: address})
+			l, err := attachUprobe(exe, s.Name, "enter_runtime_runqget", t.uprobes, address, "failed to attach goRunqget uprobe", true)
 			if err != nil {
 				log("failed to attach goRunqget uprobe", err)
 				return nil, err
 			}
 			links = append(links, l)
-			//sStart := s.Value - textSection.Addr
-			//sEnd := sStart + s.Size
-			//if sEnd > textSectionLen {
-			//	continue
-			//}
-			//sBytes := textSectionData[sStart:sEnd]
-			//returnOffsets := getReturnOffsets(ef.Machine, sBytes)
-			//if len(returnOffsets) == 0 {
-			//	log("failed to attach enter_runtime_newproc1 uprobe", fmt.Errorf("no return offsets found"))
-			//	return nil
-			//}
-			//for _, offset := range returnOffsets {
-			//	l, err := exe.Uprobe(s.Name, t.uprobes["exit_runtime_newproc1"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
-			//	if err != nil {
-			//		log("failed to attach exit_runtime_newproc1 uprobe", err)
-			//		return nil
-			//	}
-			//	links = append(links, l)
-			//}
+
+		//case goGoready:
+		//klog.Infof("[AttachGoTlsUprobes] STEP 22: Attaching uprobe for runtime.goready, address=0x%x", address)
+		//l, err := attachUprobe(exe, s.Name, "runtime_goready", t.uprobes, address, "failed to attach runtime.goready uprobe", true)
+		//if err != nil {
+		//	klog.Infoln("runtime.goready no")
+		//	return nil, err
+		//}
+		//klog.Infof("[AttachGoTlsUprobes] STEP 22.2: Successfully attached runtime.goready uprobe")
+		//klog.Infoln("runtime.goready ok")
+		//links = append(links, l)
 		case goGrpcClientConnInvoke:
-			// 根据 gRPC 版本选择相应的探针
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_ClientConn_Invoke"], &link.UprobeOptions{Address: address})
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "uprobe_ClientConn_Invoke", "uprobe_ClientConn_Invoke_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach uprobe_ClientConn_Invoke uprobe", true)
 			if err != nil {
-				klog.WithError(err).Errorf("failed to attach uprobe_ClientConn_Invoke uprobe")
-				continue
-			}
-			klog.Infoln("uprobe_ClientConn_Invoke ok")
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			if sEnd > textSectionLen {
-				continue
-			}
-			sBytes := textSectionData[sStart:sEnd]
-			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
-			if len(returnOffsets) == 0 {
-				err = fmt.Errorf("failed to attach uprobe_ClientConn_Invoke no return offsets found")
-				klog.Errorln(err)
 				return nil, err
 			}
-			for _, offset := range returnOffsets {
-				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_ClientConn_Invoke_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
-				if err != nil {
-					klog.WithError(err).Errorln(fmt.Errorf("failed to attach uprobe_ClientConn_Invoke_Returns uprobe"))
-					return nil, err
-				}
-				links = append(links, l)
+			if retLinks != nil {
+				klog.Infoln("uprobe_ClientConn_Invoke ok")
+				links = append(links, retLinks...)
 			}
 		case goGrpcClientLoopyHeaderHandler:
-			// 根据 gRPC 版本选择相应的探针
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_LoopyWriter_HeaderHandler"], &link.UprobeOptions{Address: address})
-			if err != nil {
-				klog.WithError(err).Errorf("failed to attach uprobe_LoopyWriter_HeaderHandler uprobe")
-				continue
+			l, err := attachUprobe(exe, s.Name, "uprobe_LoopyWriter_HeaderHandler", t.uprobes, address, "failed to attach uprobe_LoopyWriter_HeaderHandler uprobe", false)
+			if err == nil && l != nil {
+				klog.Infoln("uprobe_LoopyWriter_HeaderHandler ok")
+				links = append(links, l)
 			}
-			klog.Infoln("uprobe_LoopyWriter_HeaderHandler ok")
-			links = append(links, l)
 		case goGrpcHttp2ClientNewStream:
-			// 根据 gRPC 版本选择相应的探针
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_http2Client_NewStream"], &link.UprobeOptions{Address: address})
-			if err != nil {
-				klog.WithError(err).Errorf("failed to attach uprobe_http2Client_NewStream uprobe")
-				continue
+			l, err := attachUprobe(exe, s.Name, "uprobe_http2Client_NewStream", t.uprobes, address, "failed to attach uprobe_http2Client_NewStream uprobe", false)
+			if err == nil && l != nil {
+				klog.Infoln("uprobe_http2Client_NewStream ok")
+				links = append(links, l)
 			}
-			klog.Infoln("uprobe_http2Client_NewStream ok")
-			links = append(links, l)
 		case goGrpcHttp2OperateHeader:
-			// 根据 gRPC 版本选择相应的探针
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_http2Server_operateHeader"], &link.UprobeOptions{Address: address})
-			if err != nil {
-				klog.WithError(err).Errorf("failed to attach uprobe_http2Server_operateHeader uprobe")
-				continue
+			l, err := attachUprobe(exe, s.Name, "uprobe_http2Server_operateHeader", t.uprobes, address, "failed to attach uprobe_http2Server_operateHeader uprobe", false)
+			if err == nil && l != nil {
+				klog.Infoln("uprobe_http2Server_operateHeader ok")
+				links = append(links, l)
 			}
-			klog.Infoln("uprobe_http2Server_operateHeader ok")
-			links = append(links, l)
 		// case goGrpcServerWritestatus:
 		// 	// 根据 gRPC 版本选择相应的 WriteStatus 探针
 		// 	l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_http2Server_WriteStatus"], &link.UprobeOptions{Address: address})
@@ -500,114 +641,53 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 		case goGrpcServerHandleStream:
 			// 根据 gRPC 版本选择相应的探针
 			probeName := t.selectGRPCServerProbe(grpcMajorVersion, grpcMinorVersion)
-			l, err := exe.Uprobe(s.Name, t.uprobes[probeName], &link.UprobeOptions{Address: address})
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, probeName, "uprobe_server_handleStream_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, fmt.Sprintf("failed to attach %s uprobe", probeName), true)
 			if err != nil {
-				klog.WithError(err).Errorf("failed to attach %s uprobe", probeName)
-				continue
-			}
-			klog.Infof("%s ok (gRPC v%d.%d)", probeName, grpcMajorVersion, grpcMinorVersion)
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			klog.Infoln("google.golang.org/grpc.(*Server).handleStream ok----111111")
-			if sEnd > textSectionLen {
-				continue
-			}
-			klog.Infoln("google.golang.org/grpc.(*Server).handleStream ok----2222")
-			sBytes := textSectionData[sStart:sEnd]
-			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
-			if len(returnOffsets) == 0 {
-				err = fmt.Errorf("failed to attach uprobe_server_handleStream2 no return offsets found")
-				klog.Errorln(err)
 				return nil, err
 			}
-			for _, offset := range returnOffsets {
-				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_server_handleStream_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
-				if err != nil {
-					klog.WithError(err).Errorln(fmt.Errorf("failed to attach exit_runtime_newproc1 uprobe"))
-					return nil, err
-				}
+			if retLinks != nil {
+				klog.Infof("%s ok (gRPC v%d.%d)", probeName, grpcMajorVersion, grpcMinorVersion)
 				klog.Infoln("google.golang.org/grpc.(*Server).handleStream ok----")
-				links = append(links, l)
+				links = append(links, retLinks...)
 			}
 		case goServeHTTP:
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP"], &link.UprobeOptions{Address: address})
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "uprobe_HandlerFunc_ServeHTTP", "uprobe_HandlerFunc_ServeHTTP_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach uprobe_HandlerFunc_ServeHTTP uprobe", true)
 			if err != nil {
-				klog.WithError(err).Errorln("failed to attach uprobe_HandlerFunc_ServeHTTP uprobe")
-				continue
-			}
-			klog.Infoln("net/http.serverHandler.ServeHTTP ok")
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			if sEnd > textSectionLen {
-				continue
-			}
-			sBytes := textSectionData[sStart:sEnd]
-			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
-			if len(returnOffsets) == 0 {
-				err = fmt.Errorf("failed to attach uprobe_HandlerFunc_ServeHTTP no return offsets found")
-				klog.Errorln(err)
 				return nil, err
 			}
-			for _, offset := range returnOffsets {
-				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
-				if err != nil {
-					klog.WithError(err).Errorln(fmt.Errorf("failed to attach exit_runtime_newproc1 uprobe"))
-					return nil, err
-				}
-				links = append(links, l)
+			if retLinks != nil {
+				klog.Infoln("net/http.serverHandler.ServeHTTP ok")
+				links = append(links, retLinks...)
 			}
 
 		case goTransport:
 			if t.DisableE2ETracing() {
 				continue
 			}
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip"], &link.UprobeOptions{Address: address})
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "uprobe_Transport_roundTrip", "uprobe_Transport_roundTrip_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach write_enter uprobe", true)
 			if err != nil {
-				klog.WithError(err).Errorln(fmt.Errorf("failed to attach write_enter uprobe"))
-				continue
-			} else {
-			}
-			klog.Infoln("net/http.uprobe_Transport_roundTrip ok")
-
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			if sEnd > textSectionLen {
-				continue
-			}
-			sBytes := textSectionData[sStart:sEnd]
-			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
-			if len(returnOffsets) == 0 {
-				err = fmt.Errorf("failed to attach uprobe_Transport_roundTrip uprobe no return offsets found")
-				klog.Errorln(err)
 				return nil, err
 			}
-			for _, offset := range returnOffsets {
-				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
-				if err != nil {
-					klog.WithError(err).Errorln("failed to attach exit_runtime_newproc1 uprobe")
-					return nil, err
-				}
-				links = append(links, l)
+			if retLinks != nil {
+				klog.Infoln("net/http.uprobe_Transport_roundTrip ok")
+				links = append(links, retLinks...)
 			}
 
 		case goHeaderWriteSubset:
 			if t.DisableE2ETracing() {
 				continue
 			}
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_writeSubset"], &link.UprobeOptions{Address: address})
+			l, err := attachUprobe(exe, s.Name, "uprobe_writeSubset", t.uprobes, address, "failed to attach write_enter uprobe", true)
 			if err != nil {
-				klog.WithError(err).Errorln("failed to attach uprobe_writeSubset uprobe")
-				continue
+				klog.WithError(err).Errorln("failed to attach uprobe_writeSubset")
+				return nil, err
 			}
 			klog.Infoln("net/http.Header.writeSubset ok")
 			links = append(links, l)
 
 		case goTlsWriteSymbol:
 			klog.Infoln("fucktls goTlsWriteSymbol crypto/tls uprobes attached")
-			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_write_enter"], &link.UprobeOptions{Address: address})
+			l, err := attachUprobe(exe, s.Name, "go_crypto_tls_write_enter", t.uprobes, address, "failed to attach write_enter uprobe", true)
 			if err != nil {
 				klog.WithError(err).Errorln("failed to attach write_enter uprobe")
 				return nil, err
@@ -616,37 +696,50 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 
 		case goTlsReadSymbol:
 			klog.Infoln("fucktls goTlsReadSymbol crypto/tls uprobes attached")
-			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_enter"], &link.UprobeOptions{Address: address})
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "go_crypto_tls_read_enter", "go_crypto_tls_read_exit", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach read_enter uprobe", true)
 			if err != nil {
 				klog.WithError(err).Errorln("failed to attach read_enter uprobe")
 				return nil, err
 			}
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			if sEnd > textSectionLen {
-				continue
+			if retLinks != nil {
+				links = append(links, retLinks...)
 			}
-			sBytes := textSectionData[sStart:sEnd]
-			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
-			if len(returnOffsets) == 0 {
-				err = fmt.Errorf("failed to attach read_exit uprobe no return offsets found")
-				klog.Errorln(err)
-				return nil, err
-			}
-			for _, offset := range returnOffsets {
-				l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_exit"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
+		case goReadContinuedLineSlice:
+			if major == 1 && minor >= 24 {
+				retLinks, err := attachUprobeWithReturns(exe, s.Name, "", "uprobe_textproto_Reader_readContinuedLineSlice_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach read_exit uprobe", true)
 				if err != nil {
-					klog.WithError(err).Errorln("failed to attach read_exit uprobe")
 					return nil, err
 				}
-				links = append(links, l)
+				if retLinks != nil {
+					links = append(links, retLinks...)
+				}
+			}
+		case goGocqlSessionExecuteQuery:
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "uprobe_Session_executeQuery", "uprobe_Session_executeQuery_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach uprobe_Session_executeQuery uprobe", true)
+			if err != nil {
+				return nil, err
+			}
+			if retLinks != nil {
+				klog.Infoln("uprobe_Session_executeQuery ok")
+				links = append(links, retLinks...)
+			}
+		case goGocqlSessionExecuteQueryV2:
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "uprobe_Session_executeQuery_cassandra", "uprobe_Session_executeQuery_cassandra_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach uprobe_Session_executeQuery_cassandra uprobe", true)
+			if err != nil {
+				return nil, err
+			}
+			if retLinks != nil {
+				klog.Infoln("uprobe_Session_executeQuery_cassandra ok")
+				links = append(links, retLinks...)
 			}
 		}
 	}
+	klog.Infof("[AttachGoTlsUprobes] Symbol processing completed, matched symbols=%d, total links=%d", matchedSymbols, len(links))
 	if len(links) == 0 {
+		klog.Errorf("[AttachGoTlsUprobes] No uprobes attached, returning error")
 		return nil, err
 	}
+	klog.Infof("[AttachGoTlsUprobes] Function completed successfully, attached %d uprobes", len(links))
 	klog.Infoln("crypto/tls uprobes attached")
 	return links, nil
 }
@@ -731,6 +824,73 @@ func (t *Tracer) selectGRPCServerProbe(major, minor int) string {
 	}
 }
 
+// attachUprobe 附加单个 uprobe,处理错误
+func attachUprobe(exe *link.Executable, symbolName string, probeName string, uprobes map[string]*ebpf.Program, address uint64, onError string, returnOnError bool) (link.Link, error) {
+	l, err := exe.Uprobe(symbolName, uprobes[probeName], &link.UprobeOptions{Address: address})
+	if err != nil {
+		if returnOnError {
+			klog.WithError(err).Errorf("failed to attach %s uprobe: %s", probeName, onError)
+			return nil, err
+		} else {
+			klog.WithError(err).Errorf("failed to attach %s uprobe: %s", probeName, onError)
+			return nil, nil
+		}
+	}
+	return l, nil
+}
+
+// attachUprobeWithReturns 附加 uprobe 并附加返回探针
+// enterProbeName 为空字符串时,只附加返回探针
+func attachUprobeWithReturns(exe *link.Executable, symbolName string, enterProbeName, returnProbeName string, uprobes map[string]*ebpf.Program, address uint64, s elf.Symbol, textSection *elf.Section, textSectionLen uint64, textSectionData []byte, machine elf.Machine, onError string, returnOnError bool) ([]link.Link, error) {
+	var links []link.Link
+
+	// 附加入口探针(如果提供了入口探针名称)
+	if enterProbeName != "" {
+		l, err := attachUprobe(exe, symbolName, enterProbeName, uprobes, address, onError, returnOnError)
+		if err != nil {
+			return nil, err
+		}
+		if l == nil {
+			return nil, nil
+		}
+		links = append(links, l)
+	}
+
+	// 计算符号在 text section 中的位置
+	sStart := s.Value - textSection.Addr
+	sEnd := sStart + s.Size
+	if sEnd > textSectionLen {
+		return links, nil
+	}
+
+	// 读取符号字节码
+	sBytes := textSectionData[sStart:sEnd]
+	returnOffsets := getReturnOffsets(machine, sBytes)
+	if len(returnOffsets) == 0 {
+		if returnOnError {
+			err := fmt.Errorf("failed to attach %s: no return offsets found", returnProbeName)
+			klog.Errorln(err)
+			return nil, err
+		}
+		return links, nil
+	}
+
+	// 为每个返回点附加探针
+	for _, offset := range returnOffsets {
+		l, err := exe.Uprobe(symbolName, uprobes[returnProbeName], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
+		if err != nil {
+			if returnOnError {
+				klog.WithError(err).Errorf("failed to attach %s uprobe", returnProbeName)
+				return nil, err
+			}
+			continue
+		}
+		links = append(links, l)
+	}
+
+	return links, nil
+}
+
 func getReturnOffsets(machine elf.Machine, instructions []byte) []int {
 	var res []int
 	switch machine {

+ 14 - 11
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/apm_exporter.go

@@ -87,6 +87,7 @@ type RootDataT struct {
 	VipIds          []interface{} `json:"vip_ids"`
 	SrcAddr         string        `json:"src_addr"`
 	DestinationAddr string        `json:"destination_addr"`
+	UserAgent       string        `json:"user_agent"`
 	// op 新增字段
 	Pid         uint32 `json:"pid"`
 	ContainerID string `json:"container_id"`
@@ -191,8 +192,8 @@ func tracetransformData(sdl []tracesdk.ReadOnlySpan) map[int][]RootDataT {
 					buildDNSMapEvent(&mNode, event)
 				case l7.ProtocolMysql, l7.ProtocolMariaDB, l7.ProtocolPostgres, l7.ProtocolDM:
 					buildSQLMapEvent(&mNode, event)
-				case l7.ProtocolRedis:
-					buildRedisMapEvent(&mNode, event)
+				case l7.ProtocolRedis, l7.ProtocolMemcached, l7.ProtocolCassandra:
+					buildNoSqlMapEvent(&mNode, event)
 				case l7.ProtocolMongo:
 					buildMongoMapEvent(&mNode, event)
 				case l7.ProtocolGrpc:
@@ -467,6 +468,7 @@ func initRootDataFromEvent() RootDataT {
 		DestinationAddr: "",
 		Sys:             sysTag,
 		SystemUUID:      systemUUID,
+		UserAgent:       "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
 	}
 	return data
 }
@@ -791,6 +793,8 @@ func buildAppMapFromEvent(traceRoot *RootDataT, sd apmTraceSpan) int {
 			traceRoot.Pid = uint32(attr.Value.AsInt64())
 		case "server.container_id":
 			traceRoot.ContainerID = attr.Value.AsString()
+		case "server.user_agent":
+			traceRoot.UserAgent = attr.Value.AsString()
 		}
 	}
 	traceRoot.Maps = append(traceRoot.Maps, mNode)
@@ -1096,13 +1100,10 @@ func buildDMMapEvent(mNode *MapInfoT, event tracesdk.Event) {
 	}
 }
 
-func buildRedisMapEvent(mNode *MapInfoT, event tracesdk.Event) {
-	mNode.ServiceName = REDIS_SERVICE_NAME
+func buildNoSqlMapEvent(mNode *MapInfoT, event tracesdk.Event) {
+	mNode.ServiceName = l7.Protocol(event.ProtocolType).ServiceNameString()
 	mNode.ServiceType = NOSQL_SERVICE_TYPE
-	//mNode.MethodName = span(sd).Name + " query"
-	//mNode.MethodName = "redis.Do()"
 	for _, attr := range event.Attributes {
-		//fmt.Println(attr.Key, ":", attr.Value.AsInterface())
 		switch attr.Key {
 		case "net.peer.name":
 			mNode.Ip = attr.Value.AsString()
@@ -1112,14 +1113,16 @@ func buildRedisMapEvent(mNode *MapInfoT, event tracesdk.Event) {
 			query := attr.Value.AsString()
 			mNode.MethodName = query
 			mNode.Ps = []string{query}
-			//words := strings.Fields(query)
-			//if len(words) > 0 {
-			//	mNode.OperType = strings.ToUpper(words[0])
-			//}
 		case "nosql.src_addr":
 			mNode.SrcAddr = attr.Value.AsString()
 		case "nosql.destination_addr":
 			mNode.DestinationAddr = attr.Value.AsString()
+		case "nosql.exception":
+			if attr.Value.AsBool() {
+				mNode.Exception = 1
+			}
+		case "nosql.exception_msg":
+			mNode.ExceptionMsg = attr.Value.AsString()
 		}
 	}
 }

+ 53 - 45
tracing/apm_tracing.go

@@ -3,7 +3,6 @@ package tracing
 import (
 	"context"
 	"fmt"
-	klog "github.com/sirupsen/logrus"
 	"os"
 	"strconv"
 	"strings"
@@ -11,6 +10,8 @@ import (
 	"sync/atomic"
 	"time"
 
+	klog "github.com/sirupsen/logrus"
+
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/utils"
@@ -129,7 +130,7 @@ func (t *Trace) AllEventReady(traceID uint64) bool {
 	return t.startEventReady && t.endEventReady && *t.currenEventCount >= t.needEventCount
 }
 
-func (t *Trace) GrpcServerTraceStartEvent(sn string, sport uint16,r *l7.RequestData, appInfo AppInfo, container_id string) {
+func (t *Trace) GrpcServerTraceStartEvent(sn string, sport uint16, r *l7.RequestData, appInfo AppInfo, container_id string) {
 	t.span.SetAttributes(attribute.String("rpc.uri", string(r.Payload)))
 	t.commonAttrs = []attribute.KeyValue{
 		semconv.NetPeerName(sn),
@@ -149,7 +150,7 @@ func (t *Trace) GrpcServerTraceStartEvent(sn string, sport uint16,r *l7.RequestD
 	t.startReady()
 }
 
-func (t *Trace) TraceStartEvent(method, path, sn string, sport uint16, status l7.Status, addr netaddr.IPPort, pid uint32, appInfo AppInfo, container_id string) {
+func (t *Trace) TraceStartEvent(method, path, sn, ua string, sport uint16, status l7.Status, addr netaddr.IPPort, pid uint32, appInfo AppInfo, container_id string) {
 	t.span.SetAttributes(semconv.HTTPURL(fmt.Sprintf("http://%s:%d%s", sn, sport, path)),
 		semconv.HTTPMethod(method),
 		attribute.String("http.uri", path))
@@ -170,6 +171,7 @@ func (t *Trace) TraceStartEvent(method, path, sn string, sport uint16, status l7
 		attribute.Int64("server.instance_id", appInfo.InstanceIdHash.IntVal),
 		attribute.Int("server.pid", int(pid)),
 		attribute.String("server.container_id", container_id),
+		attribute.String("server.user_agent", ua),
 	}
 	t.span.SetAttributes(t.commonAttrs...)
 	t.startReady()
@@ -326,11 +328,15 @@ func (t *Trace) GetSpan() trace.Span {
 	return t.span
 }
 
-func (t *Trace) createTraceEvent(name string, eventType int, l7Type int, attrs ...attribute.KeyValue) {
-	t.span.AddEventApm(name, eventType, l7Type, trace.WithAttributes(attrs...))
+func (t *Trace) createTraceEvent(protocol l7.Protocol, eventType int, attrs ...attribute.KeyValue) {
+	t.span.AddEventApm(protocol.String(), eventType, protocol.Int(), trace.WithAttributes(attrs...))
 	//atomic.AddUint32(t.currenEventCount, 1)
 }
 
+func (t *Trace) createFuncTraceEvent(name string, eventType int, attrs ...attribute.KeyValue) {
+	t.span.AddEventApm(name, eventType, 0, trace.WithAttributes(attrs...))
+}
+
 func (t *Trace) addEvent() {
 	atomic.AddUint32(t.currenEventCount, 1)
 }
@@ -443,7 +449,7 @@ func (t *Trace) SQLTraceQueryEvent(l7Type l7.Protocol, semconvVal attribute.KeyV
 
 	// 拼接 destination 的 IP 和端口为 ip:port 格式
 	destAddr := fmt.Sprintf("%s:%d", destination.IP().String(), destination.Port())
-	
+
 	var attr []attribute.KeyValue
 	attr = append(attr,
 		semconvVal,
@@ -464,7 +470,7 @@ func (t *Trace) SQLTraceQueryEvent(l7Type l7.Protocol, semconvVal attribute.KeyV
 			attribute.String("sql.exception_msg", r.ErrorMsg),
 		)
 	}
-	t.createTraceEvent(l7Type.String(), ebpftracer.EventTypeL7Request.Int(), l7Type.Int(), attr...)
+	t.createTraceEvent(l7Type, ebpftracer.EventTypeL7Request.Int(), attr...)
 }
 
 //func (t *Trace) MysqlTraceQueryEvent(query string, r *l7.RequestData, destination netaddr.IPPort) {
@@ -561,31 +567,54 @@ func (t *Trace) SQLTraceQueryEvent(l7Type l7.Protocol, semconvVal attribute.KeyV
 //	)
 //}
 
-func (t *Trace) RedisTraceQueryEvent(cmd, args string, r *l7.RequestData, destination netaddr.IPPort) {
+func (t *Trace) NoSQLTraceQueryEvent(l7Type l7.Protocol, semconvVal attribute.KeyValue, operation, statement string, r *l7.RequestData, connSrc, connDest netaddr.IPPort) {
 	if t == nil {
 		return
 	}
 	t.addEvent()
-	if cmd == "" {
+	if statement == "" {
 		return
 	}
-	statement := cmd
-	if args != "" {
-		statement += " " + args
+
+	// 拼接 destination 的 IP 和端口为 ip:port 格式
+
+	srcAddr := r.ComponentSAddr.String()
+	if r.ComponentSAddr.Port() == 0 {
+		srcAddr = connSrc.String()
+	}
+
+	destinationAddr := r.ComponentDAddr.String()
+	if r.ComponentDAddr.Port() == 0 {
+		destinationAddr = connDest.String()
 	}
 
 	var attr []attribute.KeyValue
 	attr = append(attr,
-		semconv.DBSystemRedis,
-		semconv.DBOperation(cmd),
+		semconvVal,
 		semconv.DBStatement(statement),
-		semconv.NetPeerName(destination.IP().String()),
-		semconv.NetPeerPort(int(destination.Port())),
-		attribute.String("nosql.src_addr", r.ComponentSAddr.String()),
-		attribute.String("nosql.destination_addr", r.ComponentDAddr.String()),
+		semconv.NetPeerName(connDest.IP().String()),
+		semconv.NetPeerPort(int(connDest.Port())),
+		attribute.String("nosql.src_addr", srcAddr),
+		attribute.String("nosql.destination_addr", destinationAddr),
+		attribute.String("nosql.dbn", connDest.String()),
 	)
+
+	// 如果 operation 不为空,添加 DBOperation 属性
+	if operation != "" {
+		attr = append(attr, semconv.DBOperation(operation))
+	}
+
 	t.appendTimestamp(&attr, r.StartAt, r.EndAt, r.Duration.Nanoseconds())
-	t.createTraceEvent(l7.ProtocolRedis.String(), ebpftracer.EventTypeL7Request.Int(), l7.ProtocolRedis.Int(), attr...)
+
+	attr = append(attr,
+		attribute.Bool("nosql.exception", r.Status.Error()),
+	)
+	if r.Status.Error() {
+		attr = append(attr,
+			attribute.String("nosql.exception_msg", r.ErrorMsg),
+		)
+	}
+	t.createTraceEvent(l7Type, ebpftracer.EventTypeL7Request.Int(), attr...)
 }
 
 func (t *Trace) GrpcServerTraceQueryEvent(r *l7.RequestData, appInfo AppInfo) {
@@ -616,7 +645,7 @@ func (t *Trace) GrpcServerTraceQueryEvent(r *l7.RequestData, appInfo AppInfo) {
 	)
 
 	t.appendTimestamp(&attr, r.StartAt, r.EndAt, r.Duration.Nanoseconds())
-	t.createTraceEvent(l7.ProtocolGrpc.String(), ebpftracer.EventTypeL7Request.Int(), l7.ProtocolGrpc.Int(), attr...)
+	t.createTraceEvent(l7.ProtocolGrpc, ebpftracer.EventTypeL7Request.Int(), attr...)
 }
 
 func (t *Trace) GrpcClientTraceQueryEvent(r *l7.RequestData) {
@@ -683,28 +712,7 @@ func (t *Trace) GrpcClientTraceQueryEvent(r *l7.RequestData) {
 	)
 
 	t.appendTimestamp(&attr, r.StartAt, r.EndAt, r.Duration.Nanoseconds())
-	t.createTraceEvent(l7.ProtocolGrpc.String(), ebpftracer.EventTypeL7Request.Int(), l7.ProtocolGrpc.Int(), attr...)
-}
-func (t *Trace) MongoTraceQueryEvent(query string, r *l7.RequestData, destination netaddr.IPPort) {
-	if t == nil {
-		return
-	}
-	t.addEvent()
-	if query == "" {
-		return
-	}
-
-	var attr []attribute.KeyValue
-	attr = append(attr,
-		semconv.DBSystemMongoDB,
-		semconv.DBStatement(query),
-		semconv.NetPeerName(destination.IP().String()),
-		semconv.NetPeerPort(int(destination.Port())),
-		attribute.String("nosql.src_addr", r.ComponentSAddr.String()),
-		attribute.String("nosql.destination_addr", r.ComponentDAddr.String()),
-	)
-	t.appendTimestamp(&attr, r.StartAt, r.EndAt, r.Duration.Nanoseconds())
-	t.createTraceEvent(l7.ProtocolMongo.String(), ebpftracer.EventTypeL7Request.Int(), l7.ProtocolMongo.Int(), attr...)
+	t.createTraceEvent(l7.ProtocolGrpc, ebpftracer.EventTypeL7Request.Int(), attr...)
 }
 
 func (t *Trace) DNSTraceQueryEvent(r *l7.RequestData, _type string, fqdn string, ttl uint32, ips []netaddr.IP) {
@@ -725,7 +733,7 @@ func (t *Trace) DNSTraceQueryEvent(r *l7.RequestData, _type string, fqdn string,
 		attribute.String("dns.ips", ip),
 	)
 	t.appendTimestamp(&attr, r.StartAt, r.EndAt, r.Duration.Nanoseconds())
-	t.createTraceEvent(l7.ProtocolDNS.String(), ebpftracer.EventTypeL7Request.Int(), l7.ProtocolDNS.Int(), attr...)
+	t.createTraceEvent(l7.ProtocolDNS, ebpftracer.EventTypeL7Request.Int(), attr...)
 }
 
 func (t *Trace) HttpTraceRequest(method, path, ip string, port uint16, r *l7.RequestData) {
@@ -780,7 +788,7 @@ func (t *Trace) HttpTraceRequestEvent(method, path, ip string, port uint16, r *l
 		attribute.Bool("http.is_tls", r.IsTls),
 	)
 	t.appendTimestamp(&attr, r.StartAt, r.EndAt, r.Duration.Nanoseconds())
-	t.createTraceEvent(l7.ProtocolHTTP.String(), ebpftracer.EventTypeL7Request.Int(), l7.ProtocolHTTP.Int(), attr...)
+	t.createTraceEvent(l7.ProtocolHTTP, ebpftracer.EventTypeL7Request.Int(), attr...)
 }
 
 func (t *Trace) FuncTraceQuery(funcname string, duration time.Duration, start uint64, end uint64) {
@@ -829,5 +837,5 @@ func (t *Trace) createTraceSpanNoTime2(name string, duration time.Duration, erro
 	//})
 	var attr []attribute.KeyValue
 	t.appendTimestamp(&attr, start, end, int64(end-start))
-	t.createTraceEvent(name, ebpftracer.EventTypeFunEnt.Int(), 0, attr...)
+	t.createFuncTraceEvent(name, ebpftracer.EventTypeFunEnt.Int(), attr...)
 }

+ 5 - 1
utils/modelse/bpf_struct.go

@@ -134,6 +134,8 @@ const (
 	OFFSET_IDX_FIELDS_HTTP2_META_HEADERS_FRAME
 	OFFSET_IDX_STREAM_HTTP2_CLIENT_CONN
 	OFFSET_IDX_STREAM_ID_HTTP2_FRAME_HEADER
+	OFFSET_IDX_P_GOIDCACHE // runtime.p.goidcache
+	OFFSET_IDX_P_RUNNEXT   // runtime.p.runnext
 	OFFSET_IDX_MAX
 )
 
@@ -176,6 +178,8 @@ type EbpfTraceConf struct {
 		__u64 ctx_ptr_pos;
 		__u64 headers_ptr_pos;
 		__u64 buckets_ptr_pos;
+		__u64 use_swiss_map;
+		//gRPC
 		__u64 httpclient_nextid_pos;
 		__u64 stream_method_ptr_pos;
 		__u64 stream_ctx_pos;
@@ -203,7 +207,7 @@ type EbpfProcInfo struct {
 	CtxPtrPos      uint64
 	HeadersPtrPos  uint64
 	BucketsPtrPos  uint64
-
+	UseSwissMap    uint64
 	// gRPC
 	HttpClientNextidPos uint64
 	StreamMethodPtrPos  uint64