Prechádzať zdrojové kódy

Feature #TASK_QT-18250 1、go 1.24,已经完成http-client cwtraceheader的注入,待解析header完成端到端串联。

rock 7 mesiacov pred
rodič
commit
1d1ce23e95

+ 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. ✅ 调试支持:详细的日志输出
+
+代码已实现并可以编译,下一步需要实际测试验证功能是否正常工作。
+

+ 592 - 471
ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c

@@ -63,393 +63,203 @@ struct
 // volatile const u64 request_host_pos;
 // volatile const u64 request_proto_pos;
 
-static __always_inline long inject_header(void* headers_ptr, struct span_context* propagated_ctx) {
-	bpf_printk("11111111111111");
-    // Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go
-    u64 curr_keyvalue_count = 0;
-    long res = bpf_probe_read_user(&curr_keyvalue_count, sizeof(curr_keyvalue_count), headers_ptr);
-
-    if (res < 0) {
-        cw_bpf_debug("Couldn't read map key-value count from user");
-        return -1;
-    }
-
-    if (curr_keyvalue_count >= 8) {
-        cw_bpf_debug("Map size is bigger than 8, skipping context propagation");
+// cw_inject_header_half: 准备 header 注入数据,保存到 bucket_map_value,由后续的 go_update_header 完成实际注入
+// 支持 Go 1.24+ Swiss Tables 和旧版本的 hmap
+// 
+// 流程说明:
+// 1. uprobe_Transport_roundTrip 调用 cw_inject_header_half,准备数据
+// 2. bpf_tail_call 调用 go_update_header,完成实际注入
+//
+// Swiss Tables 结构说明(Go 1.24+):
+// - maps.Map.used (offset 0): 元素数量
+// - maps.Map.dirPtr (offset 16): 指向 group (小 map) 或 directory (大 map)
+// - maps.Map.dirLen (offset 24): 0 表示小 map,>0 表示大 map
+// - group: ctrlGroup (8 bytes) + slots[8] (每个 slot 40 bytes: string 16 + []string 24)
+
+// cw_inject_header_half: 准备 header 注入数据,保存到 bucket_map_value,由后续的 go_update_header 完成实际注入
+// 支持 Go 1.24+ Swiss Tables 和旧版本的 hmap
+static __always_inline long cw_inject_header_half(void* headers_ptr, char * header_str,struct ebpf_proc_info* proc_info) {
+    bpf_printk("cw_inject_header_half: START");
+    
+    if (!proc_info) {
+        bpf_printk("cw_inject_header_half: proc_info is NULL");
         return -1;
     }
 
     // Get pointer to temp bucket struct we store in a map (avoiding large local variable on the stack)
-    // Performing read-modify-write on the bucket
-    u32 map_id = 1;
+    u32 map_id = CLIENT_MAP_KEY;
     struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
     if (!bucket_map_value) {
+        bpf_printk("cw_inject_header_half: Failed to get bucket_map_value");
         return -1;
     }
-	__builtin_memset(bucket_map_value, 0, sizeof(struct map_bucket));
+    __builtin_memset(bucket_map_value, 0, sizeof(struct map_bucket));
 
-    __u64 pid_tgid = bpf_get_current_pid_tgid();
-	__u32 tgid = pid_tgid >> 32;
-	struct ebpf_proc_info *proc_info =
-			bpf_map_lookup_elem(&proc_info_map, &tgid);
-    if(!proc_info)
-    {
-        return -1;
-    }
-
-    void *buckets_ptr_ptr = (void*) (headers_ptr + proc_info->buckets_ptr_pos);
-    void *bucket_ptr = 0; // The actual pointer to the buckets
-
-    if (curr_keyvalue_count == 0) {
-	    bpf_printk("curr_keyvalue_count == 0");
-
-        // No key-value pairs in the Go map, need to "allocate" memory for the user
-        bucket_ptr = write_target_data(bucket_map_value, sizeof(struct map_bucket));
-        if (bucket_ptr == NULL) {
-            cw_bpf_debug("inject_header: Failed to write bucket to user");
-            return -1;
-        }
-        // Update the buckets pointer in the hmap struct to point to newly allocated bucket
-        res = bpf_probe_write_user(buckets_ptr_ptr, &bucket_ptr, sizeof(bucket_ptr));
+    // Check if Go version >= 1.24 (Swiss Tables)
+    if (proc_info->version >= GO_VERSION(1, 24, 0)) {
+        bpf_printk("cw_inject_header_half: Go 1.24+ Swiss Tables detected");
+        
+        // Read used count (first field in maps.Map)
+        u64 used = 0;
+        long res = bpf_probe_read_user(&used, sizeof(used), headers_ptr);
         if (res < 0) {
-            cw_bpf_debug("Failed to update the map bucket pointer for the user");
+            bpf_printk("cw_inject_header_half: Failed to read used count, res=%ld", res);
             return -1;
         }
-    } else {
-        // There is at least 1 key-value pair, hence the bucket pointer from the user is valid
-        // Read the bucket pointer
-        res = bpf_probe_read_user(&bucket_ptr, sizeof(bucket_ptr), buckets_ptr_ptr);
-        // Read the user's bucket to the eBPF map entry
-        bpf_probe_read_user(bucket_map_value, sizeof(struct map_bucket), bucket_ptr);
-    }
-
-    u8 bucket_index = curr_keyvalue_count & 0x7;
-	bpf_printk("bucket_index:%d",bucket_index);
-
-    char traceparent_tophash = 0xee;
-    bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-
-    // Prepare the key string for the user
-    char key[W3C_KEY_LENGTH] = "traceparent";
-    void *ptr = write_target_data(key, W3C_KEY_LENGTH);
-    if (ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write key to user");
-        return -1;
-    }
-    bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = W3C_KEY_LENGTH, .str = ptr};
-
-    // Prepare the value string slice
-    // First the value string which constains the span context
-    char val[W3C_VAL_LENGTH];
-    span_context_to_w3c_string(propagated_ctx, val);
-    ptr = write_target_data(val, sizeof(val));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write value to user");
-        return -1;
-    }
-
-    // The go string pointing to the above val
-    struct go_string_ot header_value = {.len = W3C_VAL_LENGTH, .str = ptr};
-    ptr = write_target_data((void*)&header_value, sizeof(header_value));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write go_string to user");
-        return -1;
-    }
-
-    // Last, go_slice pointing to the above go_string
-    bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = ptr, .cap = 1, .len = 1};
-
-    // Update the map header count field
-    curr_keyvalue_count += 1;
-    res = bpf_probe_write_user(headers_ptr, &curr_keyvalue_count, sizeof(curr_keyvalue_count));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update key-value count in map header");
-        return -1;
-    }
-
-    // Update the bucket
-    res = bpf_probe_write_user(bucket_ptr, bucket_map_value, sizeof(struct map_bucket));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update bucket content");
-        return -1;
-    }
-
-    bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
-    return 0;
-}
-// 00:00:1015481350055581:5450531005555981:5610250100539899:304775019cd3218a304775019cd3218a:1001025098564810:140acc88cde8773f
-static __always_inline long cw_inject_header(void* headers_ptr, struct apm_span_context* propagated_ctx) {
-    // Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go
-    u64 curr_keyvalue_count = 0;
-    long res = bpf_probe_read_user(&curr_keyvalue_count, sizeof(curr_keyvalue_count), headers_ptr);
-
-    if (res < 0) {
-        cw_bpf_debug("Couldn't read map key-value count from user");
-        return -1;
-    }
-
-    if (curr_keyvalue_count >= 8) {
-        cw_bpf_debug("Map size is bigger than 8, skipping context propagation");
-        return -1;
-    }
-
-    // Get pointer to temp bucket struct we store in a map (avoiding large local variable on the stack)
-    // Performing read-modify-write on the bucket
-    u32 map_id = 0;
-    struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
-    if (!bucket_map_value) {
-        return -1;
-    }
-
-    __u64 pid_tgid = bpf_get_current_pid_tgid();
-	__u32 tgid = pid_tgid >> 32;
-	struct ebpf_proc_info *proc_info =
-			bpf_map_lookup_elem(&proc_info_map, &tgid);
-    if(!proc_info)
-    {
-        return -1;
-    }
-
-    void *buckets_ptr_ptr = (void*) (headers_ptr + proc_info->buckets_ptr_pos);
-    void *bucket_ptr = 0; // The actual pointer to the buckets
+        bpf_printk("cw_inject_header_half: used=%llu", used);
 
-    if (curr_keyvalue_count == 0) {
-        // No key-value pairs in the Go map, need to "allocate" memory for the user
-        bucket_ptr = write_target_data(bucket_map_value, sizeof(struct map_bucket));
-        if (bucket_ptr == NULL) {
-            cw_bpf_debug("inject_header: Failed to write bucket to user");
+        if (used >= 8) {
+            bpf_printk("cw_inject_header_half: Map is full (used=%llu)", used);
             return -1;
         }
-        // Update the buckets pointer in the hmap struct to point to newly allocated bucket
-        res = bpf_probe_write_user(buckets_ptr_ptr, &bucket_ptr, sizeof(bucket_ptr));
+
+        // Read dirLen (at offset 24)
+        s32 dirLen = 0;
+        res = bpf_probe_read_user(&dirLen, sizeof(dirLen), (void*)(headers_ptr + 24));
         if (res < 0) {
-            cw_bpf_debug("Failed to update the map bucket pointer for the user");
+            bpf_printk("cw_inject_header_half: Failed to read dirLen, res=%ld", res);
             return -1;
         }
-    } else {
-        // There is at least 1 key-value pair, hence the bucket pointer from the user is valid
-        // Read the bucket pointer
-        res = bpf_probe_read_user(&bucket_ptr, sizeof(bucket_ptr), buckets_ptr_ptr);
-        // Read the user's bucket to the eBPF map entry
-        bpf_probe_read_user(bucket_map_value, sizeof(struct map_bucket), bucket_ptr);
-    }
-
-    u8 bucket_index = curr_keyvalue_count & 0x7;
-
-    char traceparent_tophash = 0xee;
-    bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-
-    // Prepare the key string for the user
-    char key[W3C_KEY_LENGTH] = "traceparent";
-    void *ptr = write_target_data(key, W3C_KEY_LENGTH);
-    if (ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write key to user");
-        return -1;
-    }
-    bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = W3C_KEY_LENGTH, .str = ptr};
-
-    // Prepare the value string slice
-    // First the value string which constains the span context
-    char val[CW_HEADER_LENGTH];
-	span_context_to_cw_string(propagated_ctx, val);
-    ptr = write_target_data(val, sizeof(val));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write value to user");
-        return -1;
-    }
-
-    // The go string pointing to the above val
-    struct go_string_ot header_value = {.len = CW_HEADER_LENGTH, .str = ptr};
-    ptr = write_target_data((void*)&header_value, sizeof(header_value));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write go_string to user");
-        return -1;
-    }
-
-    // Last, go_slice pointing to the above go_string
-    bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = ptr, .cap = 1, .len = 1};
-
-    // Update the map header count field
-    curr_keyvalue_count += 1;
-    res = bpf_probe_write_user(headers_ptr, &curr_keyvalue_count, sizeof(curr_keyvalue_count));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update key-value count in map header");
-        return -1;
-    }
-
-    // Update the bucket
-    res = bpf_probe_write_user(bucket_ptr, bucket_map_value, sizeof(struct map_bucket));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update bucket content");
-        return -1;
-    }
-
-    bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
-    return 0;
-}
-
-// 00:00:1015481350055581:5450531005555981:5610250100539899:304775019cd3218a304775019cd3218a:1001025098564810:140acc88cde8773f
-static __always_inline long cw_inject_header2(void* headers_ptr, struct apm_span_context* propagated_ctx) {
-    // Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go
-    u64 curr_keyvalue_count = 0;
-    long res = bpf_probe_read_user(&curr_keyvalue_count, sizeof(curr_keyvalue_count), headers_ptr);
-
-    if (res < 0) {
-        cw_bpf_debug("Couldn't read map key-value count from user");
-        return -1;
-    }
-
-    if (curr_keyvalue_count >= 8) {
-        cw_bpf_debug("Map size is bigger than 8, skipping context propagation");
-        return -1;
-    }
-
-    // Get pointer to temp bucket struct we store in a map (avoiding large local variable on the stack)
-    // Performing read-modify-write on the bucket
-    u32 map_id = 0;
-    struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
-    if (!bucket_map_value) {
-        return -1;
-    }
-    __u64 pid_tgid = bpf_get_current_pid_tgid();
-	__u32 tgid = pid_tgid >> 32;
-	struct ebpf_proc_info *proc_info =
-			bpf_map_lookup_elem(&proc_info_map, &tgid);
-    if(!proc_info)
-    {
-        return -1;
-    }
-
-    void *buckets_ptr_ptr = (void*) (headers_ptr + proc_info->buckets_ptr_pos);
-    void *bucket_ptr = 0; // The actual pointer to the buckets
-
-    if (curr_keyvalue_count == 0) {
-        // No key-value pairs in the Go map, need to "allocate" memory for the user
-        bucket_ptr = write_target_data(bucket_map_value, sizeof(struct map_bucket));
-        if (bucket_ptr == NULL) {
-            cw_bpf_debug("inject_header: Failed to write bucket to user");
+        bpf_printk("cw_inject_header_half: dirLen=%d", dirLen);
+
+        void *group_ptr = NULL;
+        
+        if (dirLen == 0) {
+            // Small map: dirPtr points directly to a single group
+            void *dirPtr_ptr = (void*)(headers_ptr + proc_info->buckets_ptr_pos);
+            res = bpf_probe_read_user(&group_ptr, sizeof(group_ptr), dirPtr_ptr);
+            if (res < 0) {
+                bpf_printk("cw_inject_header_half: Failed to read dirPtr, res=%ld", res);
+                return -1;
+            }
+            
+            if (group_ptr == NULL) {
+                // Empty map: need to allocate a new group
+                // Group structure: ctrlGroup (8 bytes) + slots[8] (each slot 40 bytes = 320 bytes total)
+                // Total: 8 + 320 = 328 bytes
+                // We'll allocate in chunks to avoid stack overflow
+                // First allocate and write control word (8 bytes)
+                u64 empty_ctrls = 0x8080808080808080ULL;
+                group_ptr = cw_write_target_data(&empty_ctrls, sizeof(empty_ctrls), proc_info);
+                if (group_ptr == NULL) {
+                    bpf_printk("cw_inject_header_half: Failed to allocate group");
+                    return -1;
+                }
+                
+                // Initialize slots area (320 bytes) with zeros
+                // Write in 8-byte chunks: 320 / 8 = 40 chunks
+                u64 zero = 0;
+                for (int i = 1; i <= 40; i++) {
+                    void *chunk_ptr = (void*)((u64)group_ptr + (i * 8));
+                    res = bpf_probe_write_user(chunk_ptr, &zero, sizeof(zero));
+                    if (res < 0) {
+                        bpf_printk("cw_inject_header_half: Failed to init chunk %d, res=%ld", i, res);
+                        return -1;
+                    }
+                }
+                
+                bpf_printk("cw_inject_header_half: Allocated new group at %p", group_ptr);
+                
+                // Update dirPtr in the map to point to the new group
+                res = bpf_probe_write_user(dirPtr_ptr, &group_ptr, sizeof(group_ptr));
+                if (res < 0) {
+                    bpf_printk("cw_inject_header_half: Failed to update dirPtr, res=%ld", res);
+                    return -1;
+                }
+                bpf_printk("cw_inject_header_half: Updated dirPtr");
+            } else {
+                bpf_printk("cw_inject_header_half: group_ptr=%p (existing)", group_ptr);
+            }
+        } else {
+            bpf_printk("cw_inject_header_half: Large map (dirLen=%d) not supported yet", dirLen);
             return -1;
         }
-        // Update the buckets pointer in the hmap struct to point to newly allocated bucket
-        res = bpf_probe_write_user(buckets_ptr_ptr, &bucket_ptr, sizeof(bucket_ptr));
+
+        // Read control word to find empty slot
+        u64 ctrls = 0;
+        res = bpf_probe_read_user(&ctrls, sizeof(ctrls), group_ptr);
         if (res < 0) {
-            cw_bpf_debug("Failed to update the map bucket pointer for the user");
+            bpf_printk("cw_inject_header_half: Failed to read control word, res=%ld", res);
             return -1;
         }
-    } else {
-        // There is at least 1 key-value pair, hence the bucket pointer from the user is valid
-        // Read the bucket pointer
-        res = bpf_probe_read_user(&bucket_ptr, sizeof(bucket_ptr), buckets_ptr_ptr);
-        // Read the user's bucket to the eBPF map entry
-        bpf_probe_read_user(bucket_map_value, sizeof(struct map_bucket), bucket_ptr);
-    }
-
-    u8 bucket_index = curr_keyvalue_count & 0x7;
-
-//    char traceparent_tophash = 0xee;
-//    bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-
-    // Prepare the key string for the user
-    char key[CW_HEADER_KEY_LENGTH] = CW_HEADER_KEY_VAL;
-    void *ptr = write_target_data(key, CW_HEADER_KEY_LENGTH);
-    if (ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write key to user");
-        return -1;
-    }
-    bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = CW_HEADER_KEY_LENGTH, .str = ptr};
-
-	char traceparent_tophash = 0xee;
-	bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-	bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = CW_HEADER_KEY_LENGTH, .str = ptr};
-
-
-    // Prepare the value string slice
-    // First the value string which constains the span context
-    char val[CW_HEADER_VAL_LENGTH];
-	span_context_to_cw_string(propagated_ctx, val);
-    ptr = write_target_data(val, sizeof(val));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write value to user");
-        return -1;
-    }
-
-    // The go string pointing to the above val
-    struct go_string_ot header_value = {.len = CW_HEADER_VAL_LENGTH, .str = ptr};
-    ptr = write_target_data((void*)&header_value, sizeof(header_value));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write go_string to user");
-        return -1;
-    }
-
-    // Last, go_slice pointing to the above go_string
-    bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = ptr, .cap = 1, .len = 1};
+        bpf_printk("cw_inject_header_half: ctrls=0x%llx", ctrls);
+
+        // Find first empty slot (ctrl byte = 0x80)
+        u8 empty_ctrl = 0x80;
+        u8 slot_idx = 0;
+        bool found_empty = false;
+        
+        for (u8 i = 0; i < 8; i++) {
+            u8 ctrl_byte = (u8)((ctrls >> (i * 8)) & 0xFF);
+            if (ctrl_byte == empty_ctrl) {
+                slot_idx = i;
+                found_empty = true;
+                bpf_printk("cw_inject_header_half: Found empty slot at index %d", i);
+                break;
+            }
+        }
 
-    // Update the map header count field
-    curr_keyvalue_count += 1;
-    res = bpf_probe_write_user(headers_ptr, &curr_keyvalue_count, sizeof(curr_keyvalue_count));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update key-value count in map header");
-        return -1;
-    }
+        if (!found_empty) {
+            bpf_printk("cw_inject_header_half: No empty slot found (ctrls=0x%llx)", ctrls);
+            return -1;
+        }
 
-    // Update the bucket
-    res = bpf_probe_write_user(bucket_ptr, bucket_map_value, sizeof(struct map_bucket));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update bucket content");
-        return -1;
+        // Save information for go_update_header
+        // For Swiss Tables, bucket_ptr stores group_ptr, bucket_index stores slot_idx
+        bucket_map_value->headers_ptr = headers_ptr;
+        bucket_map_value->bucket_ptr = group_ptr;  // Store group_ptr instead of bucket_ptr
+        bucket_map_value->curr_keyvalue_count = used;  // Store used count
+        bucket_map_value->bucket_index = slot_idx;  // Store slot index
+        
+        // Copy header_str to bucket_map_value
+        // Note: header_str is a local array in uprobe_Transport_roundTrip, we need to copy it
+        // Use __builtin_memcpy instead of bpf_probe_read for local-to-local copy
+        __builtin_memcpy(bucket_map_value->header_str, header_str, CW_HEADER_VAL_LENGTH);
+        
+        // Quick verification: check first 3 bytes
+        bpf_printk("cw_inject_header_half: copied[0-2]=0x%02x 0x%02x 0x%02x", 
+                   (u8)bucket_map_value->header_str[0], 
+                   (u8)bucket_map_value->header_str[1], 
+                   (u8)bucket_map_value->header_str[2]);
+        
+        // Mark as Swiss Tables by setting a special flag (we'll use bucket_index >= 8 as flag)
+        // Actually, we can use a different approach - store dirLen in bucket_index
+        // For now, we'll use bucket_index as slot_idx (0-7), and check version in go_update_header
+        
+        bpf_printk("cw_inject_header_half: SUCCESS - saved info for Swiss Tables, slot_idx=%d", slot_idx);
+        return 0;
     }
 
-    bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
-    return 0;
-}
-
-// 00:00:1015481350055581:5450531005555981:5610250100539899:304775019cd3218a304775019cd3218a:1001025098564810:140acc88cde8773f
-static __always_inline long cw_inject_header_half(void* headers_ptr, char * header_str,struct ebpf_proc_info* proc_info) {
-    // Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go
+    // Old implementation for Go < 1.24
+    bpf_printk("cw_inject_header_half: Using old hmap implementation");
+    
+    // Read the key-value count - this field must be the first one in the hmap struct
     u64 curr_keyvalue_count = 0;
     long res = bpf_probe_read_user(&curr_keyvalue_count, sizeof(curr_keyvalue_count), headers_ptr);
-
     if (res < 0) {
-        cw_bpf_debug("Couldn't read map key-value count from user");
+        bpf_printk("cw_inject_header_half: Couldn't read map key-value count, res=%ld", res);
         return -1;
     }
 
     if (curr_keyvalue_count >= 8) {
-        cw_bpf_debug("Map size is bigger than 8, skipping context propagation");
+        bpf_printk("cw_inject_header_half: Map size is bigger than 8, skipping");
         return -1;
     }
 
-    // Get pointer to temp bucket struct we store in a map (avoiding large local variable on the stack)
-    // Performing read-modify-write on the bucket
-    u32 map_id = CLIENT_MAP_KEY;
-    struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
-    if (!bucket_map_value) {
-        return -1;
-    }
-	__builtin_memset(bucket_map_value, 0, sizeof(struct map_bucket));
-//    __u64 pid_tgid = bpf_get_current_pid_tgid();
-//	__u32 tgid = pid_tgid >> 32;
-//	struct ebpf_proc_info *proc_info =
-//			bpf_map_lookup_elem(&proc_info_map, &tgid);
-//    if(!proc_info)
-//    {
-//        return -1;
-//    }
-
     void *buckets_ptr_ptr = (void*) (headers_ptr + proc_info->buckets_ptr_pos);
-    void *bucket_ptr = 0; // The actual pointer to the buckets
+    void *bucket_ptr = 0;
 
     if (curr_keyvalue_count == 0) {
         // No key-value pairs in the Go map, need to "allocate" memory for the user
-        bucket_ptr = cw_write_target_data(bucket_map_value, sizeof(struct map_bucket),proc_info);
+        bucket_ptr = cw_write_target_data(bucket_map_value, sizeof(struct map_bucket), proc_info);
         if (bucket_ptr == NULL) {
-            cw_bpf_debug("inject_header: Failed to write bucket to user");
+            bpf_printk("cw_inject_header_half: Failed to write bucket to user");
             return -1;
         }
         // Update the buckets pointer in the hmap struct to point to newly allocated bucket
         res = bpf_probe_write_user(buckets_ptr_ptr, &bucket_ptr, sizeof(bucket_ptr));
         if (res < 0) {
-            cw_bpf_debug("Failed to update the map bucket pointer for the user");
+            bpf_printk("cw_inject_header_half: Failed to update bucket pointer, res=%ld", res);
             return -1;
         }
     } else {
@@ -462,87 +272,13 @@ static __always_inline long cw_inject_header_half(void* headers_ptr, char * head
 
     u8 bucket_index = curr_keyvalue_count & 0x7;
 
-//	bucket_map_value->header_value_str_p = ptr;
-
-	bucket_map_value->headers_ptr = headers_ptr;
-	bucket_map_value->bucket_ptr = bucket_ptr;
-
-	bucket_map_value->curr_keyvalue_count = curr_keyvalue_count;
-	bucket_map_value->bucket_index = bucket_index;
-	bpf_probe_read(bucket_map_value->header_str, sizeof(bucket_map_value->header_str),header_str);
-	cw_bpf_debug("bucket_map_value->header_str %s",bucket_map_value->header_str);
-//	bucket_map_value->header_str = header_str;
-
-//	void * header_str_ptr = cw_write_target_data(header_str, CW_HEADER_VAL_LENGTH,proc_info);
-//	if(header_str_ptr == NULL) {
-//		cw_bpf_debug("inject_header: Failed to write value to user");
-//		return -1;
-//	}
-//	bucket_map_value->header_str_p = header_str;
-
-	return 0;
-
-//    char traceparent_tophash = 0xee;
-//    bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-
-    // Prepare the key string for the user
-    char key[CW_HEADER_KEY_LENGTH] = CW_HEADER_KEY_VAL;
-    void *ptr = cw_write_target_data(key, CW_HEADER_KEY_LENGTH,proc_info);
-    if (ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write key to user");
-        return -1;
-    }
-//    bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = CW_HEADER_KEY_LENGTH, .str = ptr};
-
-	char traceparent_tophash = 0xee;
-	bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-	bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = CW_HEADER_KEY_LENGTH, .str = ptr};
-
-//	u32 k0;
-//	struct tail_client_ctx_t *tail_calls_context = bpf_map_lookup_elem(&tail_client_ctx_heap, &k0);
-//	if (!tail_calls_context){
-//		return -1;
-//	}
-
-    // Prepare the value string slice
-    // First the value string which constains the span context
-//    char val[CW_HEADER_VAL_LENGTH];
-//	span_context_to_cw_string(propagated_ctx, val);
-// 往前提
-    ptr = cw_write_target_data(header_str, CW_HEADER_VAL_LENGTH,proc_info);
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write value to user");
-        return -1;
-    }
-
-//	return 0;
-    // The go string pointing to the above val
-    struct go_string_ot header_value = {.len = CW_HEADER_VAL_LENGTH, .str = ptr};
-    ptr = cw_write_target_data((void*)&header_value, sizeof(header_value),proc_info);
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write go_string to user");
-        return -1;
-    }
-
-    // Last, go_slice pointing to the above go_string
-    bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = ptr, .cap = 1, .len = 1};
-
-    // Update the map header count field
-    curr_keyvalue_count += 1;
-    res = bpf_probe_write_user(headers_ptr, &curr_keyvalue_count, sizeof(curr_keyvalue_count));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update key-value count in map header");
-        return -1;
-    }
-
-    // Update the bucket
-    res = bpf_probe_write_user(bucket_ptr, bucket_map_value, sizeof(struct map_bucket));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update bucket content");
-        return -1;
-    }
-
-    bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
+    bucket_map_value->headers_ptr = headers_ptr;
+    bucket_map_value->bucket_ptr = bucket_ptr;
+    bucket_map_value->curr_keyvalue_count = curr_keyvalue_count;
+    bucket_map_value->bucket_index = bucket_index;
+    bpf_probe_read(bucket_map_value->header_str, sizeof(bucket_map_value->header_str), header_str);
+    
+    bpf_printk("cw_inject_header_half: SUCCESS - saved info for old hmap, bucket_index=%d", bucket_index);
     return 0;
 }
 
@@ -550,7 +286,7 @@ static __always_inline long cw_inject_header_half(void* headers_ptr, char * head
 // func net/http/transport.roundTrip(req *Request) (*Response, error)
 SEC("uprobe/Transport_roundTrip")
 int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
-	cw_bpf_debug("--------[Uprobe HTTP Client] start %llu",get_current_goroutine());
+	bpf_printk("--------[Uprobe HTTP Client] start %llu",get_current_goroutine());
 
     u64 request_pos = 2;
     void *req_ptr = get_argument(ctx, request_pos);
@@ -688,7 +424,19 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
 //        cw_bpf_debug("uprobe_Transport_roundTrip: Failed to inject header");
 //    }
 	char val[CW_HEADER_VAL_LENGTH];
+	__builtin_memset(val, 0, sizeof(val));  // Initialize to zero
 	span_context_to_cw_string(&httpReq->apm_sc, val);
+	
+	// Verify val content after generation - check first few bytes as hex
+	bpf_printk("uprobe_Transport_roundTrip: val[0]=0x%02x, val[1]=0x%02x, val[2]=0x%02x", 
+	           (u8)val[0], (u8)val[1], (u8)val[2]);
+	bpf_printk("uprobe_Transport_roundTrip: val[3]=0x%02x, val[4]=0x%02x, val[5]=0x%02x", 
+	           (u8)val[3], (u8)val[4], (u8)val[5]);
+	
+	// Also verify as characters if printable
+	if (val[0] >= 32 && val[0] < 127) {
+		bpf_printk("uprobe_Transport_roundTrip: val[0-2] as chars: %c%c%c", val[0], val[1], val[2]);
+	}
 
 	long res2 = cw_inject_header_half(headers_ptr, val,proc_info);
 	if (res2 < 0) {
@@ -708,115 +456,488 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
     return 0;
 }
 
-// bpf_prog_up__go_update_header
+// bpf_prog_up__go_update_header: 完成实际的 header 注入
+// 从 bucket_map_value 读取信息,写入到 Go map 中
 PROGUP(go_update_header)(struct pt_regs *ctx) {
+	bpf_printk("go_update_header: START");
+	
 	__u64 pid_tgid = bpf_get_current_pid_tgid();
 	__u32 tgid = pid_tgid >> 32;
 	struct ebpf_proc_info *proc_info =
 			bpf_map_lookup_elem(&proc_info_map, &tgid);
 	if(!proc_info)
 	{
+		bpf_printk("go_update_header: proc_info is NULL");
 		return 0;
 	}
-//	__u32 k0;
-//	struct tail_client_ctx_t *tail_calls_context =
-//			bpf_map_lookup_elem(&tail_client_ctx_heap, &k0);
-//	if (!tail_calls_context){
-//		return -1;
-//	}
-	u32 map_id = CLIENT_MAP_KEY;
 
+	u32 map_id = CLIENT_MAP_KEY;
 	struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
 	if (!bucket_map_value) {
+		bpf_printk("go_update_header: bucket_map_value is NULL");
 		return -1;
 	}
-//	if (!bucket_map_value->header_value_str_p){
-//		return -1;
-//	}
+	
 	if (!bucket_map_value->headers_ptr){
+		bpf_printk("go_update_header: headers_ptr is NULL");
 		return -1;
 	}
 	if (!bucket_map_value->bucket_ptr){
+		bpf_printk("go_update_header: bucket_ptr is NULL");
 		return -1;
 	}
-//	if (!bucket_map_value->header_str){
-//		return -1;
-//	}
-//	if (!bucket_map_value->header_str_p){
-//		return -1;
-//	}
-	if (bucket_map_value->bucket_index >=8) {
+	if (bucket_map_value->bucket_index >= 8) {
+		bpf_printk("go_update_header: bucket_index >= 8");
 		return -1;
 	}
 
 	void *headers_ptr = bucket_map_value->headers_ptr;
-	void *bucket_ptr = bucket_map_value->bucket_ptr;
-
-//	u8 bucket_index = 0;
-//	bucket_index = bucket_map_value->bucket_index;
-
+	void *bucket_ptr = bucket_map_value->bucket_ptr;  // For old hmap: bucket_ptr, For Swiss Tables: group_ptr
 	u64 curr_keyvalue_count = bucket_map_value->curr_keyvalue_count;
 	char * header_str = bucket_map_value->header_str;
+	u8 bucket_index = bucket_map_value->bucket_index;  // For old hmap: bucket_index, For Swiss Tables: slot_idx
+
+	bpf_printk("go_update_header: headers_ptr=%p", headers_ptr);
+	bpf_printk("go_update_header: bucket_ptr=%p, count=%llu", bucket_ptr, curr_keyvalue_count);
+	bpf_printk("go_update_header: bucket_index=%d", bucket_index);
+	
+	// Verify header_str content before using it - check first 6 bytes
+	bpf_printk("go_update_header: header_str[0-2]=0x%02x 0x%02x 0x%02x", 
+	           (u8)header_str[0], (u8)header_str[1], (u8)header_str[2]);
+	bpf_printk("go_update_header: header_str[3-5]=0x%02x 0x%02x 0x%02x", 
+	           (u8)header_str[3], (u8)header_str[4], (u8)header_str[5]);
+
+	// Check if Go version >= 1.24 (Swiss Tables)
+	if (proc_info->version >= GO_VERSION(1, 24, 0)) {
+		bpf_printk("go_update_header: Go 1.24+ Swiss Tables - injecting into group");
+		
+		// For Swiss Tables, bucket_ptr is actually group_ptr, bucket_index is slot_idx
+		void *group_ptr = bucket_ptr;
+		u8 slot_idx = bucket_index;
+		
+		// Prepare key "cwtrace"
+		// Note: Go strings don't include null terminator, so len is exactly 7
+		char key[8] = "cwtrace";  // 7 chars + null terminator for C string
+		void *key_str_ptr = cw_write_target_data(key, CW_HEADER_KEY_LENGTH, proc_info);
+		if (key_str_ptr == NULL) {
+			bpf_printk("go_update_header: Failed to write key");
+			return -1;
+		}
+		bpf_printk("go_update_header: key_str_ptr=%p", key_str_ptr);
+		
+		// Save key_str_ptr to a volatile variable to prevent compiler optimization
+		// Use multiple steps to force compiler to use correct value
+		void *key_str_ptr_copy = key_str_ptr;
+		volatile void *key_str_ptr_volatile = key_str_ptr_copy;
+		u64 saved_key_str_ptr_early = (u64)(void*)key_str_ptr_volatile;
+		
+		bpf_printk("go_update_header: saved_key_str_ptr_early=0x%llx", saved_key_str_ptr_early);
+		
+		// Verify the saved value is not NULL and not equal to any future slot_ptr_u64 value
+		// We can't check against slot_ptr_u64 yet, but we can check it's not zero
+		if (saved_key_str_ptr_early == 0) {
+			bpf_printk("go_update_header: ERROR: saved_key_str_ptr_early is NULL!");
+			return -1;
+		}
+		
+		// Double-check by reading back from key_str_ptr
+		void *key_str_ptr_verify = key_str_ptr;
+		u64 saved_verify = (u64)key_str_ptr_verify;
+		if (saved_key_str_ptr_early != saved_verify) {
+			bpf_printk("go_update_header: ERROR: saved_key_str_ptr_early mismatch!");
+			bpf_printk("go_update_header: saved=0x%llx, verify=0x%llx", saved_key_str_ptr_early, saved_verify);
+			return -1;
+		}
+
+		// Prepare value string
+		// Verify header_str content before writing
+		bpf_printk("go_update_header: Before write, header_str[0-2]=0x%02x 0x%02x 0x%02x", 
+		           (u8)header_str[0], (u8)header_str[1], (u8)header_str[2]);
+		bpf_printk("go_update_header: Before write, header_str[3-5]=0x%02x 0x%02x 0x%02x", 
+		           (u8)header_str[3], (u8)header_str[4], (u8)header_str[5]);
+		
+		void *header_str_ptr = cw_write_target_data(header_str, CW_HEADER_VAL_LENGTH, proc_info);
+		if(header_str_ptr == NULL) {
+			bpf_printk("go_update_header: Failed to write header_str");
+			return -1;
+		}
+		// Save header_str_ptr to u64 immediately and verify it's correct
+		// Use the same technique as key_str_ptr to avoid register corruption
+		void *header_str_ptr_copy = header_str_ptr;
+		volatile void *header_str_ptr_volatile = header_str_ptr_copy;
+		u64 saved_header_str_ptr_u64 = (u64)(void*)header_str_ptr_volatile;
+		
+		// Verify immediately
+		u64 verify_immediate = (u64)header_str_ptr;
+		if (saved_header_str_ptr_u64 != verify_immediate) {
+			bpf_printk("go_update_header: ERROR: saved_header_str_ptr_u64 mismatch at save!");
+			bpf_printk("go_update_header: saved=0x%llx, verify=0x%llx", saved_header_str_ptr_u64, verify_immediate);
+			return -1;
+		}
+		
+		bpf_printk("go_update_header: header_str_ptr=%p", header_str_ptr);
+		bpf_printk("go_update_header: saved_header_str_ptr_u64=0x%llx", saved_header_str_ptr_u64);
+		
+		// Verify what was written to userspace using both pointers
+		char verify_header_str_saved[8] = {0};
+		long verify_res_saved = bpf_probe_read_user(verify_header_str_saved, sizeof(verify_header_str_saved), (void*)saved_header_str_ptr_u64);
+		if (verify_res_saved == 0) {
+			bpf_printk("go_update_header: From saved_ptr, [0-2]=0x%02x 0x%02x 0x%02x", 
+			           (u8)verify_header_str_saved[0], (u8)verify_header_str_saved[1], (u8)verify_header_str_saved[2]);
+		}
+		
+		char verify_header_str_orig[8] = {0};
+		long verify_res_orig = bpf_probe_read_user(verify_header_str_orig, sizeof(verify_header_str_orig), header_str_ptr);
+		if (verify_res_orig == 0) {
+			bpf_printk("go_update_header: From orig_ptr, [0-2]=0x%02x 0x%02x 0x%02x", 
+			           (u8)verify_header_str_orig[0], (u8)verify_header_str_orig[1], (u8)verify_header_str_orig[2]);
+		}
+
+		// Create go_string_ot for the value using saved pointer
+		struct go_string_ot val_str = {.str = (void*)saved_header_str_ptr_u64, .len = CW_HEADER_VAL_LENGTH};
+		void *val_str_struct_ptr = cw_write_target_data((void*)&val_str, sizeof(val_str), proc_info);
+		if(val_str_struct_ptr == NULL) {
+			bpf_printk("go_update_header: Failed to write val_str struct");
+			return -1;
+		}
+		bpf_printk("go_update_header: val_str_struct_ptr=%p", val_str_struct_ptr);
+
+		// Calculate slot offset
+		// Group layout: ctrlGroup (8 bytes) + slots[8]
+		// For map[string][]string: 
+		//   - key: string (16 bytes: ptr 8 + len 8)
+		//   - elem: []string (24 bytes: array 8 + len 8 + cap 8)
+		//   - slot size: 16 + 24 = 40 bytes
+		// Note: Go structs are aligned, but for map[string][]string, the slot should be exactly 40 bytes
+		u64 group_slots_offset = 8;  // After ctrlGroup (8 bytes)
+		u64 slot_size = 40;  // string (16) + []string (24)
+		u64 slot_offset = group_slots_offset + (slot_idx * slot_size);
+		
+		bpf_printk("go_update_header: slot_idx=%d", slot_idx);
+		bpf_printk("go_update_header: group_slots_offset=%llu", group_slots_offset);
+		bpf_printk("go_update_header: slot_size=%llu", slot_size);
+		bpf_printk("go_update_header: slot_offset=%llu", slot_offset);
+		bpf_printk("go_update_header: group_ptr=%p", group_ptr);
+		
+		// Calculate slot_ptr carefully to avoid any issues
+		u64 group_ptr_u64 = (u64)group_ptr;
+		u64 slot_ptr_u64 = group_ptr_u64 + slot_offset;
+		
+		bpf_printk("go_update_header: group_ptr_u64=0x%llx", group_ptr_u64);
+		bpf_printk("go_update_header: slot_ptr_u64=0x%llx", slot_ptr_u64);
+		
+		// Verify slot_ptr is within reasonable bounds (should be >= group_ptr + 8 and < group_ptr + 328)
+		if (slot_ptr_u64 < group_ptr_u64 + 8 || slot_ptr_u64 >= group_ptr_u64 + 328) {
+			bpf_printk("go_update_header: slot_ptr out of bounds!");
+			bpf_printk("go_update_header: group_ptr_u64=0x%llx, slot_ptr_u64=0x%llx", group_ptr_u64, slot_ptr_u64);
+			return -1;
+		}
+
+		// Write key (go_string_ot at slot offset 0)
+		// Verify key_str_ptr is valid
+		if (key_str_ptr == NULL) {
+			bpf_printk("go_update_header: key_str_ptr is NULL!");
+			return -1;
+		}
+		
+		// Verify key string data is valid before writing
+		char key_check[8] = {0};
+		long res = bpf_probe_read_user(key_check, sizeof(key_check), key_str_ptr);
+		if (res < 0) {
+			bpf_printk("go_update_header: Failed to verify key string data, res=%ld", res);
+			return -1;
+		}
+		bpf_printk("go_update_header: key_str_ptr=%p", key_str_ptr);
+		bpf_printk("go_update_header: key_check[0]=0x%x", (u8)key_check[0]);
+		
+		// Create key_str struct and verify it's correct before writing
+		struct go_string_ot key_str = {.str = key_str_ptr, .len = CW_HEADER_KEY_LENGTH};
+		bpf_printk("go_update_header: Writing key: str=%p, len=%d", key_str.str, key_str.len);
+		
+		// Verify key_str is still correct right before writing
+		if (key_str.str != key_str_ptr || key_str.len != CW_HEADER_KEY_LENGTH) {
+			bpf_printk("go_update_header: ERROR: key_str corrupted before write!");
+			bpf_printk("go_update_header: key_str.str=%p, expected=%p", key_str.str, key_str_ptr);
+			return -1;
+		}
+		
+		// Read what's currently in the slot before writing (for debugging)
+		// Use (void*)slot_ptr_u64 directly to avoid variable corruption
+		struct go_string_ot slot_before = {0};
+		res = bpf_probe_read_user(&slot_before, sizeof(slot_before), (void*)slot_ptr_u64);
+		if (res == 0) {
+			bpf_printk("go_update_header: Slot before write: str=%p", slot_before.str);
+			bpf_printk("go_update_header: Slot before write: len=%lld", slot_before.len);
+		}
+		
+		// Write key directly using slot_ptr_u64 - write pointer and length separately to avoid corruption
+		// Re-read key_str_ptr from the bucket_map_value to get the correct value
+		// This is a workaround for eBPF compiler register corruption issues
+		char key_recreate[8] = "cwtrace";
+		void *key_str_ptr_fresh = cw_write_target_data(key_recreate, CW_HEADER_KEY_LENGTH, proc_info);
+		if (key_str_ptr_fresh == NULL) {
+			bpf_printk("go_update_header: Failed to recreate key string");
+			return -1;
+		}
+		u64 key_str_ptr_u64 = (u64)key_str_ptr_fresh;
+		
+		u64 saved_key_str_len = (u64)CW_HEADER_KEY_LENGTH;
+		
+		bpf_printk("go_update_header: key_str_ptr_fresh=%p, u64=0x%llx", key_str_ptr_fresh, key_str_ptr_u64);
+		bpf_printk("go_update_header: saved_key_str_len=%llu", saved_key_str_len);
+		bpf_printk("go_update_header: Writing key pointer: 0x%llx to slot at 0x%llx", key_str_ptr_u64, slot_ptr_u64);
+		
+		// Write pointer first (first 8 bytes of go_string_ot) - use fresh value
+		res = bpf_probe_write_user((void*)slot_ptr_u64, &key_str_ptr_u64, sizeof(key_str_ptr_u64));
+		if (res < 0) {
+			bpf_printk("go_update_header: Failed to write key pointer, res=%ld", res);
+			return -1;
+		}
+		
+		// Verify pointer was written correctly
+		u64 verify_ptr = 0;
+		res = bpf_probe_read_user(&verify_ptr, sizeof(verify_ptr), (void*)slot_ptr_u64);
+		if (res == 0) {
+			bpf_printk("go_update_header: Verified pointer after write: 0x%llx", verify_ptr);
+			if (verify_ptr != key_str_ptr_u64) {
+				bpf_printk("go_update_header: ERROR: Pointer mismatch!");
+				bpf_printk("go_update_header: Expected 0x%llx, got 0x%llx", key_str_ptr_u64, verify_ptr);
+			}
+		}
+		
+		// Write length second (next 8 bytes of go_string_ot)
+		u64 slot_len_ptr = slot_ptr_u64 + 8;
+		bpf_printk("go_update_header: Writing key length: %llu to slot at 0x%llx", saved_key_str_len, slot_len_ptr);
+		res = bpf_probe_write_user((void*)slot_len_ptr, &saved_key_str_len, sizeof(saved_key_str_len));
+		if (res < 0) {
+			bpf_printk("go_update_header: Failed to write key length, res=%ld", res);
+			return -1;
+		}
+		bpf_printk("go_update_header: Key written to slot");
+		
+		// Verify what we wrote - use slot_ptr_u64 directly
+		u64 slot_ptr_for_verify = slot_ptr_u64;
+		struct go_string_ot verify_key = {0};
+		res = bpf_probe_read_user(&verify_key, sizeof(verify_key), (void*)slot_ptr_for_verify);
+		if (res == 0) {
+			bpf_printk("go_update_header: Verified key: str=%p, len=%lld", verify_key.str, verify_key.len);
+			bpf_printk("go_update_header: Verify read from 0x%llx", slot_ptr_for_verify);
+			if (verify_key.str != key_str_ptr_fresh || verify_key.len != CW_HEADER_KEY_LENGTH) {
+				bpf_printk("go_update_header: Key verification failed!");
+				bpf_printk("go_update_header: Expected str=%p, len=%d", key_str_ptr_fresh, CW_HEADER_KEY_LENGTH);
+				bpf_printk("go_update_header: Got str=%p, len=%lld", verify_key.str, verify_key.len);
+				return -1;
+			}
+			
+			// Also verify the actual key string content
+			char key_content[8] = {0};
+			res = bpf_probe_read_user(key_content, sizeof(key_content), verify_key.str);
+			if (res == 0) {
+				bpf_printk("go_update_header: Key content[0-2]: %c%c%c", key_content[0], key_content[1], key_content[2]);
+				bpf_printk("go_update_header: Key content[3-4]: %c%c", key_content[3], key_content[4]);
+				bpf_printk("go_update_header: Key content[5-6]: %c%c", key_content[5], key_content[6]);
+			}
+			
+			// Read the entire slot to see what Go runtime will see
+			// Read as go_string_ot first, then verify the bytes
+			u64 slot_ptr_for_read = slot_ptr_u64;
+			bpf_printk("go_update_header: Reading slot at 0x%llx", slot_ptr_for_read);
+			struct go_string_ot slot_key_verify = {0};
+			res = bpf_probe_read_user(&slot_key_verify, sizeof(slot_key_verify), (void*)slot_ptr_for_read);
+			if (res == 0) {
+				bpf_printk("go_update_header: Slot key as struct: str=%p, len=%lld", slot_key_verify.str, slot_key_verify.len);
+				bpf_printk("go_update_header: Expected: str=%p, len=%d", key_str_ptr_fresh, CW_HEADER_KEY_LENGTH);
+				if (slot_key_verify.str != key_str_ptr_fresh || slot_key_verify.len != CW_HEADER_KEY_LENGTH) {
+					bpf_printk("go_update_header: WARNING: Slot content mismatch!");
+				}
+			} else {
+				bpf_printk("go_update_header: Failed to read slot_key_verify, res=%ld", res);
+			}
+			
+			// Also read raw bytes for debugging - use slot_key_verify we already read
+			bpf_printk("go_update_header: Slot raw pointer from struct: 0x%llx", (u64)slot_key_verify.str);
+			bpf_printk("go_update_header: Slot raw length from struct: 0x%llx", (u64)slot_key_verify.len);
+			
+			// Read raw bytes to see what's actually in memory
+			char slot_raw[16] = {0};  // Only read first 16 bytes (go_string_ot)
+			res = bpf_probe_read_user(slot_raw, sizeof(slot_raw), (void*)slot_ptr_for_read);
+			if (res == 0) {
+				bpf_printk("go_update_header: Slot raw bytes (first 16 bytes):");
+				for (int i = 0; i < 16; i++) {
+					bpf_printk("go_update_header: slot[%d]=0x%02x", i, (u8)slot_raw[i]);
+				}
+				// Verify first 8 bytes match the pointer we wrote
+				u64 *raw_ptr = (u64*)slot_raw;
+				bpf_printk("go_update_header: First 8 bytes as u64: 0x%llx", raw_ptr[0]);
+				bpf_printk("go_update_header: Expected pointer: 0x%llx", key_str_ptr_u64);
+			}
+		} else {
+			bpf_printk("go_update_header: Failed to verify key, res=%ld", res);
+		}
 
-//	cw_bpf_debug("header_str %s",bucket_map_value->header_str);
+		// Write value (go_slice_ot at slot offset 16, after key)
+		// Verify elem_ptr is within bounds
+		u64 elem_ptr_u64 = slot_ptr_u64 + 16;  // After key (16 bytes)
+		if (elem_ptr_u64 < group_ptr_u64 + 8 || elem_ptr_u64 >= group_ptr_u64 + 328) {
+			bpf_printk("go_update_header: elem_ptr out of bounds!");
+			bpf_printk("go_update_header: elem_ptr_u64=0x%llx", elem_ptr_u64);
+			return -1;
+		}
+		bpf_printk("go_update_header: elem_ptr_u64=0x%llx", elem_ptr_u64);
+		
+		// Re-create value string struct to avoid register corruption (similar to key fix)
+		// Re-read header_str from map to get the correct pointer value
+		// Since saved_header_str_ptr_u64 might be corrupted, we'll re-read from the map
+		// But actually, we already have header_str_ptr from the map, so let's use that directly
+		// However, header_str_ptr might also be corrupted by now, so we need to re-read header_str
+		// and write it again to get a fresh pointer
+		
+		// Re-write header_str to get a fresh pointer (similar to key fix)
+		void *header_str_ptr_fresh = cw_write_target_data(header_str, CW_HEADER_VAL_LENGTH, proc_info);
+		if (header_str_ptr_fresh == NULL) {
+			bpf_printk("go_update_header: Failed to recreate header_str");
+			return -1;
+		}
+		u64 header_str_ptr_fresh_u64 = (u64)header_str_ptr_fresh;
+		bpf_printk("go_update_header: header_str_ptr_fresh=%p, u64=0x%llx", header_str_ptr_fresh, header_str_ptr_fresh_u64);
+		
+		// Use fresh pointer to create val_str struct
+		struct go_string_ot val_str_fresh = {.str = (void*)header_str_ptr_fresh_u64, .len = CW_HEADER_VAL_LENGTH};
+		void *val_str_struct_ptr_fresh = cw_write_target_data((void*)&val_str_fresh, sizeof(val_str_fresh), proc_info);
+		if (val_str_struct_ptr_fresh == NULL) {
+			bpf_printk("go_update_header: Failed to recreate val_str struct");
+			return -1;
+		}
+		u64 val_str_struct_ptr_u64 = (u64)val_str_struct_ptr_fresh;
+		bpf_printk("go_update_header: val_str_struct_ptr_fresh=%p, u64=0x%llx", val_str_struct_ptr_fresh, val_str_struct_ptr_u64);
+		
+		struct go_slice_ot val_slice = {.array = (void*)val_str_struct_ptr_u64, .len = 1, .cap = 1};
+		bpf_printk("go_update_header: Writing value slice: array=%p, len=%d, cap=%d", val_slice.array, val_slice.len, val_slice.cap);
+		// Use elem_ptr_u64 directly to avoid variable corruption
+		res = bpf_probe_write_user((void*)elem_ptr_u64, &val_slice, sizeof(val_slice));
+		if (res < 0) {
+			bpf_printk("go_update_header: Failed to write value slice, res=%ld", res);
+			return -1;
+		}
+		bpf_printk("go_update_header: Value written to slot");
+		
+		// Verify value was written correctly
+		struct go_slice_ot verify_val = {0};
+		res = bpf_probe_read_user(&verify_val, sizeof(verify_val), (void*)elem_ptr_u64);
+		if (res == 0) {
+			bpf_printk("go_update_header: Verified value: array=%p, len=%lld, cap=%lld", verify_val.array, verify_val.len, verify_val.cap);
+			if (verify_val.array != (void*)val_str_struct_ptr_u64 || verify_val.len != 1 || verify_val.cap != 1) {
+				bpf_printk("go_update_header: Value verification failed!");
+				bpf_printk("go_update_header: Expected array=%p, got array=%p", (void*)val_str_struct_ptr_u64, verify_val.array);
+				return -1;
+			}
+			
+			// Also verify the actual string content in val_str_struct_ptr
+			struct go_string_ot verify_val_str = {0};
+			res = bpf_probe_read_user(&verify_val_str, sizeof(verify_val_str), verify_val.array);
+			if (res == 0) {
+				bpf_printk("go_update_header: Value string: str=%p, len=%lld", verify_val_str.str, verify_val_str.len);
+				if (verify_val_str.str != (void*)header_str_ptr_fresh_u64) {
+					bpf_printk("go_update_header: WARNING: Value string pointer mismatch!");
+					bpf_printk("go_update_header: Expected str=%p, got str=%p", (void*)header_str_ptr_fresh_u64, verify_val_str.str);
+				} else {
+					// Verify the actual string content
+					char verify_str_content[8] = {0};
+					res = bpf_probe_read_user(verify_str_content, sizeof(verify_str_content), verify_val_str.str);
+					if (res == 0) {
+						bpf_printk("go_update_header: Value string content[0-2]=0x%02x 0x%02x 0x%02x", 
+						           (u8)verify_str_content[0], (u8)verify_str_content[1], (u8)verify_str_content[2]);
+					}
+				}
+			}
+		}
 
+		// Update control byte: set to H2 hash (lower 7 bits of hash)
+		// For simplicity, use a fixed value (0x5A) that's not 0x80 (empty) or 0xFE (deleted)
+		u8 new_ctrl = 0x5A;  // Placeholder H2 hash
+		
+		// Read current control word
+		u64 ctrls = 0;
+		res = bpf_probe_read_user(&ctrls, sizeof(ctrls), group_ptr);
+		if (res < 0) {
+			bpf_printk("go_update_header: Failed to read control word, res=%ld", res);
+			return -1;
+		}
+		
+		// Update control byte in the control word
+		u64 ctrl_mask = 0xFFULL << (slot_idx * 8);
+		u64 new_ctrl_value = ((u64)new_ctrl) << (slot_idx * 8);
+		ctrls = (ctrls & ~ctrl_mask) | new_ctrl_value;
+		bpf_printk("go_update_header: Updating ctrl, slot_idx=%d, new_ctrl=0x%x", slot_idx, new_ctrl);
+		
+		res = bpf_probe_write_user(group_ptr, &ctrls, sizeof(ctrls));
+		if (res < 0) {
+			bpf_printk("go_update_header: Failed to update control word, res=%ld", res);
+			return -1;
+		}
+		bpf_printk("go_update_header: Control word updated");
+
+		// Update used count
+		u64 used = curr_keyvalue_count + 1;
+		res = bpf_probe_write_user(headers_ptr, &used, sizeof(used));
+		if (res < 0) {
+			bpf_printk("go_update_header: Failed to update used count, res=%ld", res);
+			return -1;
+		}
+		bpf_printk("go_update_header: Used count updated to %llu", used);
+
+		bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
+		bpf_printk("go_update_header: SUCCESS - Swiss Tables injection completed");
+		return 0;
+	}
 
+	// Old implementation for Go < 1.24
+	bpf_printk("go_update_header: Using old hmap implementation");
+	
 	char key[CW_HEADER_KEY_LENGTH] = CW_HEADER_KEY_VAL;
-	void *ptr = cw_write_target_data(key, CW_HEADER_KEY_LENGTH,proc_info);
+	void *ptr = cw_write_target_data(key, CW_HEADER_KEY_LENGTH, proc_info);
 	if (ptr == NULL) {
-//		cw_bpf_debug("inject_header: Failed to write key to user");
+		bpf_printk("go_update_header: Failed to write key");
 		return -1;
 	}
-//    bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = CW_HEADER_KEY_LENGTH, .str = ptr};
 
-	// 往前提
-//	char val[CW_HEADER_VAL_LENGTH];
-//	bpf_probe_read(val, sizeof(val),&header_str);
-	void * header_str_ptr = cw_write_target_data(header_str, CW_HEADER_VAL_LENGTH,proc_info);
+	void * header_str_ptr = cw_write_target_data(header_str, CW_HEADER_VAL_LENGTH, proc_info);
 	if(header_str_ptr == NULL) {
-//		cw_bpf_debug("inject_header: Failed to write value to user");
+		bpf_printk("go_update_header: Failed to write header_str");
 		return -1;
 	}
 
-//	if (bucket_index < 0 || bucket_index >= 8){
-//		return -1;
-//	}
-
-
 	// The go string pointing to the above val
 	struct go_string_ot header_value = {.len = CW_HEADER_VAL_LENGTH, .str = header_str_ptr};
-	void * header_value_ptr = cw_write_target_data((void*)&header_value, sizeof(header_value),proc_info);
+	void * header_value_ptr = cw_write_target_data((void*)&header_value, sizeof(header_value), proc_info);
 	if(header_value_ptr == NULL) {
-		cw_bpf_debug("inject_header: Failed to write go_string to user");
+		bpf_printk("go_update_header: Failed to write go_string");
 		return -1;
 	}
 
 	char traceparent_tophash = 0xee;
-	u8 bucket_index = curr_keyvalue_count & 0x7;
-	bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-	bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = CW_HEADER_KEY_LENGTH, .str = ptr};
-	bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = header_value_ptr, .cap = 1, .len = 1};
-
-	// Last, go_slice pointing to the above go_string
-//	return 1;
-//	bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = ptr, .cap = 1, .len = 1};
+	u8 idx = curr_keyvalue_count & 0x7;  // Use calculated index
+	bucket_map_value->tophash[idx] = traceparent_tophash;
+	bucket_map_value->keys[idx] = (struct go_string_ot) {.len = CW_HEADER_KEY_LENGTH, .str = ptr};
+	bucket_map_value->values[idx] = (struct go_slice_ot) {.array = header_value_ptr, .cap = 1, .len = 1};
 
 	// Update the map header count field
 	curr_keyvalue_count = curr_keyvalue_count + 1;
 	long res = bpf_probe_write_user(headers_ptr, &curr_keyvalue_count, sizeof(curr_keyvalue_count));
 	if (res < 0) {
-		cw_bpf_debug("Failed to update key-value count in map header");
+		bpf_printk("go_update_header: Failed to update count, res=%ld", res);
 		return -1;
 	}
 
 	// Update the bucket
 	res = bpf_probe_write_user(bucket_ptr, bucket_map_value, sizeof(struct map_bucket));
 	if (res < 0) {
-		cw_bpf_debug("Failed to update bucket content");
+		bpf_printk("go_update_header: Failed to update bucket, res=%ld", res);
 		return -1;
 	}
 
 	bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
+	bpf_printk("go_update_header: SUCCESS - old hmap injection completed");
 	return 0;
 }
 

+ 8 - 0
ebpftracer/ebpf/utrace/go/net/server.probe.bpf.c

@@ -147,6 +147,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) {

+ 209 - 6
ebpftracer/tls.go

@@ -131,11 +131,14 @@ func (t *Tracer) AttachOpenSslUprobes(pid uint32) ([]link.Link, error) {
 }
 
 func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint16) ([]link.Link, error) {
+	klog.Infof("[AttachGoTlsUprobes] STEP 1: Function entry, pid=%d", pid)
 	if t.DisableL7Tracing() {
+		klog.Infof("[AttachGoTlsUprobes] STEP 1.1: L7 tracing disabled, returning early")
 		return nil, nil
 	}
 
 	path := proc.Path(pid, "exe")
+	klog.Infof("[AttachGoTlsUprobes] STEP 2: Got executable path, pid=%d, path=%s", pid, path)
 	instanceID := appInfo.InstanceIdHash.HashtVal
 	appID := appInfo.AppIdHash.HashtVal
 	var err error
@@ -153,60 +156,85 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 		klog.Infof("pid=%d golang_app=%s golang_version=%s: %s", pid, name, version, msg)
 	}
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 3: Reading buildinfo from %s", path)
 	bi, err := buildinfo.ReadFile(path)
 	if err != nil {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 3.1: Failed to read buildinfo, pid=%d, error=%v", pid, err)
 		log("failed to read build info", err)
 		return nil, err
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 3.2: Buildinfo read successfully, GoVersion=%s", bi.GoVersion)
 	// isGolangApp = true
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 4: Reading executable link")
 	name, err = os.Readlink(path)
 	if err != nil {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 4.1: Failed to readlink, pid=%d, error=%v", pid, err)
 		log("failed to read name", err)
 		return nil, err
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 4.2: Executable name=%s", name)
+	
 	version = strings.Replace(bi.GoVersion, "go", "v", 1)
+	klog.Infof("[AttachGoTlsUprobes] STEP 5: Checking version compatibility, version=%s, minSupported=%s", version, minSupportedGoVersion)
 	if semver.Compare(version, minSupportedGoVersion) < 0 {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 5.1: Version too old, version=%s < minSupported=%s", version, minSupportedGoVersion)
 		log(fmt.Sprintf("go_versions below %s are not supported", minSupportedGoVersion), nil)
 		return nil, err
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 5.2: Version check passed")
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 6: Opening ELF file")
 	ef, err := elf.Open(path)
 	if err != nil {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 6.1: Failed to open ELF, pid=%d, error=%v", pid, err)
 		log("failed to open as elf binary", err)
 		return nil, err
 	}
 	defer ef.Close()
+	klog.Infof("[AttachGoTlsUprobes] STEP 6.2: ELF file opened successfully")
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 7: Reading symbols")
 	symbols, err := ef.Symbols()
 	if err != nil {
 		if errors.Is(err, elf.ErrNoSymbols) {
+			klog.Warnf("[AttachGoTlsUprobes] STEP 7.1: No symbol section, pid=%d", pid)
 			log("no symbol section", nil)
 			return nil, err
 		}
+		klog.Errorf("[AttachGoTlsUprobes] STEP 7.2: Failed to read symbols, pid=%d, error=%v", pid, err)
 		log("failed to read symbols", err)
 		return nil, err
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 7.3: Symbols read successfully, count=%d", len(symbols))
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 8: Reading .text section")
 	textSection := ef.Section(".text")
 	if textSection == nil {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 8.1: No .text section, pid=%d", pid)
 		log("no text section", nil)
 		return nil, err
 	}
 	textSectionData, err := textSection.Data()
 	if err != nil {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 8.2: Failed to read .text section data, pid=%d, error=%v", pid, err)
 		log("failed to read text section", err)
 		return nil, err
 	}
 	textSectionLen := uint64(len(textSectionData) - 1)
+	klog.Infof("[AttachGoTlsUprobes] STEP 8.3: .text section read, size=%d", textSectionLen)
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 9: Opening executable for uprobe")
 	exe, err := link.OpenExecutable(path)
 	if err != nil {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 9.1: Failed to open executable for uprobe, pid=%d, error=%v", pid, err)
 		log("failed to open executable", err)
 		return nil, err
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 9.2: Executable opened for uprobe")
 
 	// 检测 gRPC 版本
+	klog.Infof("[AttachGoTlsUprobes] STEP 10: Detecting gRPC version")
 	var grpcMajorVersion, grpcMinorVersion int
 	for _, dep := range bi.Deps {
 		if strings.Contains(dep.Path, "grpc") {
@@ -245,34 +273,141 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 			}
 		}
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 10.1: gRPC version detection completed, major=%d, minor=%d", grpcMajorVersion, grpcMinorVersion)
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 11: Getting offset for runtime.g.goid")
 	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)
+	}
+
+	klog.Infof("[AttachGoTlsUprobes] STEP 12: Getting offset for runtime.hmap.buckets")
 	bucketsOff, ok2 := tracer.GetOffset(tracer.NewID("std", "runtime", "hmap", "buckets"), path)
+	
+	// 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)
+	}
 
-	if ok && ok2 {
+	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 {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 14.2: Error converting major version, error=%v", err)
 				log("Error converting major version:", err)
 				return nil, err
 			}
 			minor, err = strconv.Atoi(parts[1])
 			if err != nil {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 14.3: Error converting minor version, error=%v", err)
 				log("Error converting minor version:", err)
 				return nil, err
 			}
 			if len(parts) >= 3 {
 				revision, err = strconv.Atoi(parts[2])
 				if err != nil {
+					klog.Warnf("[AttachGoTlsUprobes] STEP 14.4: Error converting revision version, error=%v", err)
 					log("Error converting revision version:", err)
 				}
 			}
 
+			klog.Infof("[AttachGoTlsUprobes] STEP 14.5: Parsed version, major=%d, minor=%d, revision=%d", major, minor, revision)
 			goVersion := ((major & 0xFF) << 16) + ((minor & 0xFF) << 8) + min(revision, 255)
+			klog.Infof("[AttachGoTlsUprobes] STEP 14.6: Encoded version=%d (0x%x)", goVersion, goVersion)
 
+			klog.Infof("[AttachGoTlsUprobes] STEP 15: Initializing EbpfProcInfo structure")
 			info := EbpfProcInfo{}
 			info.Version = uint32(goVersion)
 			info.Offsets[OFFSET_IDX_GOID_RUNTIME_G] = uint16(offset)
@@ -284,11 +419,20 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 			info.CodeType = codeType
 			if grpcMajorVersion >= 1 && grpcMinorVersion >= 60 {
 				info.IsNewFramePos = 1
+				klog.Infof("[AttachGoTlsUprobes] STEP 15.1: Using new frame position for gRPC >= 1.60")
 			} else {
 				info.IsNewFramePos = 0
+				klog.Infof("[AttachGoTlsUprobes] STEP 15.2: 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"),
@@ -303,26 +447,39 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 				&info.StreamCtxPos: tracer.NewID("google.golang.org/grpc","google.golang.org/grpc/internal/transport","Stream","ctx"),
 			}
 
+			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] STEP 16.3: Field offset collection completed, success=%d, failed=%d", successCount, failCount)
 
+			klog.Infof("[AttachGoTlsUprobes] STEP 17: Allocating memory for process")
 			// 获取内存地址
 			if appInfo.GoProcCache.StartAddr == 0 && appInfo.GoProcCache.EndAddr == 0 {
+				klog.Infof("[AttachGoTlsUprobes] STEP 17.1: Cache empty, calling Allocate")
 				allocDetails, allocErr := tracer.Allocate(int(pid))
 				if allocErr != nil {
+					klog.Errorf("[AttachGoTlsUprobes] STEP 17.2: Allocate failed, pid=%d, error=%v", pid, allocErr)
 					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] STEP 17.3: Allocate succeeded, StartAddr=0x%x, EndAddr=0x%x", allocDetails.StartAddr, allocDetails.EndAddr)
+				} else {
+					klog.Warnf("[AttachGoTlsUprobes] STEP 17.4: Allocate returned nil")
 				}
+			} else {
+				klog.Infof("[AttachGoTlsUprobes] STEP 17.5: 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
@@ -332,26 +489,63 @@ 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] STEP 18: Updating proc_info map")
 			_, err = tracer.UpdateProcInfoToMap(t.collection, pid, info)
 			if err != nil {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 18.1: Failed to update proc_info map, pid=%d, error=%v", pid, err)
 				klog.Error("failed to update program info", err)
 				return nil, err
 			}
+			klog.Infof("[AttachGoTlsUprobes] STEP 18.2: Proc_info map updated successfully")
 			appInfo.EBPFProcInfo = &info
+		} else {
+			klog.Errorf("[AttachGoTlsUprobes] STEP 13.2: Skipping proc_info initialization due to missing offsets, goid_ok=%v, buckets_ok=%v", ok, ok2)
+			if !ok {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 13.3: runtime.g.goid offset missing - this is critical!")
+			}
+			if !ok2 {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 13.4: runtime.hmap.buckets offset missing - Go 1.24+ may use new map implementation")
+			}
 		}
 	}
 
+	klog.Infof("[AttachGoTlsUprobes] STEP 19: 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, 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.5: Matched net/http.serverHandler.ServeHTTP symbol (index=%d)", i)
+		case goTransport:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.6: Matched net/http.Transport.roundTrip symbol (index=%d)", i)
+		case goGrpcClientConnInvoke:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.7: Matched gRPC ClientConn.Invoke symbol (index=%d)", i)
+		case goGrpcHttp2OperateHeader, goGrpcServerHandleStream, goGrpcServerWritestatus, goGrpcClientLoopyHeaderHandler, goGrpcHttp2ClientNewStream:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.8: Matched gRPC symbol: %s (index=%d)", s.Name, i)
 		default:
 			continue
 		}
+		klog.Debugf("[AttachGoTlsUprobes] STEP 19.9: 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 {
@@ -367,22 +561,28 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 
 		switch s.Name {
 		case goExecute:
+			klog.Infof("[AttachGoTlsUprobes] STEP 20: Attaching uprobe for runtime.execute, address=0x%x", address)
 			l, err := exe.Uprobe(s.Name, t.uprobes["runtime_execute"], &link.UprobeOptions{Address: address})
 			if err != nil {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 20.1: Failed to attach runtime.execute uprobe, error=%v", err)
 				log("failed to attach write_enter uprobe", err)
 				klog.Infoln("runtime.execute no")
 				return nil, err
 			} else {
+				klog.Infof("[AttachGoTlsUprobes] STEP 20.2: Successfully attached runtime.execute uprobe")
 				klog.Infoln("runtime.execute ok")
 			}
 			links = append(links, l)
 
 		case goNewproc1:
+			klog.Infof("[AttachGoTlsUprobes] STEP 21: Attaching uprobe for runtime.newproc1, address=0x%x", address)
 			l, err := exe.Uprobe(s.Name, t.uprobes["enter_runtime_newproc1"], &link.UprobeOptions{Address: address})
 			if err != nil {
+				klog.Errorf("[AttachGoTlsUprobes] STEP 21.1: Failed to attach enter_runtime_newproc1 uprobe, error=%v", err)
 				log("failed to attach newproc1 uprobe", err)
 				return nil, err
 			}
+			klog.Infof("[AttachGoTlsUprobes] STEP 21.2: Successfully attached enter_runtime_newproc1 uprobe")
 			links = append(links, l)
 			sStart := s.Value - textSection.Addr
 			sEnd := sStart + s.Size
@@ -629,9 +829,12 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 			}
 		}
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 22: Symbol processing completed, matched symbols=%d, total links=%d", matchedSymbols, len(links))
 	if len(links) == 0 {
+		klog.Errorf("[AttachGoTlsUprobes] STEP 22.1: No uprobes attached, returning error")
 		return nil, err
 	}
+	klog.Infof("[AttachGoTlsUprobes] STEP 23: Function completed successfully, attached %d uprobes", len(links))
 	klog.Infoln("crypto/tls uprobes attached")
 	return links, nil
 }