浏览代码

Merge branch 'dev'

carl 5 月之前
父节点
当前提交
5812042622
共有 100 个文件被更改,包括 17600 次插入1719 次删除
  1. 5 0
      .gitmodules
  2. 3 11
      Dockerfile
  3. 15 0
      Dockerfile.arm64
  4. 464 0
      GO_1.24_SWISS_TABLES_ADAPTATION.md
  5. 95 11
      Makefile
  6. 2 2
      Makefile2
  7. 12 1
      containers/apm_register_app.go
  8. 11 3
      containers/apm_register_host.go
  9. 2 2
      containers/apm_stack_dispatch.go
  10. 19 0
      containers/apm_white_list_v2.go
  11. 16 10
      containers/container.go
  12. 360 79
      containers/container_apm.go
  13. 3 1
      containers/metrics.go
  14. 5 4
      containers/process.go
  15. 111 21
      containers/registry.go
  16. 6 0
      dist/aarch64/.gitignore
  17. 87 0
      dist/aarch64/README.md
  18. 二进制
      dist/aarch64/package_dir/agents/NativeAgent/lib/apmAgent.jar
  19. 二进制
      dist/aarch64/package_dir/agents/NativeAgent/lib/apmCore.jar
  20. 二进制
      dist/aarch64/package_dir/agents/NativeAgent/lib/apmSpy.jar
  21. 二进制
      dist/aarch64/package_dir/agents/NativeAgent/lib/libnativeAgent.so
  22. 二进制
      dist/aarch64/package_dir/agents/NativeAgent/plugins/cloudwise-javacode-plugin-ssl-socket.jar
  23. 244 0
      dist/aarch64/package_dir/bin/agentctl
  24. 0 0
      dist/aarch64/package_dir/conf/.gitkeep
  25. 二进制
      dist/aarch64/package_dir/libs/amd64/jvm/cwlibnet.so
  26. 二进制
      dist/aarch64/package_dir/libs/arm64/jvm/cwlibnet.so
  27. 0 0
      dist/aarch64/package_dir/logs/.gitkeep
  28. 0 0
      dist/aarch64/package_dir/runtime/.gitkeep
  29. 二进制
      dist/aarch64/package_dir/scripts/cwjattach
  30. 2954 0
      dist/aarch64/scripts/install_temp.sh
  31. 611 0
      dist/aarch64/scripts/package.sh
  32. 1116 0
      dist/aarch64/scripts/uninstall.sh
  33. 二进制
      dist/aarch64/scripts/xzdec
  34. 二进制
      dist/package_dir/agents/NativeAgent/plugins/cloudwise-javacode-plugin-ssl-socket.jar
  35. 6 0
      dist/x86_64/.gitignore
  36. 87 0
      dist/x86_64/README.md
  37. 二进制
      dist/x86_64/package_dir/agents/NativeAgent/lib/apmAgent.jar
  38. 二进制
      dist/x86_64/package_dir/agents/NativeAgent/lib/apmCore.jar
  39. 二进制
      dist/x86_64/package_dir/agents/NativeAgent/lib/apmSpy.jar
  40. 二进制
      dist/x86_64/package_dir/agents/NativeAgent/lib/libnativeAgent.so
  41. 二进制
      dist/x86_64/package_dir/agents/NativeAgent/plugins/cloudwise-javacode-plugin-ssl-socket.jar
  42. 244 0
      dist/x86_64/package_dir/bin/agentctl
  43. 0 0
      dist/x86_64/package_dir/conf/.gitkeep
  44. 二进制
      dist/x86_64/package_dir/libs/amd64/jvm/cwlibnet.so
  45. 二进制
      dist/x86_64/package_dir/libs/arm64/jvm/cwlibnet.so
  46. 0 0
      dist/x86_64/package_dir/logs/.gitkeep
  47. 0 0
      dist/x86_64/package_dir/runtime/.gitkeep
  48. 二进制
      dist/x86_64/package_dir/scripts/cwjattach
  49. 2954 0
      dist/x86_64/scripts/install_temp.sh
  50. 611 0
      dist/x86_64/scripts/package.sh
  51. 1116 0
      dist/x86_64/scripts/uninstall.sh
  52. 二进制
      dist/x86_64/scripts/xzdec
  53. 12 0
      ebpftracer/bindata_linux_arm64.go
  54. 1 0
      ebpftracer/ebpf/config.h
  55. 10 3
      ebpftracer/ebpf/ebpf.c
  56. 65 3
      ebpftracer/ebpf/include/apm_trace.h
  57. 10 10
      ebpftracer/ebpf/include/socket_trace.h
  58. 30 0
      ebpftracer/ebpf/include/socket_trace_common.h
  59. 42 4
      ebpftracer/ebpf/l7/apm_trace.c
  60. 4 4
      ebpftracer/ebpf/l7/cassandra.c
  61. 1 1
      ebpftracer/ebpf/l7/gotls.c
  62. 108 66
      ebpftracer/ebpf/l7/l7.c
  63. 34 7
      ebpftracer/ebpf/l7/memcached.c
  64. 0 2
      ebpftracer/ebpf/l7/mongo.c
  65. 26 6
      ebpftracer/ebpf/l7/mysql.c
  66. 1 1
      ebpftracer/ebpf/l7/openssl.c
  67. 13 1
      ebpftracer/ebpf/l7/redis.c
  68. 3 4
      ebpftracer/ebpf/tcp/state.c
  69. 19 9
      ebpftracer/ebpf/uprobe_base_bpf.c
  70. 445 0
      ebpftracer/ebpf/utrace/go/db/gocql.probe.bpf.c
  71. 186 116
      ebpftracer/ebpf/utrace/go/include/alloc.h
  72. 58 0
      ebpftracer/ebpf/utrace/go/include/go_net.h
  73. 125 21
      ebpftracer/ebpf/utrace/go/include/go_types.h
  74. 4 2
      ebpftracer/ebpf/utrace/go/include/span_context.h
  75. 6 5
      ebpftracer/ebpf/utrace/go/include/utils.h
  76. 406 0
      ebpftracer/ebpf/utrace/go/mq/kafka/consumer.probe.bpf.c
  77. 552 0
      ebpftracer/ebpf/utrace/go/mq/kafka/producer.probe.bpf.c
  78. 189 347
      ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c
  79. 448 0
      ebpftracer/ebpf/utrace/go/net/grpc.client.probe.bpf.c
  80. 719 0
      ebpftracer/ebpf/utrace/go/net/grpc.server.probe.bpf.c
  81. 101 237
      ebpftracer/ebpf/utrace/go/net/server.probe.bpf.c
  82. 3 4
      ebpftracer/ebpf/utrace/java/include/java_common.h
  83. 21 23
      ebpftracer/ebpf/utrace/java/net/client.probe.bpf.c
  84. 9 1
      ebpftracer/jvm.go
  85. 427 0
      ebpftracer/l7/cassandra.go
  86. 90 19
      ebpftracer/l7/http.go
  87. 66 3
      ebpftracer/l7/l7.go
  88. 182 16
      ebpftracer/l7/mongo.go
  89. 54 7
      ebpftracer/stack.go
  90. 596 142
      ebpftracer/tls.go
  91. 105 22
      ebpftracer/tracer.go
  92. 1 1
      ebpftracer/tracer/allocate.go
  93. 225 0
      ebpftracer/tracer/inject/inject.go
  94. 347 176
      ebpftracer/tracer/inject/inject_linux_amd64.go
  95. 559 240
      ebpftracer/tracer/inject/inject_linux_arm64.go
  96. 二进制
      ebpftracer/tracer/inject/lib/libhotpatch_arm64.a
  97. 4 3
      ebpftracer/tracer/offset.go
  98. 1 0
      external/hotpatch
  99. 55 68
      flags/flags.go
  100. 78 0
      flags/port_whitelist.go

+ 5 - 0
.gitmodules

@@ -0,0 +1,5 @@
+[submodule "external/hotpatch"]
+	path = external/hotpatch
+	url = https://git.cloudwise.com/TSB/hotpatch.git
+[submodule "hotpatch"]
+	branch = master

+ 3 - 11
Dockerfile

@@ -1,21 +1,13 @@
-FROM ubuntu:22.04
-
+FROM harbor.cloudwise.com/apm/euspace-base:22.04-amd64
 ARG EUSPACE_BASE_PATH=/opt/cloudwise/apm/euspace
 RUN mkdir -p $EUSPACE_BASE_PATH
-
 #拷贝安装目录结构
-ADD ./dist/package_dir/  $EUSPACE_BASE_PATH/
-
+ADD ./dist/x86_64/package_dir/  $EUSPACE_BASE_PATH/
 ARG EUSPACE_BIN_PATH=$EUSPACE_BASE_PATH/bin
-
 # 拷贝euspace可执行文件,make直接生成到dist/package_dir/bin/euspace
 #COPY ./euspace $EUSPACE_BIN_PATH/
-
 # 设置工作目录
 WORKDIR $EUSPACE_BIN_PATH
-
 # 设置PATH变量
 ENV PATH=$PATH:$EUSPACE_BIN_PATH
-
-ENTRYPOINT ["euspace"]
-
+ENTRYPOINT ["euspace"]

+ 15 - 0
Dockerfile.arm64

@@ -0,0 +1,15 @@
+FROM harbor.cloudwise.com/apm/euspace-base:22.04-arm64
+ARG EUSPACE_BASE_PATH=/opt/cloudwise/apm/euspace
+RUN mkdir -p $EUSPACE_BASE_PATH
+RUN uname -m && cat /etc/os-release
+
+# 拷贝安装目录结构
+ADD ./dist/aarch64/package_dir/  $EUSPACE_BASE_PATH/
+
+ARG EUSPACE_BIN_PATH=$EUSPACE_BASE_PATH/bin
+#COPY ./euspace $EUSPACE_BIN_PATH/
+
+WORKDIR $EUSPACE_BASE_PATH
+ENV PATH=$PATH:$EUSPACE_BIN_PATH
+
+ENTRYPOINT ["euspace"]

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

+ 95 - 11
Makefile

@@ -1,6 +1,28 @@
+# 统一的 Makefile - 支持 x86 和 ARM 架构
+# 使用方法:
+#   make ARCH=x86 all          - 构建 x86 版本
+#   make ARCH=arm image        - 构建并推送 ARM 镜像
+#   make ARCH=arm only-build   - 构建 ARM 版本
+
+arch ?= x86
+
+# 根据架构设置变量
+ifeq ($(arch),arm)
+	DOCKER_CONTAINER = euspace-arm
+	IMAGE_TAG_SUFFIX = arm64
+	PLATFORM = linux/arm64
+	DOCKERFILE = Dockerfile.arm64
+else
+	DOCKER_CONTAINER = euspace
+	IMAGE_TAG_SUFFIX = amd64
+	PLATFORM = linux/amd64
+	DOCKERFILE = Dockerfile
+endif
+
 VERSION=0.0.1
 FILTER=
 PARAMS=
+
 # Set debug
 ifeq ($(debug),1)
 PARAMS+=debug=1
@@ -21,34 +43,78 @@ endif
 
 # Set eBPF build kernel
 ifdef kernel
-	PARAMS+= kernel=${kernel}
+	PARAMS+=kernel=${kernel}
 endif
 
 ifdef version
 	VERSION=${version}
+	PARAMS+=version=${version}
 endif
 
 GIT_COMMIT=$(shell git rev-parse --short HEAD)
 BUILD_DATE=$(shell date '+%Y-%m-%d-%H:%M:%S')
+INNER_ARCH=$(shell uname -m)
 
 # Set Version
-TARGET_FILE=dist/package_dir/bin/euspace
+TARGET_FILE=dist/${INNER_ARCH}/package_dir/bin/euspace
 GO_LD_FLAGS_VERSION=-X github.com/coroot/coroot-node-agent/flags.AgentVersion=${VERSION}
 GO_LD_FLAGS_GIT_COMMIT=-X github.com/coroot/coroot-node-agent/flags.GitCommit=${GIT_COMMIT}
 GO_LD_FLAGS_BUILD_DATE=-X github.com/coroot/coroot-node-agent/flags.BuildDate=${BUILD_DATE}
 GO_LD_FLAGS=-ldflags="${GO_LD_FLAGS_VERSION} ${GO_LD_FLAGS_GIT_COMMIT} ${GO_LD_FLAGS_BUILD_DATE}"
 
-all: c-build go-build
+# 帮助信息
+help:
+	@echo "Usage: make arch=<x86|arm> <target>"
+	@echo ""
+	@echo "Architecture options (case-insensitive):"
+	@echo "  arch=x86     - Build for x86_64 architecture (default)"
+	@echo "  arch=arm     - Build for ARM64 architecture"
+	@echo ""
+	@echo "Build targets:"
+	@echo "  all          - Build (ebpf-build + go-build)"
+	@echo "  ebpf-build   - Build eBPF components"
+	@echo "  go-build     - Build Go application"
+	@echo "  only-build   - Build only (ebpf-build + go-build)"
+	@echo "  docker-build - Build in containerized environment (for CI/CD)"
+	@echo ""
+	@echo "eBPF kernel parameters (only for ebpf-build):"
+	@echo "  kernel=all   - Build for all supported kernels (512, 506, 420, 416)"
+	@echo "  kernel=512   - Build for kernel 5.12 only"
+	@echo "  kernel=506   - Build for kernel 5.06 only"
+	@echo "  kernel=420   - Build for kernel 4.20 only"
+	@echo "  kernel=416   - Build for kernel 4.16 only"
+	@echo ""
+	@echo "Version parameters (only for go-build / image-build):"
+	@echo "  version=<ver> - Set version number for Go application"
+	@echo "                  version=1.1.2, version=2.0.0, etc."
+	@echo ""
+	@echo "Docker targets:"
+	@echo "  image-build  - Build Docker image"
+	@echo "  image-push   - Push Docker image"
+	@echo "  image        - Build and push image"
+	@echo ""
+	@echo "Examples:"
+	@echo "  make arch=x86 all          - Build for x86"
+	@echo "  make arch=arm image        - Build and push ARM image"
+	@echo "  make arch=arm only-build   - Build ARM version only"
+	@echo "  make ebpf-build kernel=all - Build eBPF for all kernels"
+	@echo "  make ebpf-build kernel=512 - Build eBPF for kernel 5.12 only"
+	@echo "  make go-build version=1.1.2 - Build Go with version 1.1.2"
+	@echo "  make docker-build kernel=all version=1.0 - CI/CD build with all kernels"
 
-build:
-	CGO_ENABLED=1 go build -gcflags="all=-N -l" ${GO_LD_FLAGS} -buildvcs=false -o ${TARGET_FILE}
-c:
-	docker exec -it 62d0676aa0b7 sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
-c-build: c
+ebpf-build:
+	docker exec -it $(DOCKER_CONTAINER) sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
 
 go-build:
-	docker exec -it 62d0676aa0b7 bash -c 'cd /opt/github/euspace && source ~/.g/env && make build'
-go: go-build run
+	docker exec -it $(DOCKER_CONTAINER) bash -c 'cd /opt/github/euspace && source ~/.g/env && make build ${PARAMS}'
+
+only-build: ebpf-build go-build
+
+all: only-build
+
+build:
+	CGO_ENABLED=1 go build -gcflags="all=-N -l" ${GO_LD_FLAGS} -buildvcs=false -o ${TARGET_FILE}
+	${TARGET_FILE} -v
 
 run:
 	ssh [email protected] 'cd /opt/github/euspace && CONFIG_ENDPOINT=10.0.16.250:18080 && TRACES_ENDPOINT=http://10.0.16.250:18080/docp/api/v2/data/receive ${FILTER} ./euspace --listen="0.0.0.0:8123"'
@@ -59,4 +125,22 @@ docker-build-go:
 	source ~/.g/env; CGO_ENABLED=1 go build -a -gcflags="all=-N -l" ${GO_LD_FLAGS} -buildvcs=false -o ${TARGET_FILE}
 docker-build: docker-build-c docker-build-go
 	@echo Target file: ${TARGET_FILE}
-	@echo Euspaces build success!
+	@echo Euspaces build success!
+
+# Image configuration
+AGENT_IMAGE_NAME=euspace-agent
+IMAGE_TAG=${VERSION}-dev-$(IMAGE_TAG_SUFFIX)
+IMAGE_FULL=${AGENT_IMAGE_NAME}:${IMAGE_TAG}
+
+image-build:
+	docker build --platform=$(PLATFORM) -t harbor.cloudwise.com/apm/${IMAGE_FULL} -f $(DOCKERFILE) .
+
+image-push:
+	docker push harbor.cloudwise.com/apm/${IMAGE_FULL}
+
+image: only-build image-build image-push
+
+debug:
+	exit
+
+.PHONY: help ebpf-build go-build only-build all docker-build image-build image-push image debug

+ 2 - 2
Makefile2

@@ -17,12 +17,12 @@ build:
 	CGO_ENABLED=1 go build -gcflags="all=-N -l" -buildvcs=false -o euspace
 c:
 	#docker exec -it 9d928d96d4d0 sh -c 'cd /opt/github/euspace/ebpftracer && sh build.sh${PARAMS}'
-	docker exec -it 432002584cbf sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
+	docker exec -it 889965bb1d4a sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
 c-build: c
 
 go-build:
 	#ssh [email protected] 'export https_proxy=http://10.0.22.50:4780 && source ~/.g/env && cd /opt/github/euspace && make -f Makefile2 build'
-	docker exec -it 432002584cbf bash -c 'cd /opt/github/euspace && source ~/.g/env && make -f Makefile2 build'
+	docker exec -it 889965bb1d4a bash -c 'cd /opt/github/euspace && source ~/.g/env && make -f Makefile2 build'
 go: go-build
 
 run:

+ 12 - 1
containers/apm_register_app.go

@@ -2,6 +2,7 @@ package containers
 
 import (
 	"fmt"
+	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/utils"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	klog "github.com/sirupsen/logrus"
@@ -9,6 +10,14 @@ import (
 )
 
 func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
+	if *flags.Test {
+		c.AppInfo.AppName = c.WhiteSettingInfo.AppName
+		c.AppInfo.AppIdHash.IntVal, _ = utils.BuildInt64ID(c.WhiteSettingInfo.AppName).ToInt64()
+		c.AppInfo.AppIdHash.HashtVal = utils.BuildInt64ID(c.WhiteSettingInfo.AppName).ToHashByte()
+		c.AppInfo.CodeType = c.GetCodeTypeFromCache(pid)
+		c.AppInfo.SetAppSuccess()
+		return nil
+	}
 	if r.isFusing {
 		return fmt.Errorf("euspace in fusing can not to regist app")
 	}
@@ -26,8 +35,9 @@ func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
 		if reRegFlag {
 			cl = "re "
 		}
+		c.AppInfo.CodeType = c.GetCodeTypeFromCache(pid)
 		if c.AppInfo.CodeType.IsUnknownCode() || c.AppInfo.CodeType.IsWaitCheck() {
-			return fmt.Errorf("[%sregister app] Unknown app code[%s],Wait buildIDs.", cl, c.AppInfo.CodeType.String())
+			return fmt.Errorf("[%sregister app] Unknown app code[%s] .", cl, c.AppInfo.CodeType.String())
 		}
 		c.AppInfo.AppIdHash.IntVal, err = utils.BuildInt64ID(whiteAppName).ToInt64()
 		c.AppInfo.AppIdHash.HashtVal = utils.BuildInt64ID(whiteAppName).ToHashByte()
@@ -55,6 +65,7 @@ func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
 			CodeType:    c.AppInfo.CodeType.Int(),
 			App_type:    1,
 			AppLang:     "ebpf",
+			Sys:         nodeInfo.SysTag,
 		}
 		//klog.Infof("[%sregister app] Register App req: %s.", cl, registerAppReq.String())
 		klog.Infof("[%sregister app] Register App", cl)

+ 11 - 3
containers/apm_register_host.go

@@ -2,10 +2,11 @@ package containers
 
 import (
 	"fmt"
-	. "github.com/coroot/coroot-node-agent/utils/modelse"
-	klog "github.com/sirupsen/logrus"
 	"runtime"
 	"time"
+
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 )
 
 func (r *Registry) RegisterHost() error {
@@ -27,10 +28,17 @@ func (r *Registry) RegisterHost() error {
 		Ebpf:          true,
 		AgentType:     2020,
 	}
-	err := r.connServer.RegisterHost(req)
+	accountID, err := r.connServer.RegisterHost(req)
 	if err != nil {
 		return err
 	}
+
+	// 更新nodeInfo中的AccountID
+	if r.nodeInfo != nil {
+		r.nodeInfo.AccountID = accountID
+		klog.Infof("[register host] Updated account_id to %d", accountID)
+	}
+
 	return nil
 }
 

+ 2 - 2
containers/apm_stack_dispatch.go

@@ -145,7 +145,7 @@ func (c *Container) jvmStackTrace(tracer *ebpftracer.Tracer, pid uint32) error {
 		return fmt.Errorf("[jvmStackTrace] Unsupported Java version")
 	}
 
-	err = tracer.JattachJvm(pid, c.AppInfo, c.WhiteSettingInfo.WhiteStackSettingInfo.WhiteList, c.WhiteSettingInfo.WhiteStackSettingInfo.BlackList)
+	err = tracer.JattachJvm(pid, c.AppInfo, c.WhiteSettingInfo.WhiteStackSettingInfo.WhiteList, c.WhiteSettingInfo.WhiteStackSettingInfo.BlackList, c.getRootfs())
 	if err != nil {
 		p.stackStatus.JattachFailure()
 		return err
@@ -153,7 +153,7 @@ func (c *Container) jvmStackTrace(tracer *ebpftracer.Tracer, pid uint32) error {
 		p.stackStatus.JattachSuccess()
 	}
 
-	jvmStackProbes, err := tracer.AttachJVMStackUprobes(pid, c.AppInfo)
+	jvmStackProbes, err := tracer.AttachJVMStackUprobes(pid, c.AppInfo, c.getRootfs())
 
 	if err != nil {
 		p.stackStatus.StackUprobesFailure()

+ 19 - 0
containers/apm_white_list_v2.go

@@ -1,10 +1,14 @@
 package containers
 
 import (
+	"encoding/json"
 	"fmt"
 	"github.com/coroot/coroot-node-agent/common"
+	"github.com/coroot/coroot-node-agent/utils"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	log "github.com/sirupsen/logrus"
+	"os"
+	"path"
 )
 
 func (r *Registry) getWhiteListAll() []WhiteSettingInfo {
@@ -56,5 +60,20 @@ func (r *Registry) pullWhiteListV2() (bool, error) {
 	//// 更新时间
 	r.whiteLastUpdatedTime = whiteData.LastUpdatedTime
 	r.setWhiteListV2(whiteData)
+	saveRule(whiteData)
 	return true, nil
 }
+
+func saveRule(runtimeApps WhiteDataV2) {
+	appStr, _ := json.Marshal(runtimeApps)
+	dumpPath := path.Join(utils.GetDefaultRuntimePath(), "memdump")
+	err := os.MkdirAll(dumpPath, 0755)
+	if err != nil {
+		log.Error(err)
+	}
+	fileName := path.Join(dumpPath, "rule.snap")
+	err = os.WriteFile(fileName, appStr, 0644)
+	if err != nil {
+		log.Error(err)
+	}
+}

+ 16 - 10
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 {
@@ -633,7 +634,7 @@ func (c *Container) onListenOpen(pid uint32, addr netaddr.IPPort, safe bool) {
 			klog.Warningln(err)
 			return
 		}
-		klog.Infof("got IPs %s for %s", ips, ns.UniqueId())
+		//klog.Infof("got IPs %s for %s", ips, ns.UniqueId())
 		details.NsIPs = ips
 	}
 }
@@ -652,7 +653,7 @@ func (c *Container) onListenClose(pid uint32, addr netaddr.IPPort) {
 }
 
 func (c *Container) onAcceptOpen(pid uint32, fd uint64, src, dst netaddr.IPPort, timestamp uint64, failed bool, duration time.Duration) {
-	klog.Debugf("accept pid=%d id=%s dstaddr=%s srcaddr=%s", pid, c.id, dst.IP(), src.IP())
+	//klog.Debugf("accept pid=%d id=%s dstaddr=%s srcaddr=%s", pid, c.id, dst.IP(), src.IP())
 	// if common.PortFilter.ShouldBeSkipped(dst.Port()) {
 	// 	return
 	// }
@@ -1427,8 +1428,8 @@ func (c *Container) revalidateListens(now time.Time, actualListens map[netaddr.I
 	}
 }
 
-func (c *Container) AttachUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
-	klog.Infoln("[attach] attachUprobes start")
+func (c *Container) AttachUprobes(tracer *ebpftracer.Tracer, pid uint32, _type string) error {
+	klog.Infoln("[attach] attachUprobes start by :", _type)
 	if tracer.DisableL7Tracing() {
 		return nil
 	}
@@ -1448,7 +1449,11 @@ func (c *Container) AttachUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
 		err = c.attachNetCoreUprobes(tracer, pid)
 	}
 	if err != nil {
-		c.DetachUprobes(tracer, pid, APP_UPROBE_ERROR)
+		klog.WithField("pid", pid).Errorf("[attach] error  %v :", err)
+		deErr := c.DetachUprobes(tracer, pid, APP_UPROBE_ERROR)
+		if deErr != nil {
+			klog.WithField("pid", pid).Errorf("[attach] Detach Uprobes error  %v :", deErr)
+		}
 		return err
 	}
 	return nil
@@ -1547,7 +1552,8 @@ func (c *Container) attachJVMUprobes(tracer *ebpftracer.Tracer, pid uint32) erro
 		c.l7AttachSuccess()
 		err = tracer.InitKProcInfo(pid, &c.AppInfo)
 		if err != nil {
-			klog.Errorf("[attach] InitKProcInfo failed pid:[%d] ;%s.", err.Error(), pid)
+			klog.Errorf("[attach] InitKProcInfo failed pid:[%d] ;%s.", pid, err.Error())
+			return err
 		} else {
 			klog.Infof("[attach] InitKProcInfo succeed! pid:[%d]", pid)
 		}

+ 360 - 79
containers/container_apm.go

@@ -5,6 +5,7 @@ import (
 	"bytes"
 	"debug/elf"
 	"fmt"
+	"net"
 	"os"
 	"path"
 	"sort"
@@ -28,10 +29,6 @@ import (
 	"inet.af/netaddr"
 )
 
-const (
-	TRACE_STATUS = 1
-)
-
 func (c *Container) getTrace(traceId uint64) (*tracing.Trace, bool) {
 	trace, ok := c.traceMap[traceId]
 	return trace, ok
@@ -55,24 +52,67 @@ func (c *Container) getOrInitTrace(traceId uint64) (*tracing.Trace, error) {
 	return trace, nil
 }
 
-func (c *Container) InitTrace(traceId uint64, r *l7.RequestData) error {
-	method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
-	ip, err := netaddr.ParseIP(hostIp)
-	if err != nil {
-		//fmt.Println("host ip error")
-		hostIp = "127.0.0.1"
+// getGrpcServerNetworkInfo 获取 gRPC server 的网络信息
+// 返回: IP地址, 端口号, 容器ID
+func (c *Container) getGrpcServerNetworkInfo() (string, uint16, string) {
+	containerID := ""
+	if c.cgroup != nil {
+		containerID = c.cgroup.ContainerId
 	}
-	addr := netaddr.IPPortFrom(ip, port)
-	trace := tracing.NewTrace(string(c.id), addr)
-	if trace == nil {
-		return fmt.Errorf("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is null")
+
+	ipAddr := ""
+	ifaces, err := net.Interfaces()
+	if err == nil {
+		for _, iface := range ifaces {
+			if iface.Name == "eth0" {
+				addrs, err := iface.Addrs()
+				if err == nil {
+					for _, addr := range addrs {
+						var ipnet *net.IPNet
+						switch v := addr.(type) {
+						case *net.IPNet:
+							ipnet = v
+						case *net.IPAddr:
+							ipnet = &net.IPNet{IP: v.IP, Mask: v.IP.DefaultMask()}
+						}
+						if ipnet != nil && ipnet.IP.To4() != nil {
+							ipAddr = ipnet.IP.String()
+							break
+						}
+					}
+				}
+				break
+			}
+		}
 	}
-	c.traceMap[traceId] = trace
+	klog.Debugf("grpc server ip %s", ipAddr)
 
-	trace.TraceStart(method, path, r.Status, r.Duration)
-	return nil
+	// 本地端口尝试从AppInfo.Sport获取
+	port := c.AppInfo.Sport
+	klog.Debugf("grpc server port %d", port)
+
+	return ipAddr, uint16(port), containerID
 }
 
+// Deprecated: InitTrace not used
+//func (c *Container) InitTrace(traceId uint64, r *l7.RequestData) error {
+//	method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
+//	ip, err := netaddr.ParseIP(hostIp)
+//	if err != nil {
+//		//fmt.Println("host ip error")
+//		hostIp = "127.0.0.1"
+//	}
+//	addr := netaddr.IPPortFrom(ip, port)
+//	trace := tracing.NewTrace(string(c.id), addr)
+//	if trace == nil {
+//		return fmt.Errorf("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is null")
+//	}
+//	c.traceMap[traceId] = trace
+//
+//	trace.TraceStart(method, path, r.Status, r.Duration)
+//	return nil
+//}
+
 // 在任意阶段,r.TraceId 不等于0 则创建 traceMap && createParentSpan
 // 更新 createTraceSpan 机制,更新触发traceEnd机制,当事件个数满足时,任意event均可触发end
 
@@ -108,20 +148,51 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 	//if !c.valuableTrace(r.TraceId) {
 	//	return nil
 	//}
-	// klog.Infof("====ProtocolTrace+++++ start==== %d %d", pid, r.TraceId)
+	//klog.Infof("====ProtocolTrace+++++ start==== %d %d", pid, r.TraceId)
 	// klog.Infof("====ProtocolTrace===== start==== %d %d", r.Protocol == l7.ProtocolTrace, c.l7Attach)
-	if r.Protocol == l7.ProtocolTrace && c.l7Attach && c.valuableTrace(r.TraceId) {
+	if c.l7Attach && c.valuableTrace(r.TraceId) {
 		// klog.Infof("====ProtocolTrace---- start==== %d %d", pid, r.TraceId)
 		if r.TraceStart == TRACE_STATUS {
 			// klog.Infof("====ProtocolTrace start==== %d %d", pid, r.TraceId)
 			trace, err := c.getOrInitTrace(r.TraceId)
-			klog.Debugf("payload:[%s]", r.Payload)
+			if c.AppInfo.AppName != "" {
+				klog.Debugf("->>> [%s] -> payload:[%s]", c.AppInfo.AppName, r.Payload)
+			}
 			if err == nil {
-				method, requestURI, sn, sport := l7.ParseHttpHost(r.Payload)
-				ip, _ := netaddr.ParseIP(sn)
-				//codeType := c.GetCodeTypeFromCache(pid)
-				trace.TraceStartEvent(method, requestURI, sn, sport, r.Status, netaddr.IPPortFrom(ip, sport), pid, c.GetAppInfo())
-				c.SendEvent(trace, r.TraceId)
+				switch r.Protocol {
+				case l7.ProtocolHTTP:
+					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, userAgent, sport, r.Status, netaddr.IPPortFrom(ip, sport), pid, c.GetAppInfo(), container_id)
+					c.SendEvent(trace, r.TraceId)
+				case l7.ProtocolGrpc:
+					// gRPC
+					ipAddr, port, containerID := c.getGrpcServerNetworkInfo()
+					trace.GrpcServerTraceStartEvent(ipAddr, port, r, c.GetAppInfo(), containerID)
+					c.SendEvent(trace, r.TraceId)
+				case l7.ProtocolKafka:
+					var sn string
+					var sport uint16
+					var container_id string
+					conn := c.connectionsByPidFd[PidFd{Pid: pid, Fd: fd}]
+					if conn != nil {
+						sn = conn.ActualDest.IP().String()
+						sport = conn.ActualDest.Port()
+					}
+
+					if c.cgroup != nil {
+						container_id = c.cgroup.ContainerId
+					}
+					// MQ
+					trace.MQConsumerTraceStartEvent(sn, sport, r, c.GetAppInfo(), container_id)
+					c.SendEvent(trace, r.TraceId)
+				}
 			}
 
 			return nil
@@ -136,32 +207,110 @@ 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)
+			// 检查是否启用 Elasticsearch 检测
+			if *flags.EnableElasticsearchDetection {
+				// 解析 User-Agent 以检测 Elasticsearch 请求
+				method, requestURI, sn, sport, userAgent := l7.ParseHttpHostWithUserAgent(r.Payload, r.IsTls)
+				// 检查是否是 Elasticsearch 请求(通过 User-Agent)
+				isElasticsearch := strings.Contains(strings.ToLower(userAgent), "elasticsearch")
+				apmTrace, err := c.getOrInitTrace(r.TraceId)
+				//fmt.Println("ProtocolHTTP-----", r.TraceId, err)
+				if err == nil {
+					if isElasticsearch {
+						r.Protocol = l7.ProtocolES
+						// Elasticsearch 请求,按照 NoSQL 方式处理
+						query := l7.ParseElasticsearch(r.Payload)
+						if c.AppInfo.AppName != "" {
+							klog.Debugf("[%s] ->>>>> Elasticsearch -> %s:%d Query:[%s]", c.AppInfo.AppName, sn, sport, query)
+						}
+						conn := c.connectionsByPidFd[PidFd{Pid: pid, Fd: fd}]
+						if conn == nil {
+							conn = &ActiveConnection{
+								Dest:       r.ComponentDAddr,
+								ActualDest: r.ComponentDAddr,
+								Timestamp:  timestamp,
+							}
+						}
+						apmTrace.NoSQLTraceQueryEvent(r.Protocol, semconv.DBSystemElasticsearch, method, query, r, conn.Src, conn.ActualDest)
+					} else {
+						// 普通 HTTP 请求
+						apmTrace.HttpTraceRequestEvent(method, requestURI, sn, sport, r)
+					}
+					c.SendEvent(apmTrace, r.TraceId)
+				}
+			} else {
+				// Elasticsearch 检测未启用,使用普通 HTTP 处理
+				method, requestURI, sn, sport := l7.ParseHttpHost(r.Payload, r.IsTls)
+				apmTrace, err := c.getOrInitTrace(r.TraceId)
+				if err == nil {
+					apmTrace.HttpTraceRequestEvent(method, requestURI, sn, sport, r)
+					c.SendEvent(apmTrace, r.TraceId)
+				}
+			}
+		}
+		//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)
-			//fmt.Println("ProtocolHTTP-----", r.TraceId, err)
 			if err == nil {
-				apmTrace.HttpTraceRequestEvent(method, requestURI, sn, sport, r)
+				apmTrace.GrpcClientTraceQueryEvent(r)
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
-		//return nil
 	}
 	conn := c.connectionsByPidFd[PidFd{Pid: pid, Fd: fd}]
 	//fmt.Println("l7.connectionsByPidFd", conn, pid, fd)
 
 	if conn == nil {
-		return nil
+		conn = &ActiveConnection{
+			Dest:       r.ComponentDAddr,
+			ActualDest: r.ComponentDAddr,
+			Timestamp:  timestamp,
+		}
+		//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)
+		//		}
+		//	}
+		//}
 		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()
@@ -171,6 +320,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)
@@ -186,7 +338,9 @@ 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] ->>>>> %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)
 			//fmt.Println("mysql r.TraceId:", r.TraceId)
@@ -195,10 +349,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)
@@ -209,20 +366,32 @@ 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] ->>>>> %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)
 			//fmt.Println("mysql r.TraceId:", r.TraceId)
 			//fmt.Println("ok:", ok)
 			//fmt.Println("traceMap:", len(c.traceMap))
 			if err == nil {
+				dbSystem := semconv.DBSystemMySQL
+				// 根据端口白名单确定协议类型
+				l7Type := flags.GetProtocolByPort(uint16(conn.ActualDest.Port()))
+				if l7Type == l7.ProtocolMariaDB {
+					dbSystem = semconv.DBSystemMariaDB
+				} else if l7Type == l7.ProtocolTiDB {
+					dbSystem = semconv.DBSystemTiDB
+				}
 				//apmTrace.MysqlTraceQuery(query, r.Status.Error(), r.Duration, conn.ActualDest)
 				//apmTrace.MysqlTraceQueryEvent(query, r, conn.ActualDest)
-				apmTrace.SQLTraceQueryEvent(l7.ProtocolMysql, semconv.DBSystemMySQL, query, r, conn.ActualDest)
+				apmTrace.SQLTraceQueryEvent(l7Type, dbSystem, query, r, conn.ActualDest)
 				c.SendEvent(apmTrace, r.TraceId)
 			}
 		}
-
+	/**
+	 * DM (达梦数据库)
+	 */
 	case l7.ProtocolDM:
 		//统计dm的query次数
 		stats.observe(r.Status.String(), "", r.Duration)
@@ -232,21 +401,43 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 				conn.dmParser = l7.NewDmParser()
 			}
 			query := conn.dmParser.Parse(r.Payload, r.StatementId)
+			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.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) {
@@ -254,28 +445,100 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 			//fmt.Println("cmd", cmd)
 			//fmt.Println("args", args)
 			//apmTrace, ok := c.getTrace(r.TraceId)
+			if c.AppInfo.AppName != "" {
+				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)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				apmTrace.GrpcClientTraceQueryEvent(r)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
+		}
+	/**
+	 * MongoDB
+	 */
 	case l7.ProtocolMongo:
 		stats.observe(r.Status.String(), "", r.Duration)
 		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			query := l7.ParseMongo(r.Payload)
+			if c.AppInfo.AppName != "" {
+				klog.Debugf("[%s] ->>>>> MongoDB -> %s SQL:[%s]", c.AppInfo.AppName, conn.ActualDest, query)
+			}
+
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				// 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)
+			}
 		}
-		//query := l7.ParseMongo(r.Payload)
-		//trace.MongoQuery(query, r.Status.Error(), r.Duration)
-	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) {
+			if c.AppInfo.AppName != "" {
+				klog.Debugf("[%s] ->>>>> %s -> %s payload:[%s]", c.AppInfo.AppName, r.Protocol.String(), conn.ActualDest, r.DestAddrString)
+			}
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				apmTrace.MQTraceQueryEvent(r.Protocol, semconv.MessagingKafkaClientID("kafka"), "", "", r, conn.Src, conn.ActualDest)
+				c.SendEvent(apmTrace, 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) {
@@ -326,7 +589,7 @@ func (c *Container) buildIDs(pid uint32) bool {
 		c.AppInfo.InstanceIdHash.HashtVal = strInstanceID.ToHashByte()
 		// strAgentID := utils.BuildInt64ID(fmt.Sprintf("%s:%s", utils.GetHostIP(), string(proc.GetExe(pid))))
 		// c.AppInfo.AgentId, _ = strAgentID.ToInt64()
-		c.AppInfo.CodeType = c.GetCodeTypeFromCache(pid)
+		// c.AppInfo.CodeType = c.GetCodeTypeFromCache(pid)
 		return true
 	}
 	return false
@@ -644,10 +907,10 @@ func (c *Container) verifyAttachConditions(r *Registry, pid uint32) (bool, int)
 			}
 			// 判断规则
 			if strings.Contains(cmdline, ruleVal) {
-				if !codeType.IsJvmCode() {
-					klog.WithField("pid", pid).Warning("[verify] This agent version only supports JVM applications.")
-					return false, 0
-				}
+				//if !codeType.IsJvmCode() {
+				//	klog.WithField("pid", pid).Warning("[verify] This agent version only supports JVM applications.")
+				//	return false, 0
+				//}
 				c.WhiteSettingInfo.AppName = setting.AppName
 				c.WhiteSettingInfo.Filters = setting.Filters
 				klog.WithField("pid", pid).
@@ -716,7 +979,7 @@ func (c *Container) DetachUprobes(tracer *ebpftracer.Tracer, pid uint32, detachT
 		}
 		//delete the proc info form proc_info_map(for kernel) when the uprobe detached
 		if err := tracer.DelKProcInfo(pid); err != nil {
-			return fmt.Errorf("[DetachUprobes] failed to delete KProcInfo for pid %d,detachType:%d", pid, detachType)
+			return fmt.Errorf("[DetachUprobes] failed to delete KProcInfo for pid %d, detach type is:%s", pid, detachType)
 		} else {
 			klog.Infof("[DetachUprobes] delete KProcInfo success for pid %d,detachType:%d", pid, detachType)
 			c.AppInfo.EBPFProcInfo = nil
@@ -782,49 +1045,67 @@ func (c *Container) getRootfs() string {
 }
 
 func (c *Container) BuildActiveApps(runtimeApps map[uint32]AppStatusInfo, pid uint32) {
-	if c.AppInfo.AppName != "" {
-		detail := AppStatusInfo{
-			Pid:        pid,
-			ProcName:   c.containerName,
-			AppName:    c.AppInfo.AppName,
-			Language:   c.AppInfo.CodeType.String(),
-			AppID:      c.AppInfo.AppIdHash.IntVal,
-			AgentID:    c.AppInfo.AgentId,
-			InstanceID: c.AppInfo.InstanceIdHash.IntVal,
-			Sn:         c.AppInfo.Sn,
-			Sport:      c.AppInfo.Sport,
-			RegisterAt: time.Unix(c.AppInfo.RegisterAt, 0).Format("060102 15:04:05"),
-			PreStatus:  c.AppInfo.PreStatus,
-			Status:     c.AppInfo.Status,
-			Rule:       c.WhiteSettingInfo.Filters,
-		}
-		detail.Rule = fmt.Sprintf("%s|%d", c.WhiteSettingInfo.Filters, c.WhiteSettingInfo.WhiteStackSettingInfo.OpenStack)
-		if c.AppInfo.UpdateAt != 0 {
-			detail.UpdateAt = time.Unix(c.AppInfo.UpdateAt, 0).Format("060102 15:04:05")
-		}
-		p := c.processes[pid]
-		if p != nil {
-			detail.StackStatus = p.stackStatus.String()
-			detail.StackStatus += fmt.Sprintf("|vFailed:%v", p.versionFailed)
-		}
-		runtimeApps[pid] = detail
+	if c == nil {
+		//klog.WithField("pid", pid).Warningln("[BuildActiveApps] container_apm is nil.")
+		return
+	}
+	if c.AppInfo.AppName == "" {
+		return
 	}
+	klog.WithField("pid", pid).WithField("appname", c.AppInfo.AppName).Infof("[BuildActiveApps] container %s is running.", c.AppInfo.AppName)
+	detail := AppStatusInfo{
+		Pid:             pid,
+		ProcName:        c.containerName,
+		AppName:         c.AppInfo.AppName,
+		Language:        c.AppInfo.CodeType.String(),
+		LanguageVersion: c.AppInfo.Version,
+		AppID:           c.AppInfo.AppIdHash.IntVal,
+		AgentID:         c.AppInfo.AgentId,
+		InstanceID:      c.AppInfo.InstanceIdHash.IntVal,
+		Sn:              c.AppInfo.Sn,
+		Sport:           c.AppInfo.Sport,
+		RegisterAt:      time.Unix(c.AppInfo.RegisterAt, 0).Format("060102 15:04:05"),
+		PreStatus:       c.AppInfo.PreStatus,
+		Status:          c.AppInfo.Status,
+		Rule:            c.WhiteSettingInfo.Filters,
+		Container:       string(c.id),
+	}
+	detail.Rule = fmt.Sprintf("%s|%d", c.WhiteSettingInfo.Filters, c.WhiteSettingInfo.WhiteStackSettingInfo.OpenStack)
+	if c.AppInfo.UpdateAt != 0 {
+		detail.UpdateAt = time.Unix(c.AppInfo.UpdateAt, 0).Format("060102 15:04:05")
+	}
+	p := c.processes[pid]
+	if p != nil {
+		detail.StackStatus = p.stackStatus.String()
+		v := 0
+		if !p.versionFailed {
+			v = 1
+		}
+		detail.StackStatus += fmt.Sprintf("V=%d", v)
+	}
+	runtimeApps[pid] = detail
 
 }
 
 func (c *Container) AgentCtrl(r *Registry, pid uint32) {
+	if c == nil {
+		//klog.WithField("pid", pid).Warningln("[AgentCtrl] cannot find container.")
+		return
+	}
 	var err error
 	verifyAttachConditions, _ := c.verifyAttachConditions(r, pid)
 
-	// UNINSTALL
+	// fusing UNINSTALL
 	if r.isFusing && c.Isl7AttachSuccess() {
 		c.Detach(r.tracer, pid, APP_FUSE)
+		klog.WithField("pid", pid).Infoln("[AgentCtrl] fusing")
 		return
 	}
 
 	// verify UNINSTALL
 	if !verifyAttachConditions && c.Isl7AttachSuccess() {
 		c.Detach(r.tracer, pid, APP_UNINSTALL)
+		klog.WithField("pid", pid).Infoln("[AgentCtrl] rule uninstall.")
 		return
 	}
 
@@ -835,7 +1116,7 @@ func (c *Container) AgentCtrl(r *Registry, pid uint32) {
 			return
 		}
 		klog.WithField("pid", pid).Infoln("[AgentCtrl] Attach uprobes.")
-		err = c.AttachUprobes(r.tracer, pid)
+		err = c.AttachUprobes(r.tracer, pid, "Agentctrl")
 		if err != nil {
 			klog.WithField("pid", pid).WithError(err).Errorf("[AgentCtrl] Failed attach uprobes error!")
 			return

+ 3 - 1
containers/metrics.go

@@ -89,7 +89,7 @@ var metrics = struct {
 	NetListenInfo:            metric("process_net_tcp_listen_info", "Listen address of the process", "listen_addr", "proxy"),
 	NetConnectionsSuccessful: metric("process_net_tcp_successful_connects_total", "Total number of successful TCP connects", "instance_id", "app_id", "target_app_id", "app_name", "src", "destination", "actual_destination"),
 	NetConnectionsTotalTime:  metric("process_net_tcp_connection_time_seconds_total", "Time spent on TCP connections", "instance_id", "app_id", "target_app_id", "app_name", "src", "destination", "actual_destination"),
-	NetConnectionsFailed:     metric("process_net_tcp_failed_connects_total", "Total number of failed TCP connects", "instance_id", "app_id","target_app_id", "app_name", "destination", "actual_destination"),
+	NetConnectionsFailed:     metric("process_net_tcp_failed_connects_total", "Total number of failed TCP connects", "instance_id", "app_id", "target_app_id", "app_name", "destination", "actual_destination"),
 	NetConnectionsActive:     metric("process_net_tcp_active_connections", "Number of active outbound connections used by the process", "destination", "actual_destination"),
 	NetRetransmits:           metric("process_net_tcp_retransmits_total", "Total number of retransmitted TCP segments", "instance_id", "app_id", "target_app_id", "app_name", "src", "destination", "actual_destination"),
 	NetLatency:               metric("process_net_latency_seconds", "Round-trip time between the process and a remote IP", "destination_ip"),
@@ -127,6 +127,7 @@ var (
 		l7.ProtocolRedis:     {Name: "process_redis_queries_total", Help: "Total number of outbound Redis queries"},
 		l7.ProtocolMemcached: {Name: "process_memcached_queries_total", Help: "Total number of outbound Memcached queries"},
 		l7.ProtocolMysql:     {Name: "process_mysql_queries_total", Help: "Total number of outbound Mysql queries"},
+		l7.ProtocolTiDB:      {Name: "process_tidb_queries_total", Help: "Total number of outbound TiDB queries"},
 		l7.ProtocolMongo:     {Name: "process_mongo_queries_total", Help: "Total number of outbound Mongo queries"},
 		l7.ProtocolKafka:     {Name: "process_kafka_requests_total", Help: "Total number of outbound Kafka requests"},
 		l7.ProtocolCassandra: {Name: "process_cassandra_queries_total", Help: "Total number of outbound Cassandra requests"},
@@ -142,6 +143,7 @@ var (
 		l7.ProtocolRedis:     {Name: "process_redis_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Redis query"},
 		l7.ProtocolMemcached: {Name: "process_memcached_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Memcached query"},
 		l7.ProtocolMysql:     {Name: "process_mysql_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Mysql query"},
+		l7.ProtocolTiDB:      {Name: "process_tidb_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound TiDB query"},
 		l7.ProtocolMongo:     {Name: "process_mongo_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Mongo query"},
 		l7.ProtocolKafka:     {Name: "process_kafka_requests_duration_seconds_total", Help: "Histogram of the execution time for each outbound Kafka request"},
 		l7.ProtocolCassandra: {Name: "process_cassandra_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Cassandra request"},

+ 5 - 4
containers/process.go

@@ -4,14 +4,14 @@ import (
 	"bytes"
 	"context"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/jattach"
-	"github.com/coroot/coroot-node-agent/utils"
-	. "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"
@@ -163,7 +163,8 @@ func (p *Process) DynamicClose(closeType int) {
 
 func (p *Process) uninstallJavaAgent() error {
 	if p.codeType.IsJvmCode() && p.stackAttachOnce {
-		nativeBasePath := utils.GetDefaultAgentsPath("NativeAgent")
+		//nativeBasePath := utils.GetDefaultAgentsPath("NativeAgent")
+		nativeBasePath := "/tmp/NativeAgent"
 		kvPairs := []string{
 			fmt.Sprintf("%s=%s", filepath.Join(nativeBasePath, "lib", "apmAgent.jar"), nativeBasePath),
 			"UNINSTALL",

+ 111 - 21
containers/registry.go

@@ -56,8 +56,8 @@ type Registry struct {
 
 	hostConntrack *Conntrack
 
-	containersById       map[ContainerID]*Container
-	containersByCgroupId map[string]*Container
+	containersById       map[ContainerID]*Container // calcId
+	containersByCgroupId map[string]*Container      // fmt.Sprintf("%s/%d", cg.Id, pid)
 	containersByPid      map[uint32]*Container
 	ip2fqdn              map[netaddr.IP]string
 	ip2fqdnLock          sync.Mutex
@@ -73,6 +73,7 @@ type Registry struct {
 	nodeInfo                *NodeInfoT
 	isFusing                bool
 	IsFuseException         bool
+	RegistryApps            map[uint32]AppStatusInfo
 }
 
 var (
@@ -139,6 +140,7 @@ func NewRegistry(reg prometheus.Registerer, kernelVersion string, nodeInfo *Node
 		whiteListRules:       make(WhiteListMap),
 		trafficStatsUpdateCh: make(chan *TrafficStatsUpdate),
 		nodeInfo:             nodeInfo,
+		RegistryApps:         make(map[uint32]AppStatusInfo),
 	}
 	// 初始化软负载集群节点
 	proxyClient, clientErr := NewProxyClient(*flags.ConfigServer, false)
@@ -174,7 +176,9 @@ func NewRegistry(reg prometheus.Registerer, kernelVersion string, nodeInfo *Node
 	//if err != nil {
 	//	return nil, err
 	//}
-	try.Go(r.PullAllAppInfo, CatchFn)
+	if *flags.SendNetData {
+		try.Go(r.PullAllAppInfo, CatchFn)
+	}
 	//go r.handleEvents(r.events)
 	try.GoParams(r.handleEvents, CatchFn, r.events)
 
@@ -200,12 +204,18 @@ func (r *Registry) Collect(ch chan<- prometheus.Metric) {
 
 func (r *Registry) Close() {
 	r.CloseContainers()
-	r.tracer.Close()
+	if r.tracer != nil {
+		r.tracer.Close()
+	}
 	close(r.events)
 }
 
 func (r *Registry) CloseContainers() {
 	for pid, c := range r.containersByPid {
+		if c == nil {
+			klog.Warnf("container for pid %d is nil, skipping", pid)
+			continue
+		}
 		if c.Isl7AttachSuccess() {
 			c.Detach(r.tracer, pid, APP_UNINSTALL)
 		}
@@ -232,8 +242,34 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				klog.WithError(err).Errorf("connWhiteList error")
 			}
 			runtimeApps := make(map[uint32]AppStatusInfo)
+
+			for pid, appInfo := range r.RegistryApps {
+				c, ok := r.containersByPid[pid]
+				if !ok {
+					klog.WithFields(map[string]interface{}{
+						"pid":     pid,
+						"appname": appInfo.AppName,
+					}).Infof("[handle] proc or container already deleted.")
+					continue
+				}
+				if c == nil {
+					klog.WithFields(map[string]interface{}{
+						"pid":     pid,
+						"appname": appInfo.AppName,
+					}).Infof("[handle] container exists but c.data not ready.")
+				}
+			}
+
 			for pid, c := range r.containersByPid {
-				if c != nil && !common.IsOpenFilter() && !fuseOnce {
+				if c == nil {
+					klog.WithField("pid", pid).Warningln("[handle] container is nil.")
+					app, ok := r.RegistryApps[pid]
+					if ok {
+						klog.WithFields(map[string]interface{}{"pid": pid, "appname": app.AppName}).
+							Infof("[handle] proc or container is deleted.")
+					}
+				}
+				if !common.IsOpenFilter() && !fuseOnce {
 					c.AgentCtrl(r, pid)
 				}
 				c.BuildActiveApps(runtimeApps, pid)
@@ -241,6 +277,10 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				cg, err := proc.ReadCgroup(pid)
 				if err != nil {
 					delete(r.containersByPid, pid)
+					_, ok := r.RegistryApps[pid]
+					if ok {
+						klog.Infof("[handle] cgroup not find %d", pid)
+					}
 					if c != nil {
 						c.onProcessExit(pid, false)
 					}
@@ -248,10 +288,20 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				}
 				if c != nil && cg.Id != c.cgroup.Id {
 					delete(r.containersByPid, pid)
+					_, ok := r.RegistryApps[pid]
+					if ok {
+						klog.Infof("[handle] cgroup id changed %d", pid)
+					}
 					c.onProcessExit(pid, false)
+					// 重新验证cgroup,可能需要重新创建容器
+					if newC := r.getOrCreateContainer(pid); newC != nil {
+						// 更新容器引用
+						r.containersByPid[pid] = newC
+					}
 				}
 			}
-			saveAppInfo(runtimeApps)
+			r.RegistryApps = runtimeApps
+			saveAppInfo(r.RegistryApps)
 			if r.isFusing {
 				fuseOnce = true
 			} else {
@@ -275,6 +325,11 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				for pid, cc := range r.containersByPid {
 					if cc == c {
 						delete(r.containersByPid, pid)
+						app, ok := r.RegistryApps[pid]
+						if ok {
+							klog.WithFields(map[string]interface{}{"pid": pid, "appname": app.AppName}).
+								Infof("[handle] Dead proc or container is deleted from containers.")
+						}
 					}
 				}
 				if ok := prometheus.WrapRegistererWith(setLabels(string(id),
@@ -315,10 +370,20 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				switch { // possible pids wraparound + missed `process-exit` event
 				case c == nil && seen: // ignored
 					delete(r.containersByPid, e.Pid)
+					app, ok := r.RegistryApps[e.Pid]
+					if ok {
+						klog.WithFields(map[string]interface{}{"pid": e.Pid, "appname": app.AppName}).
+							Infof("[handle] [EventTypeProcessStart nil] proc or container is deleted from containers.")
+					}
 				case c != nil: // revalidating by cgroup
 					cg, err := proc.ReadCgroup(e.Pid)
 					if err != nil || cg.Id != c.cgroup.Id {
 						delete(r.containersByPid, e.Pid)
+						app, ok := r.RegistryApps[e.Pid]
+						if ok {
+							klog.WithFields(map[string]interface{}{"pid": e.Pid, "appname": app.AppName}).
+								Infof("[handle] [EventTypeProcessStart cgroup] proc or container is deleted from containers.")
+						}
 						c.onProcessExit(e.Pid, false)
 					}
 				}
@@ -329,15 +394,25 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					}
 				}
 			case ebpftracer.EventTypeProcessExit:
-				if c := r.containersByPid[e.Pid]; c != nil {
+				c := r.containersByPid[e.Pid]
+				cid := ""
+				if c != nil {
 					c.onProcessExit(e.Pid, e.Reason == ebpftracer.EventReasonOOMKill)
+					cid = string(c.id)
 				}
 				delete(r.containersByPid, e.Pid)
+				if app, ok := r.RegistryApps[e.Pid]; ok {
+					klog.WithFields(map[string]interface{}{
+						"pid":     e.Pid,
+						"appname": app.AppName,
+						"cid":     cid,
+					}).Info("[handle] EventTypeProcessExit proc or container is deleted from containers.")
+				}
 
 			case ebpftracer.EventTypeFileOpen:
-				if c := r.getOrCreateContainer(e.Pid); c != nil {
-					c.onFileOpen(e.Pid, e.Fd)
-				}
+				//if c := r.getOrCreateContainer(e.Pid); c != nil {
+				//	c.onFileOpen(e.Pid, e.Fd)
+				//}
 
 			case ebpftracer.EventTypeListenOpen:
 				//fmt.Println("ebpftracer.EventTypeListenOpen==================", e.Pid)
@@ -351,12 +426,16 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					}
 					if common.IsOpenFilter() && common.IsFilterPid(e.Pid) {
 						c.WhiteSettingInfo.AppName = enums.TestApp
+						if os.Getenv("APP_NAME") != "" {
+							c.WhiteSettingInfo.AppName = os.Getenv("APP_NAME")
+						}
+						c.WhiteSettingInfo.WhiteStackSettingInfo.WhiteList = os.Getenv("WHITE_LIST")
 						err := c.RegisterAppInfo(r, e.Pid)
 						if err != nil {
 							klog.WithError(err).Errorf("[registry] Failed registerAppInfo. pid is %d", e.Pid)
 							continue
 						}
-						err = c.AttachUprobes(r.tracer, e.Pid)
+						err = c.AttachUprobes(r.tracer, e.Pid, "Listen Open Event")
 						if err != nil {
 							klog.WithField("pid", e.Pid).WithError(err).Errorf("[AttachUprobes] [end] Failed attach stack trace!")
 						}
@@ -368,7 +447,7 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 						}
 					}
 				} else {
-					klog.Infoln("TCP listen open from unknown container", e)
+					klog.Debugln("TCP listen open from unknown container", e)
 				}
 			case ebpftracer.EventTypeAcceptOpen:
 				//klog.Infoln("ebpftracer.EventTypeAcceptOpen==================", e.Pid)
@@ -376,7 +455,7 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					c.onAcceptOpen(e.Pid, e.Fd, e.SrcAddr, e.DstAddr, e.Timestamp, false, e.Duration)
 					c.eventReady()
 				} else {
-					klog.Infoln("TCP connection from unknown container", e)
+					klog.Debugln("TCP connection from unknown container", e)
 				}
 			case ebpftracer.EventTypeConnectionOpen:
 				//fmt.Println("ebpftracer.EventTypeConnectionOpen==================", e.Pid)
@@ -389,12 +468,15 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					}
 					if common.IsOpenFilter() && common.IsFilterPid(e.Pid) {
 						c.WhiteSettingInfo.AppName = enums.TestApp
+						if os.Getenv("APP_NAME") != "" {
+							c.WhiteSettingInfo.AppName = os.Getenv("APP_NAME")
+						}
 						err := c.RegisterAppInfo(r, e.Pid)
 						if err != nil {
 							klog.WithError(err).Errorf("[registry] Failed registerAppInfo. pid is %d", e.Pid)
 							continue
 						}
-						err = c.AttachUprobes(r.tracer, e.Pid)
+						err = c.AttachUprobes(r.tracer, e.Pid, "Connection Open Event")
 						if err != nil {
 							klog.WithField("pid", e.Pid).WithError(err).Errorf("[AttachUprobes] [end] Failed attach stack trace!")
 						}
@@ -409,7 +491,7 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 						}
 					}
 				} else {
-					klog.Infoln("TCP connection from unknown container", e)
+					klog.Debugln("TCP connection from unknown container", e)
 				}
 			case ebpftracer.EventTypeListenClose:
 				if c := r.containersByPid[e.Pid]; c != nil {
@@ -419,7 +501,7 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				if c := r.getOrCreateContainer(e.Pid); c != nil {
 					c.onConnectionOpen(e.Pid, e.Fd, e.SrcAddr, e.DstAddr, 0, true, e.Duration)
 				} else {
-					klog.Infoln("TCP connection error from unknown container", e)
+					klog.Debugln("TCP connection error from unknown container", e)
 				}
 			case ebpftracer.EventTypeConnectionClose:
 				if c := r.containersByPid[e.Pid]; c != nil {
@@ -438,7 +520,9 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				}
 			case ebpftracer.EventTypeL7Request:
 
-				//fmt.Println("e.L7Request Payload:", string(e.L7Request.Payload))
+				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
 				}
@@ -447,7 +531,7 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					//fmt.Println("EventTypeL7Request", e.Pid, c.Isl7AttachSuccess())
 					//a, _ := json.Marshal(e.L7Request)
 					//fmt.Println("EventTypeL7Request", e.Pid, string(a))
-					klog.Debugln("Payload:", string(e.L7Request.Payload))
+					//klog.Debugln("Payload:", string(e.L7Request.Payload))
 					ip2fqdn := c.onL7RequestApm(e.Pid, e.Fd, e.Timestamp, e.L7Request)
 					r.ip2fqdnLock.Lock()
 					for ip, fqdn := range ip2fqdn {
@@ -485,9 +569,13 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container {
 	}
 	cg, err := proc.ReadCgroup(pid)
 	if err != nil {
-		if !common.IsNotExist(err) {
-			klog.Warningln("failed to read proc cgroup:", err)
+		if common.IsNotExist(err) {
+			return nil
+		}
+		if strings.Contains(err.Error(), "/reserved.slice/") {
+			return nil
 		}
+		klog.Warningln("failed to read proc cgroup:", err)
 		return nil
 	}
 	cgId := fmt.Sprintf("%s/%d", cg.Id, pid)
@@ -542,7 +630,9 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container {
 	}
 	c, err := NewContainer(id, cg, md, r.hostConntrack, pid, r)
 	if err != nil {
-		klog.Warningf("failed to create container pid=%d cg=%s id=%s: %s", pid, cg.Id, id, err)
+		if err.Error() != "no such file or directory" {
+			klog.Warningf("failed to create container pid=%d cg=%s id=%s: %s", pid, cg.Id, id, err)
+		}
 		return nil
 	}
 

+ 6 - 0
dist/aarch64/.gitignore

@@ -0,0 +1,6 @@
+.idea
+installer.version
+Cloudwise-euspace-installer*.sh
+logs/*
+package_dir/logs/*.log
+package_dir/runtime/memdump

+ 87 - 0
dist/aarch64/README.md

@@ -0,0 +1,87 @@
+
+## package.sh 介绍
+
+[scripts/package.sh](../../scripts/package.sh) 主要是将 SmartAgent 安装目录下所有内容(脚本无自动去除无用目录或内容功能)压缩,
+通过`base64`编码后写入 [scripts/install_temp.sh](../../scripts/install_temp.sh) 中,
+最终生成安装文件 `cwserveragent-installer-version.sh` (version为SmartSgent版本)。
+
+### 安装帮助
+
+执行命令 `sh scripts/package.sh -h` 即可查看安装帮助详情。详情如下:
+
+```
+Usage: package.sh [-h] [-v] [DEBUG=true|false] [AGENT_VERSION=8.6]
+
+
+-h, --help     Display this help and exit.
+-v, --version  Print version and exit.
+
+RELEASE        Default true; Whether to delete the publish parameter in the script.
+VERSION        Configure the Cloudwise SmartAgent version.
+DEBUG          Default false; 1、Debug mode executed script package.sh;
+               2、Remove the debug log from the scripts (Cloudwise-SmartAgent-Linux-1.2.0.sh、smartagent、uninstall.sh).
+
+```
+
+### smartagent目录结构
+
+打包前目录结构(请保持目录及内容最简)
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+打包后目录结构
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── installer.version
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+安装后目录结构
+
+```
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   ├── cwserveragent.conf
+│   ├── installation.conf
+│   └── server-agent.ini
+├── installer.version
+├── logs
+│   └── serveragent
+│       ├── CW-ServerAgent.error.log
+│       ├── CW-ServerAgent.fatal.log
+│       └── CW-ServerAgent.panic.log
+├── runtime
+│   └── .pid
+├── scripts
+│   ├── cw-serveragent
+│   └── uninstall.sh
+└── uninstall.sh
+```

二进制
dist/aarch64/package_dir/agents/NativeAgent/lib/apmAgent.jar


二进制
dist/aarch64/package_dir/agents/NativeAgent/lib/apmCore.jar


二进制
dist/aarch64/package_dir/agents/NativeAgent/lib/apmSpy.jar


二进制
dist/aarch64/package_dir/agents/NativeAgent/lib/libnativeAgent.so


二进制
dist/aarch64/package_dir/agents/NativeAgent/plugins/cloudwise-javacode-plugin-ssl-socket.jar


+ 244 - 0
dist/aarch64/package_dir/bin/agentctl

@@ -0,0 +1,244 @@
+#!/bin/bash
+
+#== 脚本执行中遇到不存在的变量就报错
+set -o nounset
+#== 脚本执行中报错即刻退出
+set -o errexit
+#== 脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/current
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+readonly AGENT_LOG_DIR="${INSTALL_DIR}/logs"
+
+#readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#readonly MAIN_CONF_FILE="${AGENT_CONF_DIR}/${AGENT_MAIN_CONF}"
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+readonly AGENT_PROC="euspace"
+readonly AGENT_CONFIG_PATH=""
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+getDaemonPid(){
+    local agent="${1}"
+    #== 约定pid文件
+    local pidFilePath="${AGENT_PID_FILE}"
+    #== 二进制文件绝对路径
+    # shellcheck disable=SC2155
+    local procPath="$(readlink -m "${AGENT_BIN_DIR}/${agent}" 2>&1)"
+    local cmdline
+    local pid
+    #== 查询pid文件
+    if [ -s "${pidFilePath}" ] ; then
+      pid="$(cat "${pidFilePath}" 2>&1)"
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if [ -d "/proc/${pid}" ] && ( [[ ${cmdline} =~ ${procPath} ]] || [[ ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]] ); then
+#        toLogInfo "[${agent}] Find pid by ${pidFilePath} [content:${pid}][cmdline:${cmdline}]"
+          printf '%s' ${pid}
+          return 0
+      else
+        #== 进程绝对路径查询
+#        toLogWarning "[${agent}] Pidfile exist [content:${pid}]. But process not find or not myself. [cmdline:${cmdline}]"
+        if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#          toLogWarning "[${agent}] Not find process grep '${procPath}'"
+          return 1
+        fi
+        cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+        if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#            toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+            return 1
+        fi
+#        toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]"
+      fi
+    else
+      #== 进程绝对路径查询
+#      toLogWarning "[${agent}] Pidfile [${pidFilePath}] not find. Use abs path queries."
+      if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#        toLogWarning "[${agent}] Not find process by grep '${procPath}'"
+        return 1
+      fi
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#          toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+          return 1
+      fi
+#      toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]."
+    fi
+
+    printf '%s' ${pid}
+    return 0
+}
+
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+#  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E "${includeRegex}" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(ps -e -o "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+#    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+getAgentPid() {
+  local DAEMONPID="$(getDaemonPid "${AGENT_PROC}")"
+  if [ -z ${DAEMONPID} ]; then
+      return 1
+  fi
+  printf '%s' "${DAEMONPID}"
+
+}
+
+startAgent() {
+  local pid
+  if [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ]; then
+    export DISABLE_E2E_TRACING=false
+    export DISABLE_STACK_TRACING=true
+    export RUN_IN_OMNIAGENT=true
+#    export LOG_LEVEL=debug
+#    export SEND_NET_DATA=true
+#    export ENABLE_ES=true
+    export SEND=1
+    local params="--listen=0.0.0.0:8123"
+#    if [ -f "/etc/chaosd/pki/ca.crt" ] && [ -f "/etc/chaosd/pki/chaosd.crt" ] && [ -f "/etc/chaosd/pki/chaosd.key" ]; then
+#        params="server  --https-port 31768 --CA=/etc/chaosd/pki/ca.crt --cert=/etc/chaosd/pki/chaosd.crt --key=/etc/chaosd/pki/chaosd.key"
+#    fi
+    pid=$(nohup ${AGENT_BIN_DIR}/${AGENT_PROC} ${params}>>"${AGENT_LOG_DIR}/service.log" 2>&1 & echo $!)
+    echo ${pid} > "${AGENT_PID_FILE}"
+  else
+    echo "${AGENT_BIN_DIR}/${AGENT_PROC} not exist."
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+}
+
+agentStart() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} is running. Agent pid: ${agentPid}."
+    return
+  fi
+
+  startAgent
+	local exitCode=$?
+	if [ "${exitCode}" -ne 0 ]; then
+		echo "Failed to start ${BRAND_AGENT_PRODUCT_NAME} service."
+		exit "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} service started. Agent pid: ${agentPid}."
+  else
+    echo "${BRAND_AGENT_PRODUCT_NAME} not start!"
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+agentStop() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+#    ${AGENT_BIN_DIR}/stop.sh ${agentPid}
+    kill ${agentPid}
+    echo "${BRAND_AGENT_PRODUCT_NAME} service stopped success."
+    cat /dev/null > "${AGENT_PID_FILE}"
+  else
+      echo "${BRAND_AGENT_PRODUCT_NAME} is stopped. service stopped success."
+  fi
+}
+
+agentRestart(){
+  agentStop
+  sleep 1
+  agentStart
+}
+
+agentStatus() {
+  local agentPid
+	local statusOutput
+  if agentPid="$(getAgentPid)"; then
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is running. ${BRAND_AGENT_PRODUCT_NAME} pid: ${agentPid}"
+  else
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is not running. ${BRAND_AGENT_PRODUCT_NAME} process not found or stop."
+  fi
+
+  echo "${statusOutput}"
+}
+
+info() {
+  local agentPid="$(getAgentPid)"
+  local version=$(cat "${INSTALL_DIR}/installer.version" | tr -d '\n')
+  printf '{"pid":%d,"version":"%s","agent_id":"%s","config":"%s","pipe": true}' "${agentPid}" "${version}" "${BRAND_PRODUCT_NAME_LOWER}" "${AGENT_CONFIG_PATH}"
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+
+main() {
+  case "$1" in
+  start)
+    agentStart
+    ;;
+  stop)
+    agentStop
+    ;;
+  restart)
+    agentRestart
+    ;;
+  status)
+    agentStatus
+    ;;
+  info)
+    info
+    ;;
+  *)
+    toConsoleInfo "usage: $0 {start|stop|restart|status}"
+    ;;
+  esac
+
+  exit "${EXIT_CODE_OK}"
+}
+
+main "$@"

+ 0 - 0
dist/aarch64/package_dir/conf/.gitkeep


二进制
dist/aarch64/package_dir/libs/amd64/jvm/cwlibnet.so


二进制
dist/aarch64/package_dir/libs/arm64/jvm/cwlibnet.so


+ 0 - 0
dist/aarch64/package_dir/logs/.gitkeep


+ 0 - 0
dist/aarch64/package_dir/runtime/.gitkeep


二进制
dist/aarch64/package_dir/scripts/cwjattach


+ 2954 - 0
dist/aarch64/scripts/install_temp.sh

@@ -0,0 +1,2954 @@
+#!/bin/bash
+#== AIX(Advanced Interactive eXecutive)是IBM基于AT&T Unix System V开发的一套类UNIX操作系统,运行在IBM专有的Power系列芯片设计的小型机硬件系统之上
+#== 以【#== 】开头的注释会在打包时删除
+#DEBUG== 以【#DEBUG== 】开头的注释会在调试打包时保留,正式打包时删除
+
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE="cloudwise"
+
+readonly AIX_DF_SPECIFIC_FLAG=
+readonly CONF_LD_PRELOAD=
+
+PARAM_DISABLE_SYSTEM_LOGS_ACCESS=
+
+#== 安装包版本项
+readonly AGENT_BUILD_DATE=25.09.2020
+readonly AGENT_INSTALLER_VERSION=1.2.0
+readonly AGENT_BUILD_DATE_INFO=""
+readonly AGENT_BUILD_TAG=""
+readonly AGENT_BUILD_UPLOADER=""
+#== 安装包版本项
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== CW-ServerAgent
+readonly AGENT_PROC="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/oneagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/oneagent/agents/chaosd
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/oneagent/agents/chaosd
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+#== /opt/cloudwise/cwserveragent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/runtime
+readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/lib
+#== readonly AGENT_LIB_DIR="${INSTALL_DIR}/lib"
+
+
+readonly AGENTS_BASE_DIR="${INSTALL_DIR}/agents"
+
+readonly META_BASE_DIR="${INSTALL_DIR}/meta"
+
+readonly LOG_DIR_NAME="logs"
+#== /opt/cloudwise/cwserveragent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/cwserveragent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+
+#== server-agent.ini
+#== hostId文件
+readonly INSTALLER_CONF_FILE_NAME="installation.conf"
+#== 部署在容器内的状态信息
+readonly INSTALLER_CONF_FILE="${AGENT_CONF_DIR}/${INSTALLER_CONF_FILE_NAME}"
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+
+readonly EXTERNAL_TAR_SIZE=233820160
+readonly ARTIFACTS_SIZE=953271934
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#== 【0】【3】【4】=创建不存在的目录,并设置权限
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  toConsoleInfo "#DEBUG== createDirIfNotExistAndSetRights --- ${rights} --- ${dir}"
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarn "Cannot create ${dir} directory."
+      toLogWarn "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarn "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarn "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+createCurrentVersionSymlink() {
+	toLogInfo "Creating symlink to current version..."
+
+	local currentVersionLink="${AGENT_BASE_DIR}/current"
+	local tempLink="${currentVersionLink}.tmp"
+
+	if ! commandErrorWrapper ln -s "${AGENT_INSTALLER_VERSION}" "${tempLink}"; then
+		toLogError "Failed to create current version link"
+		return
+	fi
+
+	if ! commandErrorWrapper arch_moveReplaceTarget "${tempLink}" "${currentVersionLink}"; then
+		toLogError "Failed to set up current version link"
+		commandErrorWrapper rm -f "${tempLink}"
+		return
+	fi
+
+	toLogInfo "Current version link created: ${currentVersionLink} -> ${AGENT_INSTALLER_VERSION}"
+}
+
+#== 【0】=创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  #== agents根目录
+  createDirIfNotExistAndSetRights "${AGENT_BASE_DIR}" 1775
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+#  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+#**********************************************************
+# Platform characteristics detection
+#**********************************************************
+
+#== 【5】=获取系统发型版本
+getOsReleasePath() {
+	local osReleasePath="/etc/os-release"
+	if [ ! -f "${osReleasePath}" ]; then
+		osReleasePath="/usr/lib/os-release"
+	fi
+
+	printf '%s' "${osReleasePath}"
+}
+
+parseOsReleaseFile() {
+	local osReleasePath="$(getOsReleasePath)"
+
+	#shellcheck disable=SC1090
+	. "${osReleasePath}"
+	local distrib="${NAME-}"
+	if [ -z "${distrib}" ]; then
+		distrib="${ID-}"
+	fi
+
+	local version="${VERSION_ID-}"
+	if printf '%s' "${distrib}" | grep -iq "debian"; then
+		version="$(cat /etc/debian_version)"
+	elif printf '%s' "${distrib}" | grep -iq "fedora" && [ "${VARIANT_ID-}" = "coreos" ]; then
+		distrib="${distrib} CoreOS"
+	fi
+
+	printf '%s %s' "${distrib}" "${version}"
+}
+
+#== 【5】=获取系统发型版本
+detectLinuxDistribution() {
+	if [ -f /etc/oracle-release ]; then
+		cat /etc/oracle-release
+	elif [ -f /etc/fedora-release ]; then
+		if [ -f "$(getOsReleasePath)" ]; then
+			(
+				parseOsReleaseFile
+			)
+		else
+			cat /etc/fedora-release
+		fi
+	elif [ -f /etc/redhat-release ]; then
+		cat /etc/redhat-release
+	elif [ -f "$(getOsReleasePath)" ]; then
+		(
+			parseOsReleaseFile
+		)
+	elif [ -f /etc/SuSE-release ]; then
+		head -1 /etc/SuSE-release
+	elif [ -f /etc/lsb-release ]; then
+		(
+			. /etc/lsb-release
+			printf "%s %s" "${DISTRIB_ID-}" "${DISTRIB_RELEASE-}"
+		)
+	elif ls /etc/*release* >/dev/null 2>&1; then
+		# Generic fallback
+		cat /etc/*release*
+	else
+		printf "AIX %s" "$(oslevel -s 2>&1)"
+	fi
+}
+
+#== 【3】=检查系统init (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarn "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+#== 【3】=设置系统初始化脚本目录
+setLocationOfScripts() {
+  toLogInfo "Determining location of scripts..."
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSTEMD}"x ] || [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== /opt/cloudwise/cwserveragent/scripts
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of scripts ${INIT_DIR}"
+  return 0
+}
+
+#== 【0】=检查系统结构(X86_64\IA64\X86)
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    #== arch指令主要用于显示当前主机的硬件结构类型,查询结果与uname一致,我们可以看到它输出的结果有:i386、i486、mips、alpha等
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+#**********************************************************
+# Misc functions
+#**********************************************************
+
+#== 【1】【4】【5】【6】=获取 agent (64\32)位数lib目录(""或lib64)
+getBinariesFolderByBitness() {
+  local bitness="${1}"
+  if [ "${bitness}" -eq 32 ]; then
+    bitness=""
+  fi
+  printf 'lib%s' "${bitness}"
+}
+
+#== 【1】【2】【5】【6】=获取 agent tools/lib64/smartagentctl 路径
+#== getAgentCtlBinPath
+
+#== 【1】【4】【5】【6】=获取 agent 64位数lib路径(lib64/installaction)
+#== getAgentInstallActionPath
+
+#== 【5】【6】=获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+getOsConfigBinPath() {
+  printf "%s" "${INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+#== 【5】=设置agent进程可用
+setProcessAgentEnabled() {
+  local enabled="${1}"
+  toLogInfo "Setting process agent enabled: ${enabled}..."
+  local changeStatus=
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --set-process-agent-enabled
+  changeStatus=$("$(getAgentInstallActionPath)" --set-process-agent-enabled "${enabled}" 2>&1)
+  toLogAdaptive $? "Process agent enable(${enabled}) status: ${changeStatus}"
+}
+
+#== 【1】【4】【5】【6】=获取命令执行错误信息,并写日志
+commandErrorWrapper() {
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarn "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  rm -f "${errorFile}"
+
+  return ${returnCode}
+}
+
+#== 【3】=是否独立的namespace
+isNamespaceIsolated() {
+  local pid="${1}"
+  local namespace="${2}"
+  local initNamespaceId
+  local processNamespaceId
+  initNamespaceId="$(readlink "/proc/1/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+  processNamespaceId="$(readlink "/proc/${pid}/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+
+  if [ ! "${initNamespaceId}" ] || [ ! "${processNamespaceId}" ]; then
+    toLogInfo "Link to /proc/*/ns/${namespace} does not exist"
+    printf 'error'
+    return
+  fi
+
+  if [ "${initNamespaceId}"x != "${processNamespaceId}"x ]; then
+    printf 'true'
+  else
+    printf 'false'
+  fi
+}
+
+#== 【0】=检查是否root
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)"x != "0"x ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+#== 【3】=删除存在的路径
+removeIfExists() {
+  local pathToRemove="${1}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(rm -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarn "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+
+#== 【5】【6】=执行 systemctl 命令
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    #== 执行使用 os config bin
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  #== 执行 systemctl 命令
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+#== 【6】=运行初始化命令(通过 service 方式 或 直接运行可执行命令)
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  output="$("${command}" "${parameters}" 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+#== 【0】=信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback}
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 【0】=配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#== 【0】删除权限
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(LICENSE=\)[[:alnum:]]\{16\}#\1***#'
+}
+
+#== 【3】【5】=检查目录写权限
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #== shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    rm -f "${errorFile}"
+    return 1
+  fi
+
+  rm -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+#== 【0】=设置PATH
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+#== 【5】【6】=是否非root权限
+isNonRootModeEnabled() {
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-drop-root-privileges
+  local output
+  output="$(getValueFromConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "${LEGACY_AGENT_CONF_FILE}" "${PARAM_NON_ROOT_MODE}")"
+  printf '%s' "${output}" | grep -qE "(true|no_ambient)"
+}
+
+#== 【5】=自动启动工具
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  local command="${prefix} ${file} ${suffix}"
+  toLogInfo "Executing ${command}"
+  output="$(${command} 2>&1)"
+  local status=$?
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+#== 【0】【5】=查找软链接真实地址(通过ls命令)
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput
+  local parsedLinkTarget
+  lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarn "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+#== 【0】【5】=查找软链接真实地址(通过readlink 或 ls 命令)
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      path="${PATH}:/opt/freeware/bin"
+    fi
+
+    #== 通过 readlink 命令查找地址
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+#== 【0】【3】【4】【5】【6】=检查命令是否有效/是否存在
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+#== 【0】【1】【3】【5】=读取配置文件配置参数信息
+getValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local defaultValue="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+#== 【1】【5】=删除配置文件中配置
+removeValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local output
+  if ! output="$(cp -p "${configFile}" "${configFile}.tmp" 2>&1)"; then
+    toLogWarn "Unable to initialize ${configFile}.tmp file using source file, privileges and ownership will not be preserved: ${output}"
+  fi
+
+  if sed "/^${key}/d" "${configFile}" >"${configFile}.tmp"; then
+    mv -f "${configFile}.tmp" "${configFile}"
+  else
+    toLogWarn "Failed to remove ${key} from ${configFile}"
+    rm -f "${configFile}.tmp"
+  fi
+}
+
+#== 写配置到配置文件信息
+writeParamToConfigFile() {
+  local key="${1}"
+  local newValue="${2}"
+  local configFile="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== writeParamToConfigFile --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${configFile}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${configFile}" 2>/dev/null
+  else
+    echo "${key}=${newValue}" >>"${configFile}" 2>/dev/null
+  fi
+}
+
+writeContentToConfigFile() {
+  local newValue="${1}"
+  local configFile="${2}"
+  toConsoleInfo "#DEBUG== writeContentToConfigFile --> edit ${newValue}, configFile: ${configFile}"
+
+  echo "${newValue}" > "${configFile}" 2>/dev/null
+}
+
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== editScriptFileParam --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 【0】=是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+isAnotherInstallationRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+  local pidFromLockFile
+  local nameFromLockFile
+  pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarn "Installation lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarn "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #== shellcheck disable=SC2009
+  #== 获取正在执行安装的进程信息
+  local foundProcesses
+  foundProcesses="$(pgrep -f "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIR}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installation process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 1
+}
+
+#== 【0】=创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+createInstallationLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+#== 【0】【6】删除安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+removeInstallationLockFile() {
+  toLogInfo "Removing installation lock file."
+  rm -f "${INSTALLER_LOCK_FILE}"
+}
+
+#== 【0】基础启动日志信息
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+#== 解压文件编码
+readonly UNPACK_BINARY=base64
+#== 解压文件编码参数
+readonly UNPACK_BINARY_ARGS="-di"
+
+readonly ARCH_ARCH="X86"
+
+#== 【0】=检查系统指令集(X86_64\IA64\X86)
+arch_checkArchitectureCompatibility() {
+  #== 【0】=检查系统结构(X86_64)
+  local arch
+  arch="$(detectArchitecture)"
+  if [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "IA64"x ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "AARCH64"x ]
+}
+
+#== 【5】=获取 lib 目录
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #== shellcheck disable=SC2016
+    libMacro="/${LIB}"
+  fi
+  printf "%s" "${libMacro}"
+}
+#== 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+#== 【3】【5】=执行命令超时配置
+
+#== 【0】【3】=获取文件权限信息
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+#== 【4】=替换目录
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+
+#== xz 压缩包文件名 Cloudwise-SmartAgent
+readonly INTERNAL_TAR_FILE_NAME=${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}.tar.xz
+
+#== -安装日志: installation-{timestamp}.log,如,installation-20220601110912.log
+readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation-$(date -u +"%Y%m%d%H%M%S").log"
+#readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation_$$.log"
+
+
+
+#== 【临时目录】安装临时目录
+readonly TMP_DIR=${INSTALL_DIR}_install_$$
+#== 【临时目录】解压缓存目录
+readonly UNPACK_CACHE=${BASE_INSTALL_DIR}/unpack_cache
+#== 【临时目录】从sh文件读取压缩文件 tarfile_$$.base64
+readonly EXTERNAL_TAR_FILE=${BASE_INSTALL_DIR}/tarfile_$$.base64
+#== 【临时目录】xz 压缩包文件
+readonly INTERNAL_TAR_FILE=${INSTALL_DIR}/${INTERNAL_TAR_FILE_NAME}
+
+#== 【0】=
+readonly INSTALLER_FILE=${0}
+#== 旧配置文件
+readonly LEGACY_AGENT_CONF_FILE="${AGENT_CONF_DIR}/${BRAND_PRODUCT_NAME_LOWER}.conf"
+
+#== 【0】=
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+
+#== 【0】=
+readonly HELP_URL=""
+
+readonly CONF_FIELD_NM_DATA_SERVER="DataServer"
+readonly CONF_FIELD_NM_CONFIG_SERVER="ConfigServer"
+readonly CONF_FIELD_NM_LICENSE="License"
+readonly CONF_FIELD_NM_JSON_CONF="JSON_CONF"
+readonly CONF_FIELD_NM_USER="User"
+readonly CONF_FIELD_NM_GROUP="Group"
+readonly CONF_FIELD_NM_DATA_STORAGE="DataStorage"
+readonly CONF_FIELD_NM_NON_ROOT_MODE="NonRootMode"
+
+
+#== 配置默认用户权限
+readonly BASE_OMNI_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}
+readonly BASE_OMNI_INSTALL_CONF_DIR=${BASE_OMNI_INSTALL_DIR}/conf/installation.conf
+BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${BASE_OMNI_INSTALL_CONF_DIR}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE}")"
+
+#== Those are read from params section appended to installer by the server
+#== 【0】=【参数】数据服务地址
+PARAM_DATA_SERVER=
+#== 【0】=【参数】配置服务地址
+PARAM_CONFIG_SERVER=
+#== 【0】=【参数】license
+PARAM_LICENSE=
+
+PARAM_JSON_CONF=
+
+#== 【0】=【参数】安装路径
+PARAM_INSTALL_DIR=${INSTALL_DIR}
+#== 【0】=【参数】用户
+PARAM_USER=
+#== 【0】=【参数】用户组
+PARAM_GROUP=
+#== 【0】=【参数】非root模式
+PARAM_NON_ROOT_MODE=true
+
+PARAM_USER_LOGIN=false
+
+#== 【0】=【参数】不允许root回退
+PARAM_DISABLE_ROOT_FALLBACK=false
+#== 【0】=【参数】数据存储目录
+PARAM_DATA_STORAGE=
+#== 【0】=【参数】通过容器部署
+#== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER=false
+#== 【0】=【参数】跳过SELinux策略安装
+#== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER=false
+#== 【0】=【参数】是否使用解压缓存
+PARAM_INTERNAL_USE_UNPACK_CACHE=false
+#== 【0】=【参数】是否不使用 dump
+#== PARAM_INTERNAL_DISABLE_DUMPPROC=
+#== 【0】=【参数】是否跳过非root检查
+PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=false
+#== 【0】【1】=【参数】额外的配置
+#== PARAM_INTERNAL_PASS_THROUGH_SETTERS=
+#== 【0】=检查是否降级安装
+SKIP_DOWNGRADE_CHECK=false
+
+SKIP_PRIVILEGES_CHECK=false
+
+#== 自定义字符串参数
+PARAM_TEST=
+#== 自定义字BOOL参数
+PARAM_BOOL=
+
+#== 【0】=常规日志
+initializeLog() {
+  toConsoleInfo "Installation started, version ${AGENT_INSTALLER_VERSION}, build date: ${AGENT_BUILD_DATE}, PID $$."
+  toLogInfo "Started from: ${INSTALLER_FILE}"
+
+  if [ -f /proc/version ]; then
+    toLogInfo "System version: $(cat /proc/version)"
+  else
+    toLogInfo "System version: $(uname -a)"
+  fi
+
+  toLogInfo "Path: ${PATH}"
+  toLogInfo "INSTALL_DIR: ${INSTALL_DIR}"
+  toLogInfo "Resolved installation path: $(readLink -e "${INSTALL_DIR}" 2>/dev/null)"
+  logBasicStartupInformation "${@}"
+}
+
+#**********************************************************
+# Signing related stuff
+#**********************************************************
+#== 【0】【4】=通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local linesCount
+  local offset
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    linesCount="$(wc -l "${INSTALLER_FILE}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    offset="$(tail -n"${linesToReadFromEnd}" "${INSTALLER_FILE}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${INSTALLER_FILE}"
+  fi
+}
+
+#== 【0】=从指定行范围读取配置参数
+readParam() {
+  local paramName="${1}"
+  local paramsSectionBeggining="${2}"
+  local paramsSectionEnd="${3}"
+
+  sed -n "${paramsSectionBeggining},${paramsSectionEnd} s/^${paramName}=//p" "${INSTALLER_FILE}"
+}
+
+#== 【0】=读取以【----PARAMETERS】开始到 【----PARAMETERS--】之间的参数
+readParamsSection() {
+  local sectionName="----PARAMETERS"
+  local begin
+  local end
+  begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+  end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+  #== 从指定行范围读取配置参数
+  local value
+  if value="$(readParam PARAM_DATA_SERVER "${begin}" "${end}")"; then
+    PARAM_DATA_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_CONFIG_SERVER "${begin}" "${end}")"; then
+    PARAM_CONFIG_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_LICENSE "${begin}" "${end}")"; then
+    PARAM_LICENSE="${value}"
+  fi
+
+  if value="$(readParam PARAM_JSON_CONF "${begin}" "${end}")"; then
+    PARAM_JSON_CONF="${value}"
+  fi
+
+  if value="$(readParam PARAM_USER "${begin}" "${end}")"; then
+    PARAM_USER="${value}"
+  fi
+  if value="$(readParam PARAM_GROUP "${begin}" "${end}")"; then
+    PARAM_GROUP="${value}"
+  fi
+  if value="$(readParam PARAM_NON_ROOT_MODE "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_NON_ROOT_MODE="${value}"
+    fi
+  fi
+
+  if value="$(readParam PARAM_USER_LOGIN "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_USER_LOGIN="${value}"
+    fi
+  fi
+  toConsoleInfo "#DEBUG== install sh params, DATA_SERVER: ${PARAM_DATA_SERVER}, PARAM_CONFIG_SERVER: ${PARAM_CONFIG_SERVER}, PARAM_LICENSE:${PARAM_LICENSE}"
+  toConsoleInfo "#DEBUG== install sh params, PARAM_USER: ${PARAM_USER}, PARAM_GROUP: ${PARAM_GROUP}, PARAM_USER_LOGIN: ${PARAM_USER_LOGIN}, PARAM_NON_ROOT_MODE:${PARAM_NON_ROOT_MODE}"
+}
+
+#== 【0】【6】=清空安装临时文件
+#== ${INSTALL_DIR}_install_$$
+#== EXTERNAL_TAR_FILE=${INSTALL_DIR}/tarfile_$$.base64
+#== ${INSTALL_DIR}/Dynatrace-OneAgent.tar.xz
+#== ${INSTALL_DIR}/xzdec
+#== /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+cleanInstallationTemporaryFiles() {
+  toLogInfo "Cleaning installation temporary files"
+
+  rm -f "${EXTERNAL_TAR_FILE}" "${INTERNAL_TAR_FILE}" "${INSTALL_DIR}/xzdec"
+  rm -Rf "${TMP_DIR}"
+
+  local keepInstallationLockFile="${1}"
+  if [ -z "${keepInstallationLockFile}" ]; then
+    removeInstallationLockFile
+  fi
+}
+
+#== 完成安装后清理临时目录
+finishInstallation() {
+  if [ $# -eq 2 ]; then
+    cleanInstallationTemporaryFiles "${2}"
+  else
+    cleanInstallationTemporaryFiles ""
+  fi
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  changeWorkingDir "${CURR_PATH}"
+  if [ "${CONF_LD_PRELOAD}"x = "true"x ]; then
+    exec /bin/bash && exit 0
+  fi
+  exit "${1}"
+}
+
+#**********************************************************
+# Create folders, copy files, set rights
+#**********************************************************
+
+#== 【3】=创建临时目录
+prepareTempFolder() {
+  #== 删除存在的路径
+  removeIfExists "${TMP_DIR}"
+
+  toLogInfo "Creating temporary folder $TMP_DIR"
+  createDirIfNotExistAndSetRights "${TMP_DIR}" 755
+}
+
+#== 【4】=设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -e "${file}" ]; then
+    chmod "${perms}" "${file}"
+  fi
+}
+
+setRightsForDir() {
+  local dir="${1}"
+  local perms="${2}"
+
+  if [ -d "${dir}" ]; then
+    chmod -R "${perms}" "${dir}"
+  fi
+}
+
+#== 【4】=安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+#== 【4】=移动目录到指定位置
+moveFolderToDestination() {
+  local source="${1}"
+  local destination="${2}"
+  local fullDestination
+  fullDestination="${destination}/$(basename "${source}")"
+  local output
+
+  toLogInfo "Moving ${source} to ${destination}"
+  if [ ! -e "${fullDestination}" ]; then
+    if output="$(mv -f "${source}" "${destination}" 2>&1)"; then
+      toLogInfo "Moving Successfully."
+      return
+    fi
+    toLogWarn "Failed to move ${source} to ${destination}: ${output}, attempting to copy"
+  else
+    toLogInfo "${fullDestination} already exists, attempting to copy"
+  fi
+
+  if ! output="$(cp -Rfp "${source}" "${destination}" 2>&1)"; then
+    toLogError "Failed to copy ${source} to ${destination}: ${output}"
+  fi
+}
+
+#== 【4】=将 bin 下版本内容移动到安装目录
+installVersionedContent() {
+  toLogInfo "Installing versioned content..."
+  createDirIfNotExistAndSetRights "${AGENT_BIN_DIR}" 755
+
+  local sourceDir="${TMP_DIR}/bin"
+  if [ ! -d "${AGENT_BIN_DIR}" ]; then
+    moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+    return
+  fi
+
+  toLogInfo "Directory ${AGENT_BIN_DIR} already exist, repairing the directory"
+  rm -rf "${AGENT_BIN_DIR}"
+  moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+}
+
+#== 【4】=创建当前版本软连接
+
+#== 【4】=删除存在的目录
+listAndRemoveDirectoryIfExists() {
+  local directory="${1}"
+  if [ -d "${directory}" ]; then
+    toLogInfo "${directory} exists, removing it."
+    toLogInfo "Contents: $(ls -lR "${directory}")"
+    rm -rf "${directory}"
+  fi
+}
+
+#== 【4】=临时目录中conf 移动到 agent 安装目录中
+setupConfFolder() {
+  toLogInfo "Setup conf folder..."
+  #== 移动目录到指定位置
+  moveFolderToDestination "${TMP_DIR}/conf" "${INSTALL_DIR}"
+
+  chmod 755 "${AGENT_CONF_DIR}"
+
+  toLogInfo "Setup conf done."
+}
+
+#== 【4】=给距离当前目录至少 ${mindepth} 个子目录的所有文件设置权限
+chmodFilesWithMindepth() {
+  local dir="${1}"
+  local mindepth="${2}"
+  local mask="${3}"
+
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== 安装类型(f:文件,d:目录)递归设置权限
+    chmod4FilesRecursively "${1}" f "${3}"
+  else
+    #== 查找深度距离当前目录至少 ${mindepth} 个子目录的所有文件
+    find "${dir}" -mindepth "${mindepth}" -print0 | xargs -r -0 chmod "${mask}"
+  fi
+}
+
+#== 【4】=配置其他文件(cwserveragent.service、installer.version)
+setupMiscFiles() {
+  toLogInfo "Setup misc files..."
+  #== 创建 cwserveragent.service
+  createSystemdUnitFile
+  #== echo "${AGENT_INSTALLER_VERSION}" >"${INSTALL_DIR}/installer.version"
+  mv -f "${TMP_DIR}/installer.version" "${INSTALL_DIR}/"
+  toLogInfo "Setup misc done."
+}
+
+#== 【4】=创建 cwserveragent.service
+createSystemdUnitFile() {
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    return
+  fi
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  cat <<EOF >${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}
+[Unit]
+Description=${BRAND_AGENT_PRODUCT_NAME}
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+User=root
+ExecStart=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} start
+ExecStop=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} stop
+Type=forking
+#Restart=always
+KillMode=process
+TimeoutSec=240
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+  setRightsForFiles "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 755
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    cp -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" ${INIT_DIR}
+    toLogInfo "Copy scripts ${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT} to ${INIT_DIR} done."
+  fi
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR} done."
+}
+
+setupAll() {
+#  moveFolderToDestination "${TMP_DIR}/package_dir/*" "${INSTALL_DIR}"
+#ls  ${TMP_DIR}/package_dir/*
+
+  #== 移动所有文件到目录
+  cp -Rfp ${TMP_DIR}/package_dir/* "${INSTALL_DIR}"
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  echo ${AGENT_INSTALLER_VERSION} > ${installVersionFile}
+#  mv ${INSTALL_DIR}/package_dir/* ${INSTALL_DIR}
+#     echo "${TMP_DIR}/*" "${INSTALL_DIR}/"
+}
+
+#== 【4】=配置 lib、conf、bin、cwserveragent.service、installer.version、plugins
+setupOptDir() {
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  #== 临时目录中conf 移动到 agent 安装目录中
+  setupConfFolder
+  #== 将 bin 下版本内容移动到安装目录
+  installVersionedContent
+  #== 配置其他文件(cwserveragent.service、installer.version)
+  setupMiscFiles
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScriptsToDirectory() {
+  local scriptLocation="${1}"
+  #== scripts/cwserveragent
+  local scriptFile="${TMP_DIR}/scripts/${SERVICE_SCRIPT_FILE}"
+
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+
+  local output
+  if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+    toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+    return
+  fi
+
+  setRightsForFiles "${scriptLocation}/${SERVICE_SCRIPT_FILE}" 755
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+
+  #== scripts/uninstall.sh
+#  local scriptLocation="${1}"
+#    #== scripts/cwserveragent
+#    local scriptFile="${TMP_DIR}/scripts/uninstall.sh"
+#
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+#
+#    local output
+#    if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+#      toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+#      return
+#    fi
+#    setRightsForFiles "${scriptLocation}/uninstall.sh" 755
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScripts() {
+  toLogInfo "Copy scripts..."
+  #== 创建目录 /opt/cloudwise/cwserveragent/scripts
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  #== 创建目录 /opt/cloudwise/{product}/agents
+  createDirIfNotExistAndSetRights "${AGENTS_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/meta
+  createDirIfNotExistAndSetRights "${META_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/runtime
+  createDirIfNotExistAndSetRights "${AGENT_RUNTIME_DIR}" 775
+
+  #== scripts/uninstall.sh
+  local uninstallScript="${TMP_DIR}/scripts/uninstall.sh"
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} begin."
+  local output
+  if ! output="$(cp "${uninstallScript}" "${INSTALL_DIR}/uninstall.sh" 2>&1)"; then
+    toLogError "Failed to copy ${uninstallScript} to ${INSTALL_DIR}/uninstall.sh, output: ${output}"
+  else
+    setRightsForFiles "${INSTALL_DIR}/uninstall.sh" 755
+  fi
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} done."
+
+  #== 复制临时目录 scripts/ 到安装目录 /opt/cloudwise/cwserveragent/scripts
+  copyScriptsToDirectory "${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    copyScriptsToDirectory "${INIT_DIR}"
+  fi
+  toLogInfo "Copy scripts done."
+}
+
+#== 【4】=调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+createFirstClusterTimestampFile() {
+  toLogInfo "Creating firstClusterTimestamp file"
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+  "$(getAgentInstallActionPath)" "--create-cluster-timestamp-file" >>"${LOG_FILE}" 2>&1
+}
+
+#== 【4】=配置数据存储目录
+setupDataStorageDir() {
+  toLogInfo "Setup datastorage dir..."
+  local dataStorage
+  dataStorage="$(getValueFromConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}" "${DATA_STORAGE_DIR}")"
+  if [ "${dataStorage}"x = "${LOG_DIR}"x ]; then
+    toLogInfo "Detected legacy data storage setting, changing it to the new default location"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}" ]; then
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  toLogInfo "Setup datastorage dir done."
+}
+
+#**********************************************************
+# Processing command line parameters
+#**********************************************************
+#== 【0】=help
+displayHelp() {
+  printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] "
+  #== printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] [DATA_SERVER=https://server_address:server_port] [CONFIG_SERVER=configService] [LICENSE=license] [INSTALL_DIR=install_path]"
+  local beginStr="Usage: "
+  local pad="${#beginStr}"
+
+  printf "\n\n"
+
+  pad=25
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+printOtherParamMessage() {
+  local param="${1}"
+  toConsoleInfo "---> Parameter ${param}"
+}
+
+#== 【0】=大小写转化,并返回判断结果
+istrcmp() {
+  local s1
+  local s2
+  s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+#== 【0】=
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+#== 【1】【5】=
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 【1】【5】=
+invertBoolValue() {
+  local valueToInvert="${1}"
+  if isParamFalse "${valueToInvert}"; then
+    printf '%s' "true"
+  else
+    printf '%s' "false"
+  fi
+}
+
+#== shellcheck disable=SC2003
+#== 【0】=获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+  local partParam
+  partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+#  partParam=$(substr "${input}" 1 ${paramNameLength})
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value
+  value="$(expr substr "${input}" ${valueSeparator} 1000)"
+#  value=$(substr "${input}" ${valueSeparator} 1000)
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 【0】=获取bool参数
+readBoolParam() {
+  local value
+  value="$(getParamValue "${1}" "${2}")"
+
+  if value="$(getBoolParam "${value}")"; then
+    printf "%s" "${value}"
+    return 0
+  fi
+
+  return 1
+}
+
+#== 【0】=获取bool参数
+getBoolParam() {
+  local value="${1}"
+  if [ "${value}" ]; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 【0】=校验格式是否规范[1、大于4个字符且不能"cw."开头,2、不能超过100字符,3、只能包含字母数字字符,连字符,下划线和点。]
+validateParameter() {
+  local name="${1}"
+  local value="${2}"
+  if [ "$(printf '%s' "${value}" | cut -c -3)"x = "cw."x ]; then
+    toConsoleError "${name} must not begin with 'cw.'"
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${#value}" -gt 100 ]; then
+    toConsoleError "Maximum allowed length of ${name} is 100."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${value}" | grep -q "[^[:alnum:]._-]"; then
+    toConsoleError "${name} can only contain alphanumeric characters, hyphen, underscore and dot."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=验证用户和用户组是否匹配
+validateUserAndGroupParameters() {
+  local user="${1}"
+  local group="${2}"
+  local permittedNameRegex='^[[:alnum:]._][[:alnum:]._-]{2,31}$'
+
+  if [ ! "${group}" ]; then
+    group="${user}"
+  fi
+
+  if ! printf '%s' "${user}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "USER can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if ! printf '%s' "${group}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "GROUP can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${user}" "${group}"
+}
+
+#== 【0】【3】【5】=获取系统权限信息
+getSystemEntityInfo() {
+  local database="${1}"
+  local valueToCheck="${2}"
+
+  #== 校验命令getent是否存在
+  if ! isAvailable "getent"; then
+    toLogInfo "Command getent is not available"
+    return 2
+  fi
+
+  #== 查看系统权限
+  local output
+  output="$(getent "${database}" "${valueToCheck}" 2>&1)"
+  local returnCode=$?
+
+  if [ "${returnCode}" != 0 ]; then
+    if [ "${returnCode}" != 2 ]; then
+      toLogWarn "Failed to check ${valueToCheck} in ${database} database, message: ${output}, code: ${returnCode}"
+    fi
+    return 1
+  elif [ ! "${output}" ]; then
+    toLogWarn "Failed to get user information: getent returned no output"
+  fi
+
+  printf '%s' "${output}"
+  return 0
+}
+
+#== 【0】【3】=查看用户/用户组信息
+isEntityPassedById() {
+  local database="${1}"
+  local name="${2}"
+
+  local output
+  output="$(getSystemEntityInfo "${database}" "${name}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    if [ ${returnCode} -eq 2 ]; then
+      toLogInfo "Installer will not be able to verify whether entity was passed by name or by ID"
+    fi
+    return 1
+  fi
+
+  local nameFromDatabase
+  nameFromDatabase="$(printf '%s' "${output}" | cut -d: -f1)"
+
+  if [ "${nameFromDatabase}"x = "${name}"x ]; then
+    return 1
+  fi
+
+  toLogWarn "Name from config and from ${database} system database do not match"
+  toLogWarn "Config: ${name}, database: ${nameFromDatabase}"
+  return 0
+}
+
+#== 【0】【3】【5】=用户是否存在
+userExistsInSystem() {
+  local user="${1}"
+
+  getSystemEntityInfo "passwd" "${user}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Trying to determine user existence using 'id' command"
+  id "${user}" >/dev/null 2>&1
+}
+
+#== 【5】= 查看group
+groupExistsInSystem() {
+  local group="${1}"
+
+  getSystemEntityInfo "group" "${group}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Installer will not be able to determine group existence"
+  return 1
+}
+
+#== 【3】=用户是否存在
+validateUserExistence() {
+  local user="${1}"
+  if ! userExistsInSystem "${user}"; then
+    toConsoleError "User named '${user}' configured for ${BRAND_PRODUCT_NAME} does not exist. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=检查用户/用户组信息
+checkIfEntityWasNotPassedById() {
+  local database="${1}"
+  local valueToCheck="${2}"
+  local valueTypeToLog="user"
+
+  if [ "${database}"x = "group"x ]; then
+    valueTypeToLog="group"
+  fi
+
+  if isEntityPassedById "${database}" "${valueToCheck}"; then
+    toConsoleError "\"${valueToCheck}\" is not a ${valueTypeToLog} name but its ID. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=用户权限下用户组ID
+getUserPrimaryGroupIdForComparison() {
+  local user="${1}"
+
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getSystemEntityInfo "passwd" "${user}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${userPrimaryGroupId}" | cut -d: -f4
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning user primary group name instead of its id"
+  id -gn "${user}"
+}
+
+#== 【0】【3】=获取用户组信息
+getGroupIdForComparison() {
+  local group="${1}"
+
+  local groupId
+  groupId="$(getSystemEntityInfo "group" "${group}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${groupId}" | cut -d: -f3
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning group name instead of its id"
+  printf '%s' "${group}"
+}
+
+#== 【0】【3】=校验用户/用户组信息
+validateUserPrimaryGroup() {
+  local user="${1}"
+  local group="${2}"
+
+  if ! userExistsInSystem "${user}"; then
+    return
+  fi
+
+  checkIfEntityWasNotPassedById "passwd" "${user}"
+  checkIfEntityWasNotPassedById "group" "${group}"
+
+  #== 获取用户组ID
+  local groupId
+  groupId="$(getGroupIdForComparison "${group}")"
+  #== 获取用户下用户组ID
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getUserPrimaryGroupIdForComparison "${user}")"
+
+  toConsoleInfo "#DEBUG== group \"${group}\" id: \"${groupId}\", user \"${user}\" primaryGroupId: \"${userPrimaryGroupId}\""
+  if [ "${userPrimaryGroupId}"x != "${groupId}"x ]; then
+    toConsoleError "User named \"${user}\" does not have group named \"${group}\" as its primary group. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【3】=检查用户/用户组是否存在和匹配
+checkUserAndGroupFromConfig() {
+  local configUser
+  local configGroup
+  configUser="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  configGroup="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Checking validity of user account '${configUser}:${configGroup}'"
+
+  if [ "${PARAM_UPGRADE}"x = "yes"x ]; then
+    validateUserExistence "${configUser}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${configUser}" "${configGroup}"
+}
+
+#== 【0】=解析命令行中参数
+parseCommandLineParameters() {
+  local dataServerIsEmpty=true
+  local configServerIsEmpty=true
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue DATA_SERVER "${param}"); then
+      PARAM_DATA_SERVER="${value}"
+      dataServerIsEmpty=false
+      printParamMessage "DATA_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG_SERVER "${param}"); then
+      PARAM_CONFIG_SERVER="${value}"
+      configServerIsEmpty=false
+      printParamMessage "CONFIG_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue LICENSE "${param}"); then
+      PARAM_LICENSE="${value}"
+      printParamMessage "LICENSE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue JSON_CONF "${param}"); then
+      PARAM_JSON_CONF="${value}"
+      printParamMessage "JSON_CONF" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INSTALL_DIR "${param}"); then
+      PARAM_INSTALL_DIR="${value}"
+      printParamMessage "INSTALL_DIR" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue DATA_STORAGE "${param}"); then
+      PARAM_DATA_STORAGE="${value}"
+      printParamMessage "DATA_STORAGE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DISABLE_SYSTEM_LOGS_ACCESS "${param}"); then
+      PARAM_DISABLE_SYSTEM_LOGS_ACCESS="${value}"
+      printParamMessage "DISABLE_SYSTEM_LOGS_ACCESS" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INTERNAL_OVERRIDE_CHECKS "${param}"); then
+      if printf '%s' "${value}" | grep -wq "privileges"; then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=true
+      fi
+
+      if printf '%s' "${value}" | grep -wq "downgrade"; then
+        SKIP_DOWNGRADE_CHECK=true
+      fi
+      printParamMessage "INTERNAL_OVERRIDE_CHECKS" "${value}"
+
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam INTERNAL_USE_UNPACK_CACHE "${param}"); then
+      PARAM_INTERNAL_USE_UNPACK_CACHE="${value}"
+      printParamMessage "INTERNAL_USE_UNPACK_CACHE" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+      if value=$(getParamValue USER "${param}"); then
+        PARAM_USER="${value}"
+        printParamMessage "USER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(getParamValue GROUP "${param}"); then
+        PARAM_GROUP="${value}"
+        printParamMessage "GROUP" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE "${param}"); then
+        PARAM_NON_ROOT_MODE="${value}"
+        printParamMessage "NON_ROOT_MODE" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam USER_LOGIN "${param}"); then
+        PARAM_USER_LOGIN="${value}"
+        printParamMessage "USER_LOGIN" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义参数获取
+      if value=$(getParamValue TEST "${param}"); then
+        PARAM_TEST="${value}"
+        printParamMessage "TEST" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义bool参数
+      if value=$(readBoolParam BOOL "${param}"); then
+        PARAM_BOOL="${value}"
+        printParamMessage "BOOL" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DISABLE_ROOT_FALLBACK "${param}"); then
+        PARAM_DISABLE_ROOT_FALLBACK="${value}"
+        printParamMessage "DISABLE_ROOT_FALLBACK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK "${param}"); then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK="${value}"
+        printParamMessage "NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam INTERNAL_SKIP_SELINUX_POLICY_INSTALLER "${param}"); then
+        #== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER="${value}"
+        printParamMessage "INTERNAL_SKIP_SELINUX_POLICY_INSTALLER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DOCKER_ENABLED "${param}"); then
+        #== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER="${value}"
+        printParamMessage "DOCKER_ENABLED" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ] && [ "${ARCH_ARCH}"x != "S390"x ]; then
+      if value=$(readBoolParam INTERNAL_DISABLE_DUMPPROC "${param}"); then
+        #== PARAM_INTERNAL_DISABLE_DUMPPROC="${value}"
+        printParamMessage "INTERNAL_DISABLE_DUMPPROC" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+    
+    printOtherParamMessage ${param}
+    shift
+  done
+
+  if [ "${configServerIsEmpty}"x = "true"x ] && [ "${dataServerIsEmpty}"x != "true"x ]; then
+    PARAM_CONFIG_SERVER="${PARAM_DATA_SERVER}"
+  fi
+}
+
+#== 【0】=校验PARAM_INSTALL_DIR是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的】
+validateInstallPathParameter() {
+  if printf '%s' "${PARAM_INSTALL_DIR}" | grep -q "[[:space:]]"; then
+    toConsoleError "Installation path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_INSTALL_DIR}"x = "/"x ]; then
+    toConsoleError "Installation path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_INSTALL_DIR}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Installation path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=校验PARAM_DATA_STORAGE是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的,4、数据目录不能放在安装目录下】
+validateDataStorageParameter() {
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "[[:space:]]"; then
+    toConsoleError "Data storage path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}"x = "/"x ]; then
+    toConsoleError "Data storage path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_DATA_STORAGE}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Data storage path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "^${INSTALL_DIR}"; then
+    toConsoleError "Data storage path must not be located within ${INSTALL_DIR}."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+
+#**********************************************************
+# Config files
+#**********************************************************
+
+#== 【3】【5】=格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+formatSize() {
+  local sizeInKiB="${1}"
+  local formattedSize
+
+  for symbol in "KiB" "MiB" "GiB" "TiB"; do
+    if printf '%s' "${sizeInKiB}" | awk '$1 >= 1024 { exit 1; }'; then
+      formattedSize="${sizeInKiB} ${symbol}"
+      break
+    fi
+    sizeInKiB="$(printf '%s' "${sizeInKiB}" | awk '{ print $1 / 1024 }')"
+  done
+
+  printf '%s' "${formattedSize}"
+}
+
+#== 【3】【5】=
+cropSizeValue() {
+  local size="${1}"
+  local value
+  local unit
+  value="$(printf '%s' "${size}" | cut -d' ' -f1)"
+  unit="$(printf '%s' "${size}" | cut -d' ' -f2)"
+  printf '%.2f %s' "${value}" "${unit}"
+}
+
+#== 【3】【5】=检查目录可用空间(单位:k)
+checkFreeSpace() {
+  local path="${1}"
+  local requiredSpaceInKiB="${2}"
+  local dfOutput
+  local baseFilesystem
+  local freeSpace
+  local formattedRequiredSpace
+  local formattedFreeSpace
+
+  #== shellcheck disable=SC2086
+  dfOutput="$(df -P ${AIX_DF_SPECIFIC_FLAG} "${path}" | tail -n +2)"
+  #== 文件系统目录
+  baseFilesystem="$(printf "%s" "${dfOutput}" | awk '{ print $NF }')"
+  #== 剩余空间(单位:k)
+  freeSpace="$(printf "%s" "${dfOutput}" | awk '{ print $4 }')"
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedRequiredSpace="$(formatSize "${requiredSpaceInKiB}")"
+  toLogInfo "Filesystem with ${path} is mounted under ${baseFilesystem}. Space required: ${formattedRequiredSpace}."
+
+  if [ ! "${freeSpace}" ]; then
+    printf 'Cannot determine amount of free space on %s (filesystem mounted under %s)' "${path}" "${baseFilesystem}"
+    return 1
+  fi
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedFreeSpace="$(formatSize "${freeSpace}")"
+  toLogInfo "Available free space: ${formattedFreeSpace}"
+
+  if [ "${freeSpace}" -lt "${requiredSpaceInKiB}" ]; then
+    printf 'Not enough free space on %s (filesystem mounted under %s). ' "${path}" "${baseFilesystem}"
+    printf 'Required: %s, available: %s' "$(cropSizeValue "${formattedRequiredSpace}")" "$(cropSizeValue "${formattedFreeSpace}")"
+    return 2
+  fi
+
+  printf 'Free space is sufficient'
+  return 0
+}
+
+#== 【3】=检查文件系统类型
+
+#== 【3】=检查安装目录空间
+checkInstallPathFreeSpace() {
+  local externalTarSize=${EXTERNAL_TAR_SIZE}
+  local artifactsSize=${ARTIFACTS_SIZE}
+  #== use 10% additional margin
+  local requiredSpace=$((externalTarSize + artifactsSize * 11 / 10))
+  #== convert to kibibytes
+  requiredSpace=$((requiredSpace / 1024))
+
+  toConsoleInfo "Checking free space in ${INSTALL_DIR}"
+
+  local message
+  #== 检查目录可用空间(单位:k)
+  message="$(checkFreeSpace "${INSTALL_DIR}" "${requiredSpace}")"
+  case $? in
+  0) toLogInfo "${message}" ;;
+  1) toConsoleWarn "${message}. Installation may be incomplete." ;;
+  2)
+    toConsoleError "${message}"
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_SPACE}"
+    ;;
+  esac
+}
+
+#== 【3】=获取指定路径的文件系统信息
+getFilesystemInfo() {
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    mount | grep " ${1} "
+  else
+    grep " ${1} " /proc/self/mounts
+  fi
+}
+
+#== 【3】=检查目录指定权限信息
+checkAccessRightsTo() {
+  local dir="${1}"
+  toLogInfo "Checking access to ${dir}..."
+  #== 获取文件权限信息
+  local accessRights
+  accessRights="$(arch_getAccessRights "${dir}" | cut -c 2-4)"
+  if ! printf '%s' "${accessRights}" | grep -q rwx; then
+    toConsoleError "Insufficient permissions on ${dir}: '${accessRights}'."
+    toLogInfo "$(ls -dl "${dir}" 2>&1)"
+    finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+  fi
+
+  local dfResult
+  local filesystem
+  local filesystemInfo
+  dfResult="$(df -P "${dir}")"
+  #== 获取目录文件系统路径
+  filesystem="$(printf '%s' "${dfResult}" | tail -1 | awk '{ print $NF }')"
+  #== 获取指定路径的文件系统信息
+  filesystemInfo="$(getFilesystemInfo "${filesystem}")"
+  if ! printf '%s' "${filesystemInfo}" | grep -qw rw; then
+    toLogWarn "df-based check determined filesystem mounted under ${filesystem} as readonly, trying fallback."
+    toLogWarn "Filesystem access rights: '${filesystemInfo}'"
+    toLogWarn "df returned: ${dfResult}"
+
+    if ! testWriteAccessToDir "${dir}"; then
+      toConsoleError "readonly filesystem mounted under ${filesystem}"
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+  fi
+
+  toLogInfo "Rights on directory ${dir} are sufficient"
+} 2>>"${LOG_FILE}"
+
+#== 【3】=目录是否写权限
+checkIfInstallationPathIsWriteable() {
+  for dir in "${@}"; do
+    if [ -d "${dir}" ]; then
+      checkAccessRightsTo "${dir}"
+      break
+    fi
+  done
+}
+
+#== 【3】=检查目录权限
+checkAccessRightsToDirs() {
+  checkIfInstallationPathIsWriteable "${INSTALL_DIR}" "${BASE_INSTALL_DIR}" "${INSTALL_BASE}" /
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    checkAccessRightsTo "${INIT_DIR}"
+  fi
+}
+
+
+#== 【0】=检查系统是 LINUX、AIX及指令集(X86_64\IA64\X86)
+checkSystemCompatibility() {
+  local expectedPlatform="LINUX"
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    expectedPlatform="AIX"
+  fi
+
+  #== 获取系统名称
+  local platform
+  platform="$(uname | sed -e 's/_.*//' | sed -e 's/\///' | tr '[:lower:]' '[:upper:]')"
+  if [ "${platform}"x != "${expectedPlatform}"x ]; then
+    printf "Cannot determine platform or platform not supported: <%s>" "${platform}"
+    return 1
+  fi
+
+  #== 检查系统指令集(X86_64\IA64\X86)
+  local detectedArchitecture
+  if ! detectedArchitecture="$(arch_checkArchitectureCompatibility)"; then
+    printf "Cannot determine architecture or architecture not supported: <%s>" "${detectedArchitecture}"
+    return 1
+  fi
+
+  printf 'Detected platform: %s' "${platform}"
+  if [ "${detectedArchitecture}" ]; then
+    printf ' arch: %s' "${detectedArchitecture}"
+  fi
+
+  return 0
+}
+
+#== 【4】=从sh文件读取压缩文件 tarfile_$$.base64
+separateExternalTar() {
+  toLogInfo "Determining begin of tar archive..."
+  local tarBegin
+  local tarEnd
+  #== 压缩文件开始行数,占位符为【#################ENDOFSCRIPTMARK############】
+  tarBegin="$(locateDelimiter "#################ENDOFSCRIPTMARK############" "")"
+  tarBegin=$((tarBegin + 1))
+  #== 压缩文件结束行数,占位符为【----SIGNED-INSTALLER】
+  tarEnd="$(locateDelimiter "----SIGNED-INSTALLER" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})"
+  toLogInfo "tarBegin=${tarBegin} tarEnd=${tarEnd}"
+
+  if [ ! "${tarEnd}" ]; then
+    toConsoleError "S/MIME signature is missing, installation package is corrupted."
+    finishInstallation "${EXIT_CODE_CORRUPTED_PACKAGE}"
+  fi
+
+  local tarLength=$((tarEnd - tarBegin))
+  toLogInfo "tarLength=${tarLength}"
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  tail -n +"${tarBegin}" "${INSTALLER_FILE}" 2>/dev/null | head -${tarLength} >"${EXTERNAL_TAR_FILE}"
+}
+
+#== 【4】【5】=进入目录
+changeWorkingDir() {
+  if ! cd "${1}"; then
+    toLogError "Failed to change working directory to ${1}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== shellcheck disable=SC2181
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchiveWithoutCache() {
+  #== 解压文件编码
+  local base64Binary="${UNPACK_BINARY}"
+  #== 解压文件编码参数
+  local base64BinaryArgs="${UNPACK_BINARY_ARGS}"
+  #== 使用xzdec命令可以进行liblzma为基础的xz文件解压缩
+  local xzBinary="${INSTALL_DIR}/xzdec"
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+
+  if ! isAvailable tar; then
+    toConsoleError "tar binary not found. Setup can't continue"
+    finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  local totalLines
+  if ! isAvailable "${base64Binary}"; then
+    toLogInfo "${base64Binary} not found. Falling back to openssl decode"
+    if ! isAvailable openssl; then
+      toConsoleError "Neither ${base64Binary} nor openssl can be found. Setup can't continue"
+      finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+    fi
+    #== 如果没有 base64 命令,则使用 openssl 解压文件
+    base64Binary="openssl"
+    base64BinaryArgs="enc -base64 -d -in"
+
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      #truncate the first and the last one line due to specific format of uuencode on aix
+      totalLines="$(wc -l "${EXTERNAL_TAR_FILE}" | awk '{print $1}')"
+      head -$((totalLines - 1)) "${EXTERNAL_TAR_FILE}" 2>/dev/null | tail +2 >"${EXTERNAL_TAR_FILE}.$$"
+      mv -f "${EXTERNAL_TAR_FILE}.$$" "${EXTERNAL_TAR_FILE}"
+    fi
+  fi
+
+  {
+    #== base64 转编码后,执行 tar 解压
+    "${base64Binary}" "${base64BinaryArgs}" "${EXTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  #== base64 转编码 或 tar解压失败
+  if [ $? -gt 0 ]; then
+    toConsoleError "Archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+
+  #== 进入目录
+  changeWorkingDir "${TMP_DIR}"
+  {
+    #== 使用xzdec命令解压后,执行 tar 解压
+    "${xzBinary}" "${INTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  if [ $? -gt 0 ]; then
+    toConsoleError "XZ compressed archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+}
+
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchive() {
+  toConsoleInfo "Unpacking. This may take a while..."
+
+  #== 【参数】是否使用解压缓存
+  if [ "${PARAM_INTERNAL_USE_UNPACK_CACHE}"x = "true"x ]; then
+    if [ -d "${UNPACK_CACHE}" ]; then
+      toLogInfo "Unpack cache will be used."
+      cp -Rp "${UNPACK_CACHE}"/* "${TMP_DIR}"
+    else
+      toLogInfo "Unpack cache does not exist."
+      mkdir -p "${UNPACK_CACHE}"
+      #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+      unpackArchiveWithoutCache
+      cp -Rp "${TMP_DIR}"/* "${UNPACK_CACHE}"
+    fi
+  else
+    toLogInfo "Unpacking without cache"
+    #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+    unpackArchiveWithoutCache
+  fi
+
+  toConsoleInfo "Unpacking complete."
+}
+
+#== 【3】=检查容器部署、运行情况、配置参数
+#isDeployedInsideOpenVZContainer
+#isProcessRunningInContainer
+#isDeployedViaContainer
+
+
+#== Checking if libc is new enough
+#== 【3】=格式化version
+format_version() {
+  printf '%s' "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'
+}
+
+#== 【3】=格式化路径
+format_patch() {
+  printf '%s' "$@" | tail -n 1 | awk -F- '{ printf("%d\n", $3); }'
+}
+
+#== 【3】=检查是否降级安装
+checkIfDowngrade() {
+  if [ "${SKIP_DOWNGRADE_CHECK}"x = "true"x ]; then
+    toConsoleInfo "Skipped downgrade check"
+    return
+  fi
+
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  if [ ! -f "${installVersionFile}" ]; then
+    toLogWarn "Could not perform downgrade check, ${installVersionFile} file is missing"
+    return
+  fi
+
+  local oldVersion
+  oldVersion="$(cat "${installVersionFile}")"
+  if [ "$(format_version "${AGENT_INSTALLER_VERSION}")" -eq "$(format_version "${oldVersion}")" ]; then
+    toConsoleError "${BRAND_PRODUCT_NAME} is already installed, please uninstall the old version using [${INSTALL_DIR}/uninstall.sh]."
+#    toLogError "Attempted downgrade from ${oldVersion} to ${AGENT_INSTALLER_VERSION}"
+    finishInstallation "${EXIT_CODE_UNSUPPORTED_DOWNGRADE}"
+  fi
+}
+
+#== 【3】=检查是否已经安装
+checkIfAlreadyInstalled() {
+  if [ -f "${INSTALL_DIR}/uninstall.sh" ] && [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ] ; then
+#    toConsoleError "Agent already installed. Uninstalling previous version."
+#    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+
+    checkIfDowngrade
+    PARAM_UPGRADE="yes"
+  else
+#  		if [ -f "${SIF_AGENT_INSTALL_PATH}/lib64/${AGENT_BIN}" ]; then
+#  			sif_toConsoleError "Upgrade is not possible because uninstall script is missing"
+#  			finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+#  		fi
+      local fileNum
+      fileNum=`ls ${INSTALL_DIR} | wc -l`
+      if [ "${fileNum}" -gt 1 ];then
+         toConsoleError "${INSTALL_DIR} is not empty. Please clean or change the installation directory manually."
+         finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+      fi
+  fi
+
+
+
+}
+
+#**********************************************************
+# Init related functions
+#**********************************************************
+
+#== clears dependencies in LSB init script
+#== 【5】=清除LSB init脚本中的依赖项
+clearDependenciesInLSBInit() {
+  local file="${1}"
+  toConsoleInfo "Clearing dependencies in file ${file}"
+  awk '
+		BEGIN {
+			req_start_found=0;
+			req_stop_found=0;
+			REQ_START="# Required-Start:";
+			REQ_STOP="# Required-Stop:";
+			PATTERN_REQ_START="^" REQ_START;
+			PATTERN_REQ_STOP="^" REQ_STOP;
+		}
+		{
+			if ($0 ~ PATTERN_REQ_START && req_start_found == 0) {
+				print REQ_START;
+				req_start_found++;
+			} else if ($0 ~ PATTERN_REQ_STOP && req_stop_found == 0) {
+				print REQ_STOP;
+				req_stop_found++;
+			} else
+				print $0
+		}' "${file}" >"${file}.tmp" && mv -f "${file}.tmp" "${file}"
+
+  chmod +x "${file}"
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptToSystemvAutostart() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+
+  toLogInfo "Adding ${file} to autostart"
+  #== 自动启动工具
+  if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+    toLogWarn "Failed to add ${file} script to autostart. Trying without dependencies..."
+    #== 清除LSB init脚本中的依赖项
+    clearDependenciesInLSBInit "${INIT_DIR}/${file}"
+    if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+      toConsoleError "Cannot add ${file} to autostart. For details, see: ${LOG_FILE}"
+    fi
+  fi
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptsToAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+  #== 指定目录添加自动启动脚本
+  addScriptToSystemvAutostart "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"
+}
+
+#== 【5】=设置自动启动
+#setupSystemvAutostart() {
+#  toLogInfo "Adding ${BRAND_AGENT_PRODUCT_NAME} to autostart..."
+#
+#  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+#    #== 指定目录添加自动启动脚本
+#    addScriptsToAutostart "/usr/bin/update-rc.d " "defaults"
+#  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+#    addScriptsToAutostart "/usr/sbin/update-rc.d " "defaults"
+#  elif [ -x /sbin/chkconfig ]; then #RedHat
+#    addScriptsToAutostart "/sbin/chkconfig --add "
+#  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+#    addScriptsToAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+#  elif [ "${ARCH_ARCH}"x = "AIX"x ]; then
+#    #== 【不存在】
+#    arch_setAutostart
+#  else
+#    toConsoleError "Couldn't add ${BRAND_AGENT_PRODUCT_NAME} to autostart. Please adjust and add it manually."
+#  fi
+#}
+
+#== 【5】=设置 cwserveragent.service 中用户
+setServiceScriptUser() {
+  if ! isNonRootModeEnabled; then
+    return
+  fi
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 修改 cwserveragent.service 中用户信息
+  sed -i "s/User=.*/User=${user}/g" "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+}
+
+#== 【5】=配置自动启动 cwserveragent.service
+setupSystemdAutostart() {
+  mv -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" "${SYSTEMD_UNIT_FILES_DIR}/"
+  setRightsForFiles "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 644
+
+  #== 设置 cwserveragent.service 中用户
+  setServiceScriptUser
+
+  if isAvailable restorecon; then
+    restorecon "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  #== 执行 systemctl 命令配置允许开机启动 cwserveragent.service
+  executeSystemctlCommand enable "${SYSTEMD_UNIT_FILE_AGENT}"
+  #== 执行 systemctl 命令重新加载模块
+  executeSystemctlCommand daemon-reload ""
+}
+
+#== 【6】=运行service 脚本
+execIntoServiceScript() {
+  toConsoleInfo "${SERVICE_SCRIPT_FILE} will be started via exec()"
+  #== 清除安装临时文件
+  cleanInstallationTemporaryFiles
+  toLogInfo "Installation finished, PID $$."
+
+  local command="exec ${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} exec"
+  toLogInfo "Executing: ${command}"
+  #== 执行 cwserveragent exec
+  ${command}
+
+  toLogError "Could not execute: ${command}"
+  finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+}
+
+#== 【6】=运行agent
+runAgents() {
+  toConsoleInfo "Starting agents..."
+
+  toLogInfo "Using ${INIT_SYSTEM} to start the agent"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    #== 运行初始化命令(通过 service start 方式 或 直接运行可执行命令)
+    executeInitScriptCommand start "true"
+  else
+    #== 执行 systemctl start 命令
+    executeSystemctlCommand start "${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  if [ $? -eq 0 ]; then
+    toConsoleInfo "${SERVICE_SCRIPT_FILE} service started"
+  else
+    toConsoleError "Failed to start service: ${SERVICE_SCRIPT_FILE}, it is possible that your init system is not functioning properly. For details, see: ${LOG_FILE}"
+  fi
+}
+
+#== 【5】=配置自动启动
+#setupAutostart() {
+#  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+#    #== 设置自动启动
+#    setupSystemvAutostart
+#  else
+#    #== 配置自动启动 cwserveragent.service
+#    setupSystemdAutostart
+#  fi
+#}
+
+#**********************************************************
+# Process agent related functions
+#**********************************************************
+
+#== 【5】=创建 agent 状态文件
+createAgentStateFile() {
+  local path="${1}"
+  local agentStateContents="RUNNING"
+
+  toLogInfo "Writing ${agentStateContents} to ${path} file"
+  {
+    printf "%s" "${agentStateContents}" >"${path}.tmp"
+    mv -f "${path}.tmp" "${path}"
+  } 2>>"${LOG_FILE}"
+}
+
+#== 【0】=检查全路径是否可读权限(目录最深100层)
+checkIfPathIsGloballyReadable() {
+  local path="${1}"
+  local sourcePath="${path}"
+  local maxDepth=100
+  while [ "${path}"x != "/"x ]; do
+    #== 【0】=获取文件权限信息
+    local accessRights
+    accessRights="$(arch_getAccessRights "${path}")"
+    if ! printf '%s' "${accessRights}" | cut -c 8-10 | grep -qE "r.[xt]"; then
+      toConsoleError "Insufficient access rights (${accessRights}) on: ${path}"
+      toConsoleError "${sourcePath} path must be globally readable (r-x permissions for others)."
+      toConsoleError "Please adjust the permissions and then retry the installation."
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+
+    path="$(dirname "${path}")"
+
+    maxDepth=$((maxDepth - 1))
+    if [ "${maxDepth}" -eq 0 ]; then
+      toLogWarn "Unable to verify access rights on ${path}"
+      return
+    fi
+  done
+}
+
+#== 【0】=创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+createSymlinkToInstallLocation() {
+  #== INSTALL_DIR 不能是软链接地址
+  if [ -L "${INSTALL_DIR}" ] && [ ! -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Detected that ${INSTALL_DIR} is a dangling symlink, please remove it and then retry the installation"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== PARAM_INSTALL_DIR 和 INSTALL_DIR 都不能是空地址
+  if [ "${PARAM_INSTALL_DIR}"x = "${INSTALL_DIR}"x ] || [ -z "${PARAM_INSTALL_DIR}" ]; then
+    return
+  fi
+
+  #== INSTALL_DIR 是软连接,并获取 PARAM_INSTALL_DIR 和 INSTALL_DIR 的真实地址
+  if [ -L "${INSTALL_DIR}" ] && [ "$(readLink -m "${PARAM_INSTALL_DIR}")"x = "$(readLink -m "${INSTALL_DIR}")"x ]; then
+    return
+  fi
+
+  #== 目录存在,表示未卸载
+  if [ -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Leftovers from previous agent installation detected"
+    toConsoleError "If you wish to use INSTALL_DIR parameter then perform a cleanup by following these steps:"
+    toConsoleError "1. Uninstall the agent"
+    toConsoleError "2. Restart all applications that have Deep Monitoring enabled (host restart is fine as well)"
+    toConsoleError "3. Remove ${INSTALL_DIR}"
+    toConsoleError "and then retry the installation."
+    toConsoleError "#DEBUG==For further information please visit ${HELP_URL}/command-line-install"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 创建不存在的目录
+  createDirIfNotExistAndSetRights "${PARAM_INSTALL_DIR}" 1775
+  #== 检查全路径是否可读权限(目录最深100层)
+  checkIfPathIsGloballyReadable "${PARAM_INSTALL_DIR}"
+  #== 创建不存在的目录 /opt/cloudwise
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" 755
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR
+  local lnOutput
+  if lnOutput="$(ln -fs "${PARAM_INSTALL_DIR}" "${INSTALL_DIR}" 2>&1)"; then
+    toConsoleInfo "Symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR} created"
+  else
+    toConsoleError "Failed to create symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR}, aborting installation: ${lnOutput}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【5】=修改文件系统用户和用户组
+changeFilesOwnership() {
+  local user
+  local group
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Changing ownership of files to root:${group}"
+  #== /opt/cloudwise/cwserveragent 目录下用户和用户组
+  commandErrorWrapper chown "${user}:${group}" "${AGENT_BASE_DIR}"
+  commandErrorWrapper chown -R "${user}:${group}" "${INSTALL_DIR}"
+  toLogInfo "Recursively changing group ownership of ${AGENT_CONF_DIR} to ${group}"
+
+}
+
+#== 【5】=文件用户权限检查
+fileCapabilitiesCompatibilityCheck() {
+  #== 获取配置用户
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+  if output="$("$(getOsConfigBinPath)" file-capabilities-compatibility-check "${user}" 2>&1)"; then
+    return 0
+  fi
+
+  if [ "${PARAM_NON_ROOT_MODE}" ]; then
+    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogWarn "Capabilities test output: ${output}"
+  else
+    toConsoleInfo "Non-privileged mode was not enabled, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogInfo "Capabilities test output: ${output}"
+  fi
+  #== 设置删除root权限
+  "$(getAgentInstallActionPath)" "--set-drop-root-privileges" "false" >>"${LOG_FILE}" 2>&1
+  return 1
+}
+
+
+#**********************************************************
+# User and group related functions
+#**********************************************************
+
+#== 【5】=添加用户组
+addGroup() {
+  local group="${1}"
+
+  if groupExistsInSystem "${group}"; then
+    toLogInfo "Group '${group}' already exists"
+    return 0
+  fi
+
+  local errorMessage
+  errorMessage="$(groupadd "${group}" 2>&1)"
+  local returnCode=$?
+
+  case ${returnCode} in
+  0) toLogInfo "Group '${group}' successfully created" ;;
+  9) toLogInfo "Group '${group}' already exists" ;;
+  *)
+    toLogError "Error occured while adding '${group}' group, return code: ${returnCode}, message ${errorMessage}"
+    return 1
+    ;;
+  esac
+  return 0
+}
+
+#== 【5】=添加用户
+addUser() {
+  local user="${1}"
+  local group="${2}"
+  local groupCreated="${3}"
+  local errorMessage
+  local mod="/bin/false"
+  if [ "${PARAM_USER_LOGIN}"x == "true"x ]; then
+    mod="/bin/bash"
+  fi
+  if userExistsInSystem "${user}"; then
+    toLogInfo "User '${user}' already exists."
+    #-- 权限更新
+#    errorMessage="$(usermod -s "${mod}" "${user}" 2>&1)"
+    return 0
+  fi
+
+  if [ "${groupCreated}" -eq 0 ]; then
+    errorMessage="$(useradd -r --shell "${mod}" -g "${group}" "${user}" 2>&1)"
+  else
+    errorMessage="$(useradd -r --shell "${mod}" "${user}" 2>&1)"
+  fi
+
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toConsoleError "Failed to create user '${user}'"
+    toLogError "Error occured while adding '${user}' user, return value: ${returnCode}, error message: ${errorMessage}."
+    return 1
+  fi
+
+  toConsoleInfo "User '${user}' added successfully."
+  return 0
+}
+
+#== 【5】=添加用户和用户组信息
+addUserAndGroup() {
+  local user="${1}"
+  local group="${2}"
+  #== 添加用户组
+  addGroup "${group}"
+  #== 添加用户
+  addUser "${user}" "${group}" $?
+}
+
+#== 【5】=配置用户和用户组
+handleUser() {
+  toLogInfo "Processing user and group..."
+  local user
+  local group
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 读取用户组(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-group)
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 添加用户和用户组信息
+  addUserAndGroup "${user}" "${group}"
+}
+
+#== 【4】=获取agent preLoad 安装路径,并校验是否可执行
+checkCompatibilityWithInstallActionBinary() {
+  local output
+  #== 获取agent preLoad 安装路径,并校验是否可执行
+  output="$("$(getAgentInstallActionPathPreInstallation)" "--sanity-check" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Compatibility check exit code = ${exitCode}, output = ${output}"
+  if [ ${exitCode} -ne 0 ] || [ "${output}"x != "SUCCESS"x ]; then
+    toConsoleError "System compatibility check failed, this may be caused by a problem with glibc, dynamic loader or incompatible operating system version."
+    toConsoleError "Detected version: $(detectLinuxDistribution)"
+    toConsoleError "For a list of supported distributions and versions, see: ${HELP_URL}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【部署】解压文件
+extractFiles() {
+  umask 000
+  toConsoleInfo "Extracting..."
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  separateExternalTar
+  #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+  unpackArchive
+  umask 022
+}
+
+#== 【3】=检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+detectInitSystem() {
+  #== 获取初始化系统信息 (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  #== 设置系统初始化脚本目录
+  if ! setLocationOfScripts; then
+    toConsoleError "Cannot determine location of init scripts."
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【旧配置】读取旧配置参数
+readLegacySetting() {
+  local paramName="${1}"
+  local value
+  value="$(sed -n "s|^${paramName}=||p" "${LEGACY_AGENT_CONF_FILE}" | tr -d '\r' 2>/dev/null)"
+  if [ "${value}" ]; then
+    local valueToPrint="${value}"
+    if [ "${paramName}"x = "${CONF_FIELD_NM_LICENSE}"x ]; then
+      valueToPrint="***"
+    fi
+    toLogInfo "Read legacy value: ${paramName} = ${valueToPrint}"
+  fi
+
+  printf '%s' "${value}"
+}
+
+#== 【4】=【旧配置】迁移旧配置文件
+migrateLegacySettingsFromAgentConf() {
+  toLogInfo "Looking for legacy config to migrate."
+  #== 旧配置文件
+  if [ ! -f "${LEGACY_AGENT_CONF_FILE}" ]; then
+    toLogInfo "Unable to read agent config file, skipping legacy config migration"
+    return
+  fi
+
+  if [ ! "${PARAM_CONFIG_SERVER}" ]; then
+    local configServerValue
+    configServerValue="$(readLegacySetting ${CONF_FIELD_NM_CONFIG_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_CONFIG_SERVER}: ${PARAM_CONFIG_SERVER}"
+    if [ "${configServerValue}"x = "http://localhost:8020"x ]; then
+      toLogInfo "Param 'server' has default value set, skipping it"
+    else
+      PARAM_CONFIG_SERVER="${configServerValue}"
+    fi
+  fi
+
+  if [ ! "${PARAM_DATA_SERVER}" ]; then
+    PARAM_DATA_SERVER="$(readLegacySetting ${CONF_FIELD_NM_DATA_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_DATA_SERVER}: ${PARAM_DATA_SERVER}"
+  fi
+
+  if [ ! "${PARAM_LICENSE}" ]; then
+    PARAM_LICENSE="$(readLegacySetting ${CONF_FIELD_NM_LICENSE})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_LICENSE}: ${PARAM_LICENSE}"
+  fi
+
+  if [ ! "${PARAM_JSON_CONF}" ]; then
+    PARAM_JSON_CONF="$(readLegacySetting ${CONF_FIELD_NM_JSON_CONF})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_JSON_CONF}: ${PARAM_JSON_CONF}"
+  fi
+
+  if [ ! "${PARAM_USER}" ]; then
+    PARAM_USER="$(readLegacySetting ${CONF_FIELD_NM_USER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_USER}: ${PARAM_USER}"
+
+    if [ ! "${PARAM_GROUP}" ]; then
+      PARAM_GROUP="$(readLegacySetting ${CONF_FIELD_NM_GROUP})"
+      toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_GROUP}: ${PARAM_GROUP}"
+    fi
+  fi
+}
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E \"${includeRegex}\" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(pgrep -f "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses
+  foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+#== 版本&帮助信息
+preParams() {
+  if [ $# -gt 0 ]; then
+    if [ "${1}"x = "-h"x ] || [ "${1}"x = "--help"x ]; then
+        displayHelp
+        exit "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${1}"x = "-v"x ] || [ "${1}"x = "--version"x ]; then
+       pad=20
+       printf "%-${pad}s%s\n" "Version:" "${AGENT_INSTALLER_VERSION}"
+       printf "%-${pad}s%s\n" "Commit:" "${AGENT_BUILD_TAG}"
+       printf "%-${pad}s%s\n" "Build At(UTC):" "${AGENT_BUILD_DATE_INFO}"
+       printf "%-${pad}s%s\n" "Uploader:" "${AGENT_BUILD_UPLOADER}"
+       exit "${EXIT_CODE_OK}"
+    fi
+  fi
+}
+#**********************************************************
+# Main script functions
+#**********************************************************
+
+#== 【0】=初始化安装环境
+initializeInstallation() {
+  preParams "$@"
+  #== 设置PATH
+  setPATH
+  #== 配置umask权限
+  local initialUmask
+  initialUmask="$(umask)"
+  umask 022
+
+  #== 读取配置参数
+#  readParamsSection
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  #== 校验配置参数
+
+  #== 是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+  if isAnotherInstallationRunning; then
+    #== 结束安装操作
+    finishInstallation "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}" "keep_lock_file"
+  fi
+
+  #== 创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+  createInstallationLockFile
+  #== 配置是否更新
+  PARAM_UPGRADE="no"
+
+  #== 获取并校验系统兼容性【LINUX/AIX  X86_64/IA64】
+  local platformDetectionString
+  if ! platformDetectionString="$(checkSystemCompatibility)"; then
+    toConsoleError "${platformDetectionString}"
+    finishInstallation "${EXIT_CODE_OS_NOT_SUPPORTED}"
+  fi
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+  createSymlinkToInstallLocation
+  #== 创建日志目录及日志文件installation_$$.log
+  createLogDirsIfMissing
+  #== 配置信号捕获,执行清空安装临时目录
+  configureSignalHandling "cleanInstallationTemporaryFiles"
+  #== 常规日志
+  initializeLog "${@}"
+
+  toLogInfo "Initial umask: ${initialUmask}"
+  toConsoleInfo "${platformDetectionString}"
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+}
+
+#== 【3】=安装前检查
+preInstallationChecks() {
+  #== 检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+  detectInitSystem
+
+  #== 检查目录权限
+  checkAccessRightsToDirs
+  #== 创建临时目录
+  prepareTempFolder
+  #== 检查是否已经安装
+  checkIfAlreadyInstalled
+
+  if [ ! "${PARAM_USER}" ] && [ "${ARCH_ARCH}"x != "AIX"x ] ; then
+    #== 配置文件中配置用户和用户组是否存在
+    checkUserAndGroupFromConfig
+  fi
+
+  #== 检查安装目录空间
+  checkInstallPathFreeSpace
+
+}
+
+uninstallAgent() {
+	toConsoleInfo "Agent already installed. Uninstalling previous version."
+#	printf '%s' "upgrade" >"${UNINSTALL_INFO_FILE_LEGACY_PATH}"
+
+	#shellcheck disable=SC2086
+	"${INSTALL_DIR}/uninstall.sh" $$ ${SKIP_PRIVILEGES_CHECK} 2>>"${LOG_FILE}"
+
+	local uninstallExitCode=$?
+	if [ ${uninstallExitCode} -gt 0 ]; then
+		toConsoleError "Error during uninstalling, code: ${uninstallExitCode}. Installation aborted."
+		finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+	toConsoleInfo "Agent uninstalled."
+}
+
+#== 【4】=部署文件
+deployFiles() {
+  if [ "${PARAM_UPGRADE}" = "yes" ]; then
+    uninstallAgent
+  fi
+  #== 【部署】解压文件
+  extractFiles
+  setupAll
+  createCurrentVersionSymlink
+}
+enableRootDropping() {
+  local output=
+  if ! isAvailable setcap; then
+    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. Set NON_ROOT_MODE=false."
+    #== writeParamToConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "false" "${LEGACY_AGENT_CONF_FILE}"
+    #== writeParamToConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "false" "${INSTALLER_CONF_FILE}"
+    #== editScriptFileParam "readonly PARAM_NON_ROOT_MODE" "false" "${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE}"
+    return
+  fi
+
+  #== 设置文件权限 (https://www.cnblogs.com/xzongblogs/p/14106481.html)
+  #== CAP_DAC_OVERRIDE:绕过文件的读,写,和执行权限检查。
+  #== CAP_FOWNER:对于通常要求进程的文件系统 UID 与文件的 UID 匹配的操作,绕过权限检查 (比如,chmod(2),utime(2)),除了那些包含在 CAP_DAC_OVERRIDE 和 CAP_DAC_READ_SEARCH 中的操作
+  #== CAP_IPC_LOCK:允许锁定共享内存片段
+  #== CAP_SYS_PTRACE:允许跟踪任何进程
+  #== CAP_SYS_ADMIN:访问特权 perf 事件信息
+  #== CAP_SYS_RESOURCE:忽略资源限制
+  #== CAP_NET_ADMIN:允许执行多种网络有关的操作
+  commandErrorWrapper setcap cap_setgid,cap_setuid,cap_dac_override,cap_fowner,cap_chown,cap_ipc_lock,cap_sys_ptrace,cap_sys_admin,cap_sys_resource,cap_net_admin+ep "${AGENT_BIN_DIR}/${AGENT_PROC}"
+  local setCapCwServerAgentExitCode=$?
+  if [ ${setCapCwServerAgentExitCode} -eq 0 ] ; then
+    toConsoleInfo "Set file capabilities [${AGENT_PROC}]"
+  else
+    toConsoleWarn "Failed to enable non-privileged mode. Exit Code : ${setCapCwServerAgentExitCode} . For details, see: ${LOG_FILE}"
+    toLogWarn "Set file capabilities output: ${output}"
+  fi
+}
+
+#== 【5】=配置安装
+configureInstallation() {
+  #== 配置参数(param、installation.conf)
+  #== 自定义修改配置, sed ,echo ...  参考 applyAgentSettings
+  if [ "${PARAM_TEST}" ]; then
+    toConsoleOnly "Applying test value is: ${PARAM_TEST}"
+  fi
+  #== 获取bool值参数
+  if [ "${PARAM_BOOL}" ]; then
+    toConsoleOnly "Applying bool value is: ${PARAM_BOOL}"
+  fi
+
+
+  if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+    #== 配置用户和用户组
+    if handleUser; then
+      #== 修改文件系统用户和用户组
+      changeFilesOwnership
+    fi
+    #== 设置root权限
+    enableRootDropping
+  fi
+  #== 系统配置(策略配置、dump proc、agent 进程配置、自动启动)
+#  setupAutostart
+}
+
+#== 【6】=验证安装状态、运行启动脚本
+postInstallationSteps() {
+  #== 运行agent
+#  runAgents
+  finishInstallation "${EXIT_CODE_OK}"
+}
+
+readonly CURR_PATH="$(pwd)"
+main() {
+  #== 【0】=初始化安装环境
+  initializeInstallation "$@"
+  #== 【3】=安装前检查
+  preInstallationChecks
+  #== 【4】=部署文件
+  deployFiles
+  #== 【5】=配置安装
+  configureInstallation
+  #== 【6】=验证安装状态、运行启动脚本
+  postInstallationSteps
+
+}
+
+#**********************************************************
+# Script start
+#**********************************************************
+main "$@"
+
+################################################
+############# DO NOT REMOVE THIS ###############
+#### DO NOT ADD ANYTHING BELOW THIS COMMENT ####
+################################################
+#################ENDOFSCRIPTMARK################
+
+----SIGNED-INSTALLER

+ 611 - 0
dist/aarch64/scripts/package.sh

@@ -0,0 +1,611 @@
+#!/bin/bash
+
+AGENT_INSTALLER_VERSION=1.0.0
+
+#== 当前脚本必须放在 smartagent/scripts 目录下
+readonly SCRIPT_PATH=$(dirname $0)
+readonly SCRIPT_DIR=$(cd $SCRIPT_PATH && pwd)
+readonly CURR_FILE_NAME="$(basename "$0")"
+readonly SMARTAGENT_DIR=$(cd $SCRIPT_DIR/../ && pwd)
+readonly SMARTAGENT_PARENT_DIR=$(cd $SCRIPT_DIR/../../ && pwd)
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+BRAND_PRODUCT_NAME="euspace"
+BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="cwserveragent"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="ucloudwise"
+
+
+readonly BAK_EXT_NAME=".bak"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+readonly XZDEC_NAME="xzdec"
+readonly INSTALL_TEMP_FILE_NAME="install_temp.sh"
+readonly INSTALL_VERSION_FILE_NAME="installer.version"
+#== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+ARCH="aarch64"
+TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+#TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+
+TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+
+readonly LOG_DIR="${SMARTAGENT_PARENT_DIR}/logs"
+readonly LOG_FILE_NAME="release.log"
+readonly LOG_FILE="${LOG_DIR}/${LOG_FILE_NAME}"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+readonly EXIT_CODE_SEMANAGE_NOT_FOUND=10
+readonly EXIT_CODE_WATCHDOG_ALREADY_RUNNING=11
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+readonly EXIT_CODE_DEL_DEFAULT_PARAMS=20
+
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#**********************************************************
+# compress
+#**********************************************************
+tarSmartAgent() {
+  toConsoleInfo "There are 5 steps to packing, begin package ${BRAND_AGENT_PRODUCT_NAME}...."
+  #== 将打包脚本(package.sh、xzdec、install_temp.sh、smartagent、uninstall.sh)移动到临时目录
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    mv -f "${SCRIPT_DIR}/${fileNm}" "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" 2>>"${LOG_FILE}"
+  done
+  mv -f "${SCRIPT_DIR}/${XZDEC_NAME}" "${SMARTAGENT_PARENT_DIR}/" 2>>"${LOG_FILE}"
+  cp -f "${SMARTAGENT_PARENT_DIR}/${INSTALL_TEMP_FILE_NAME}${BAK_EXT_NAME}" "${TARGET_INSTALL_TMP_FILE}" 2>>"${LOG_FILE}"
+
+  for fileNm in "uninstall.sh"; do
+    cp -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+  done
+
+  #== 删除安装脚本中注释
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    toConsoleInfo "Delete the type [#==] comment ..."
+    sed -i '/#==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#==/d' "${SCRIPT_DIR}/uninstall.sh"
+    toConsoleInfo "Delete the type [#DEBUG==] comment ..."
+    sed -i '/#DEBUG==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/uninstall.sh"
+  fi
+
+  if [ "${PARAM_RELEASE}"x = "true"x ]; then
+    deleteDefaultParams "${TARGET_INSTALL_TMP_FILE}"
+  fi
+
+  #== 进入 smartagent 目录下
+  cd "${SMARTAGENT_DIR}"
+  cp -f "${SCRIPT_DIR}/uninstall.sh" ./package_dir
+  #== 执行第一阶段压缩(源码压缩)Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【1】start【tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" ./package_dir/*】to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" --exclude="*.gitkeep" --exclude="*.log" ./package_dir/* >>"${LOG_FILE}" 2>&1
+  rm -f "./package_dir/uninstall.sh"
+
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+  local agentTarSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar")"
+
+  cd "${SMARTAGENT_PARENT_DIR}"
+  #== 执行第二阶段压缩(源码压缩 Cloudwise-SmartAgent.tar.xz
+  toConsoleInfo "step【2】start【xz -T0 -z ${BRAND_AGENT_PRODUCT_NAME}.tar】to ${BRAND_AGENT_PRODUCT_NAME}.tar.zx"
+  xz -T0 -1 -z "${BRAND_AGENT_PRODUCT_NAME}.tar" -vv
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第三阶段压缩 Cloudwise-SmartAgent.tar.xz xzdec to Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【3】start【tar -cpf ${BRAND_AGENT_PRODUCT_NAME}.tar ${BRAND_AGENT_PRODUCT_NAME}.tar.xz xzdec】 to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${BRAND_AGENT_PRODUCT_NAME}.tar" "${BRAND_AGENT_PRODUCT_NAME}.tar.xz" xzdec >>"${LOG_FILE}" 2>&1
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第四阶段压缩 Cloudwise-SmartAgent.tar to Cloudwise-SmartAgent.tar.base64
+  toConsoleInfo "step【4】start【base64 ${BRAND_AGENT_PRODUCT_NAME}.tar > ${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${BRAND_AGENT_PRODUCT_NAME}.tar.base64"
+  base64 "${BRAND_AGENT_PRODUCT_NAME}.tar" >"${BRAND_AGENT_PRODUCT_NAME}.tar.base64" 2>>"${LOG_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第五阶段将 base64 文件写入安装脚本
+  toConsoleInfo "step【5】start write 【${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+  #== 设置脚本中版本、创建时间、占用空间信息
+  editInstallScriptParam "${TARGET_INSTALL_TMP_FILE}" ${agentTarSize}
+
+  sed -i "/#################ENDOFSCRIPTMARK################/r ${BRAND_AGENT_PRODUCT_NAME}.tar.base64" "${TARGET_INSTALL_TMP_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  toConsoleInfo "${BRAND_AGENT_PRODUCT_NAME} packaging success."
+  finishPackage "${EXIT_CODE_OK}"
+}
+
+buildVersion() {
+  local oldVersion
+  if oldVersion="$(getOldVersion)"; then
+    setInstallVersion "${oldVersion}"
+    removeIfExist "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+		toConsoleInfo "Delete file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}."
+  fi
+
+  if [ "${PARAM_VERSION}" ]; then
+    setInstallVersion "${PARAM_VERSION}"
+    return
+  fi
+}
+
+setInstallVersion() {
+  local version="${1}"
+  if [ ! "${version}" ]; then
+    toConsoleWarn "Set install version failed,not found version version."
+    return
+  fi
+
+  AGENT_INSTALLER_VERSION="${version}"
+  BRAND_PRODUCT_NAME="${PARAM_NAME}"
+  BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+  TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+  #== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+#  TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+  TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+}
+
+getOldVersion() {
+	local installVersionFile="${SMARTAGENT_DIR}/installer.version"
+	if [ ! -f "${installVersionFile}" ]; then
+		toLogWarn "Could not find ${installVersionFile} file."
+		return 1
+	fi
+
+	local oldVersion="$(cat "${installVersionFile}")"
+	if [ ! "${oldVersion}" ]; then
+		toLogWarn "Could not read install version from ${installVersionFile} file."
+	  return 1
+  fi
+
+  printf '%s' "${oldVersion}"
+  return 0
+}
+
+#== 通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local filePath="${3}"
+
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    local linesCount="$(wc -l "${filePath}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    local offset="$(tail -n"${linesToReadFromEnd}" "${filePath}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${filePath}"
+  fi
+}
+
+#== 删除安装脚本中默认参数
+deleteDefaultParams() {
+  local filePath="${1}"
+  local sectionName="----PARAMETERS"
+  local begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+  local end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+
+  toConsoleInfo "Begin delete default params from line ${begin} to ${end}..."
+  local output="$(sed -i "${begin},${end}d" "${filePath}") 2>&1"
+  if [ ! "${output}" ]; then
+    toConsoleError "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params error:${output}"
+    finishPackage "${EXIT_CODE_DEL_DEFAULT_PARAMS}"
+  fi
+  toConsoleInfo "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params success."
+}
+
+#== 设置脚本中版本、创建时间、占用空间信息
+editInstallScriptParam() {
+  local targetFile="${1}"
+  local agentTarSize="${2}"
+  #== 设置版本信息
+  local buildDate="$(date +"%Y-%m-%d")"
+  local buildTag="$(git rev-parse --short HEAD)"
+  local buildDateInfo="$(date +"%Y-%m-%d %H:%M:%S")"
+  local buildUploader="$(hostname)/${USER}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE" "${buildDate}" "${targetFile}"
+  #== 构建版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${targetFile}"
+  #== commitid
+  editScriptFileParam "readonly AGENT_BUILD_TAG" "'${buildTag}'" "${targetFile}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE_INFO" "'${buildDateInfo}'" "${targetFile}"
+  #== 构建环境信息
+  editScriptFileParam "readonly AGENT_BUILD_UPLOADER" "'${buildUploader}'" "${targetFile}"
+
+  #== 设置占用空间信息
+  local agentDirSize="$(getStorageSize "${SMARTAGENT_DIR}")"
+  local agentBase64FileSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar.base64")"
+  local agentInstallFileSize="$(getStorageSize "${TARGET_INSTALL_TMP_FILE}")"
+  local artifactsSize="$((agentBase64FileSize + agentInstallFileSize + agentDirSize))";
+  editScriptFileParam "readonly EXTERNAL_TAR_SIZE" "${agentTarSize}" "${targetFile}"
+  editScriptFileParam "readonly ARTIFACTS_SIZE" "${artifactsSize}" "${targetFile}"
+}
+
+getStorageSize() {
+  local dirName="${1}"
+  if [ ! -e "${dirName}" ]; then
+    printf '%s' "0"
+    return 0
+  fi
+  printf '%s' "$(du -sb "${dirName}" | awk '{print $1}')"
+}
+
+finishFinally() {
+  #== 将打包脚本文件移动到当前目录下
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    if [ -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" ]; then
+      mv -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+    fi
+  done
+  removeIfExist "${SMARTAGENT_DIR}/uninstall.sh"
+  if [ -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" ]; then
+    mv -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" "${SCRIPT_DIR}/" 2>>"${LOG_FILE}"
+  fi
+
+  if [ -f "${TARGET_INSTALL_TMP_FILE}" ]; then
+    mv -f "${TARGET_INSTALL_TMP_FILE}" "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}" 2>>"${LOG_FILE}"
+  fi
+
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+      removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+    done
+  fi
+
+
+}
+
+removeIfExist() {
+  local filePath="${1}"
+  if [ ! -e "${filePath}" ]; then
+    return
+  fi
+  rm -rf "${filePath}" 2>>"${LOG_FILE}"
+}
+
+deleteParentTmpFile() {
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}"
+  done
+  removeIfExist "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}"
+  removeIfExist "${TARGET_INSTALL_TMP_FILE}"
+
+  for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+  done
+}
+
+#**********************************************************
+# Param
+#**********************************************************
+PARAM_DEBUG=false
+PARAM_RELEASE=false
+#PARAM_VERSION=1.1.0
+PARAM_NAME=euspace
+PARAM_AGENT_PROC=$PARAM_NAME
+PARAM_CONFIG_PATH=
+
+#== 大小写转化,并返回判断结果
+istrcmp() {
+  local s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  local s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+
+  local partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value="$(expr substr "${input}" ${valueSeparator} 1000)"
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 获取bool参数
+readBoolParam() {
+  local value=
+  if value="$(getParamValue "${1}" "${2}")"; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 解析命令行中参数
+parseCommandLineParameters() {
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue RELEASE "${param}"); then
+      PARAM_RELEASE="${value}"
+      printParamMessage "RELEASE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue VERSION "${param}"); then
+      PARAM_VERSION="${value}"
+      printParamMessage "VERSION" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue NAME "${param}"); then
+      PARAM_NAME="${value}"
+      printParamMessage "NAME" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG "${param}"); then
+      PARAM_CONFIG_PATH="${value}"
+      printParamMessage "CONFIG" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue PROC "${param}"); then
+      PARAM_AGENT_PROC="${value}"
+      printParamMessage "PROC" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DEBUG "${param}"); then
+      PARAM_DEBUG="${value}"
+      printParamMessage "DEBUG" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    toConsoleError "Unrecognized parameter: ${param}"
+
+    displayHelp
+    finishPackage "${EXIT_CODE_INVALID_PARAM}"
+  done
+}
+
+displayHelp() {
+  printf '\n'
+  printf '%s\n' "Usage: "${CURR_FILE_NAME}" [-h] [-v] [NAME=pluginName] [PROC=mainProcess] [RELEASE=true|false] [DEBUG=true|false] [VERSION=1.0]"
+  printf '\n\n'
+
+  local pad=15
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+  printf '\n'
+  printf "%-${pad}s%s\n" "RELEASE" "Default true; Whether to delete the publish parameter in the script."
+  printf "%-${pad}s%s\n" "VERSION" "Configure the ${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME} version."
+  printf "%-${pad}s%s\n" "NAME" "Setting agent name like 'bash ./scripts/package.sh NAME=pluginName PROC=mainProcess'"
+  printf "%-${pad}s%s\n" "PROC" "Setting agent main process name."
+  printf "%-${pad}s%s\n" "DEBUG" "Default false; 1、Debug mode executed script ${CURR_FILE_NAME};"
+  printf "%-${pad}s%s\n" "" "2、Remove the debug log from the scripts (${TARGET_INSTALL_FILE_NAME}、${SERVICE_SCRIPT_FILE}、uninstall.sh)."
+}
+
+#== 完成安装后清理临时目录
+finishPackage() {
+  finishFinally
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  exit "${1}"
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+
+  local value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "---> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback} "${signal}"
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    #== shellcheck disable=SC2064
+    trap "signalHandler ${signal} ${callback}" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#**********************************************************
+# main
+#**********************************************************
+main() {
+  mkdir -p "${LOG_DIR}"
+  echo "" >"${LOG_FILE}"
+
+  toConsoleInfo "package script run path: ${SCRIPT_DIR}"
+  toConsoleInfo "package logs path: ${LOG_DIR}"
+
+  chmod +x ${SCRIPT_DIR}/*
+
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  toConsoleInfo "-----params PARAM_RELEASE: ${PARAM_RELEASE}, PARAM_DEBUG: ${PARAM_DEBUG}, PARAM_VERSION: ${PARAM_VERSION}"
+
+  configureSignalHandling "finishFinally"
+
+  #== 设置 package 插件名
+  editScriptFileParam "BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/package.sh"
+  #== 设置 install 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  #== 设置 agentctl 插件名(存在时)
+  if [ -f "${SMARTAGENT_DIR}/package_dir/bin/agentctl" ];then
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  #== 设置 agentctl 配置文件路径(存在时)
+    editScriptFileParam "readonly AGENT_CONFIG_PATH" "\"${PARAM_CONFIG_PATH}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+
+  #== 设置 uninstall 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  #== 设置主进程名
+  if [ -n "${PARAM_AGENT_PROC}" ]; then
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/install_temp.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/uninstall.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+  buildVersion
+  toConsoleInfo "-----Write release version ${AGENT_INSTALLER_VERSION} to ${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME} file."
+  echo "${AGENT_INSTALLER_VERSION}">"${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME}"
+
+  #== 修改uninstall.sh 版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${SCRIPT_DIR}/uninstall.sh"
+
+  deleteParentTmpFile
+
+  #== 压缩 并生成安装脚本
+  tarSmartAgent
+}
+
+main "$@"

+ 1116 - 0
dist/aarch64/scripts/uninstall.sh

@@ -0,0 +1,1116 @@
+#!/bin/bash
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+#== 根路径
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="cloudwise"
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== smartagentd
+readonly AGENT_PROC="euspace"
+readonly AGENT_INSTALLER_VERSION=1.0.0
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly CURRENT_VERSION_LINK=${AGENT_BASE_DIR}/current
+
+#== /opt/cloudwise/omniagent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+
+readonly AGENT_LIBS_DIR="${INSTALL_DIR}/libs"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+
+readonly AGENT_AGENTS_DIR="${INSTALL_DIR}/agents"
+
+readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"
+
+
+
+readonly PERL_FILE="/usr/bin/perl"
+
+readonly SAFE_RM_FILE="safe-rm"
+
+readonly SAFE_RM_BIN="${AGENT_BIN_DIR}/${SAFE_RM_FILE}"
+
+if [ -f ${PERL_FILE} ] && [ -f "${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}" ]; then
+    readonly SAFE_RM_EXEC="${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}"
+else
+    readonly SAFE_RM_EXEC="rm"
+fi
+
+readonly LOG_DIR_NAME=logs
+#== /opt/cloudwise/omniagent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/omniagent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+#readonly INSTALLER_LOG_SUBDIR="installer"
+##== /opt/cloudwise/smartagent/log/installer
+#readonly INSTALLER_LOG_DIR=${LOG_DIR}/${INSTALLER_LOG_SUBDIR}
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+
+
+readonly USER="root"
+readonly GROUP="root"
+#== 非root模式
+readonly PARAM_NON_ROOT_MODE=false
+
+readonly CONTAINER_DEPLOYMENT_STATE_ENTRY=false
+#== 是否有 SELinux
+readonly ARCH_HAS_SELINUX=true
+################################################################################
+#	Logs
+################################################################################
+toLog() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLog "[INFO]" "$@"
+}
+
+toLogWarning() {
+  toLog "[WARN]" "$@"
+}
+
+toLogError() {
+  toLog "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarning() {
+  toConsoleOnly "Warning:" "$@"
+  toLogWarning "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarning "Cannot create ${dir} directory."
+      toLogWarning "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarning "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarning "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+#== 创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+
+################################################################################
+#	Platform characteristics detection
+################################################################################
+
+#== 设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -f "${file}" ]; then
+    chmod "${perms}" "${file}"
+  elif [ -d "${file}" ]; then
+    chmod -R "${perms}" "${file}"
+  fi
+}
+
+#== 安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+detectLinuxDistribution() {
+  if [ -f /etc/redhat-release ]; then
+    cat /etc/redhat-release
+  elif [ -f /etc/os-release ]; then
+    (
+      . /etc/os-release
+      local distrib="${NAME}"
+      if [ -z "${distrib}" ]; then
+        distrib="${ID}"
+      fi
+
+      local version="${VERSION_ID}"
+      if printf "%s" "${distrib}" | grep -iq "debian"; then
+        version="$(cat /etc/debian_version)"
+      fi
+
+      printf "%s %s" "${distrib}" "${version}"
+    )
+  elif [ -f /etc/SuSE-release ]; then
+    head -1 /etc/SuSE-release
+  elif [ -f /etc/lsb-release ]; then
+    (
+      . /etc/lsb-release
+      printf "%s %s" "${DISTRIB_ID}" "${DISTRIB_RELEASE}"
+    )
+  elif ls /etc/*release* >/dev/null 2>&1; then
+    # Generic fallback
+    cat /etc/*release*
+  else
+    printf "AIX %s" "$(oslevel -s 2>&1)"
+  fi
+}
+
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarning "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+setLocationOfInitScripts() {
+  toLogInfo "Determining location of init scripts..."
+
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSTEMD}" ] || [ "${ARCH_ARCH}" = "AIX" ]; then
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of init scripts ${INIT_DIR}"
+  return 0
+}
+
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+################################################################################
+#	Misc functions
+################################################################################
+
+getAgentCtlBinPath() {
+  printf "%s" "${AGENT_TOOLS_DIR}/lib64/${AGENT_CTL_BIN}"
+}
+
+getOsConfigBinPath() {
+  printf "%s" "${AGENT_INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+commandErrorWrapper() {
+  set +e
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarning "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  ${SAFE_RM_EXEC} -f "${errorFile}"
+  set -e
+  return ${returnCode}
+}
+
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)" != "0" ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+removeIfExists() {
+  local pathToRemove="${1}"
+  toLogInfo "${SAFE_RM_EXEC} -rf ${pathToRemove}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(${SAFE_RM_EXEC} -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarning "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+################################################################################
+#	SELinux related functions
+################################################################################
+
+executeUsingOsConfigBin() {
+  local command="${1}"
+  local unit="${2}"
+  if [ "${unit}" ]; then
+    command="${command}-${unit}"
+    unit=""
+  fi
+
+  local output=
+  output="$("$(getOsConfigBinPath)" "${command}" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Executed $(getOsConfigBinPath) ${command} ${unit}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  #shellcheck disable=SC2086
+  output="$("${command}" ${parameters} 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+signalHandler() {
+	end_exit_code=$?
+	if [ ${end_exit_code} -eq ${EXIT_CODE_SIGNAL_RECEIVED} ]; then
+		return
+	fi
+	local signal="${1}"
+	local callback="${2}"
+	if [ ${end_exit_code} -ne 0 ]; then
+		toLogWarning "Process Received [Signal: ${signal}] [Code : ${end_exit_code}]"
+	fi
+	${callback} ${end_exit_code}
+
+	if [ ${end_exit_code} -eq ${EXIT_CODE_OK} ]; then
+		exit ${EXIT_CODE_OK}
+	fi
+
+	exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM EXIT; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(Api-Token=\)[[:alnum:]_-]\{21\}#\1***#' |
+    sed 's#\(TENANT_TOKEN=\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(latest/\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(PROXY=\)[^[:space:]]*#\1***#'
+}
+
+#== waitTime must be divisible by 10
+sendSignalToProcessAndWaitForStop() {
+  local pidCheckingFunction="${1}"
+  local signal="${2}"
+  local action="${3}"
+  local waitTime="${4}"
+  local pidToStop="${5}"
+
+  if ! ${pidCheckingFunction} "${pidToStop}"; then
+    toLogInfo "Process with pid ${pidToStop} doesn't exist"
+    return
+  fi
+
+  toConsoleInfo "Waiting ${waitTime} seconds for process with pid ${pidToStop} to ${action}."
+  while [ "${waitTime}" -gt 0 ]; do
+    if [ "$((waitTime % 10))" -eq 0 ]; then
+      toLogInfo "Sending signal: ${signal} to ${pidToStop}"
+      kill -s "${signal}" "${pidToStop}" 2>>"${LOG_FILE}"
+    fi
+
+    if ! ${pidCheckingFunction} "${pidToStop}"; then
+      return 0
+    fi
+    sleep 1
+    waitTime=$((waitTime - 1))
+  done
+  return 1
+}
+
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    ${SAFE_RM_EXEC} -f "${errorFile}"
+    return 1
+  fi
+
+  ${SAFE_RM_EXEC} -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+systemLibDirSanityCheck() {
+  local dir="${1}"
+  if [ ! -d "${dir}" ]; then
+    toLogWarning "Directory: ${dir} does not exist"
+    printf ""
+    return
+  fi
+
+  if ! testWriteAccessToDir "${dir}"; then
+    toLogWarning "Detected that ${dir} is not writable"
+    dir="${PA_FALLBACK_INSTALLATION_DIR}${dir}"
+    createDirIfNotExistAndSetRights "${dir}" 755
+  fi
+
+  printf "%s" "${dir}"
+}
+
+isNonRootModeEnabled() {
+  printf '%s' "${PARAM_NON_ROOT_MODE}" | grep -qE "(true|no_ambient)"
+}
+
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  if isNonRootModeEnabled && printf '%s' "${prefix}" | grep -q "chkconfig"; then
+    local action="chkconfig-add"
+    if printf '%s' "${prefix}" | grep -q -- "--del"; then
+      action="chkconfig-del"
+    fi
+    toLogInfo "Using ${AGENT_OS_CONFIG_BIN} ${action}-${file} to modify autostart"
+    output="$("$(getOsConfigBinPath)" "${action}-${file}" 2>&1)"
+  else
+    local command="${prefix}${file} ${suffix}"
+    toLogInfo "Executing ${command}"
+    output="$(${command} 2>&1)"
+  fi
+  local status=$?
+
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    local parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarning "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}" = "AIX" ]; then
+      PATH="${PATH}:/opt/freeware/bin"
+    fi
+
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+getValueFromConfigFile() {
+  local key="${1}"
+  local separator="${2}"
+  local configFile="${3}"
+  local defaultValue="${4}"
+
+  local value="$(sed -n "s|${key}${separator}||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+isAnotherInstallerRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+
+  local pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  local nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarning "Installer lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarning "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #shellcheck disable=SC2009
+  local foundProcesses="$(ps -e -o "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIRECTORY}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installer process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 0
+}
+
+createInstallerLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+removeInstallerLockFile() {
+  toLogInfo "Removing installer lock file."
+  ${SAFE_RM_EXEC} -f "${INSTALLER_LOCK_FILE}"
+}
+
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+mapPidsToName() {
+  local pids="${1}"
+  local output
+  for pid in ${pids}; do
+    local name="$(grep 'Name:' "/proc/${pid}/status" 2>/dev/null | awk '{print $2}')"
+    output="${output}, ${pid} (${name})"
+  done
+
+  printf '%s' "${output}" | cut -c 3-
+}
+
+readonly ARCH_ARCH="X86"
+readonly ARCH_VERSIONED_LIB_DIR_PREFIX="linux-x86"
+
+arch_checkArchitectureCompatibility() {
+  local arch="$(detectArchitecture)"
+  if [ "${arch}" = "X86_64" ] || [ "${arch}" = "IA64" ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}" = "X86_64" ]
+}
+
+arch_local_getLibraryPathFromLdd() {
+  local binary="${1}"
+  ldd "${binary}" 2>/dev/null | grep libc.so | awk '{ print $3 }'
+}
+
+arch_local_detectProcessAgentInstallerDirectories() {
+  local useLddOutput="false"
+
+  local systemLib32Prefix
+  systemLib32Prefix="$(arch_local_getSystemLibraryPath 32)"
+  local exitCode=$?
+
+  if [ ! "${systemLib32Prefix}" ]; then
+    if [ "${exitCode}" -eq 0 ]; then
+      toLogWarning "This is a 64-bit platform with 32-bit libraries installed, but ${AGENT_INSTALL_ACTION_BIN} failed to determine their location"
+      useLddOutput="true"
+    else
+      toLogInfo "This is a 64-bit platform and 32-bit libraries were not detected"
+    fi
+  else
+    systemLib32Prefix="$(systemLibDirSanityCheck "/${systemLib32Prefix}")"
+    if [ ! "${systemLib32Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  local systemLib64Prefix="$(arch_local_getSystemLibraryPath 64)"
+  if [ ! "${systemLib64Prefix}" ]; then
+    toLogWarning "This is a 64-bit platform, but ${AGENT_INSTALL_ACTION_BIN} failed to determine location of 64-bit libraries"
+    useLddOutput="true"
+  else
+    systemLib64Prefix="$(systemLibDirSanityCheck "/${systemLib64Prefix}")"
+    if [ ! "${systemLib64Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  if [ "${useLddOutput}" = "true" ]; then
+    arch_local_detectProcessAgentDirectoriesBasedOnLdd
+  else
+    readonly SYSTEM_LIB32="${systemLib32Prefix}"
+    readonly SYSTEM_LIB64="${systemLib64Prefix}"
+  fi
+} 2>>"${LOG_FILE}"
+
+arch_detectProcessAgentInstallerDirectories() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x = "true"x ]; then
+    readonly SYSTEM_LIB32="${PA_FALLBACK_INSTALLATION_DIR}/lib32"
+    readonly SYSTEM_LIB64="${PA_FALLBACK_INSTALLATION_DIR}/lib64"
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB32}" 755
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB64}" 755
+    return 0
+  fi
+
+  arch_local_detectProcessAgentInstallerDirectories
+}
+
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #shellcheck disable=SC2016
+    libMacro='/$LIB'
+  fi
+  printf "%s" "${libMacro}"
+}
+
+arch_checkGlibc() {
+  local glibcVersion="$(ldd --version | awk 'NR==1{ print $NF }')"
+
+  toLogInfo "Detected glibc version: ${glibcVersion}"
+
+  if [ "$(format_version "${glibcVersion}")" -gt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    return
+  elif [ "$(format_version "${glibcVersion}")" -lt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    toConsoleError "We can't continue setup. The glibc version: ${glibcVersion} detected on your system isn't supported."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}."
+    toConsoleError "Stopping installer process..."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  if ! isAvailable rpm; then
+    toLogInfo "RPM not detected - skipping glibc patch version check."
+    return
+  fi
+
+  local glibcFullVersion="$(arch_runCommandWithTimeout 60 "rpm" "-q" "glibc" | tail -n 1)"
+  if [ ! "${glibcFullVersion}" ]; then
+    toConsoleError "Could not determine exact glibc version using RPM."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  # fix for RedHat 5.x with unsufficient glibc 2.5 patch version
+  local glibcPatchVersion="$(format_patch "${glibcFullVersion}")"
+  if [ "$(format_version "${glibcVersion}")" -eq "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ] && [ "${glibcPatchVersion}" -lt "${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}" ]; then
+    toConsoleError "glibc patch version: ${glibcPatchVersion} detected on your system is not supported, setup won't continue."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}-${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+}
+
+# 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+arch_runCommandWithTimeout() {
+  local commandTimeout="${1}"
+  shift
+  local resultFile="/tmp/smartagent_commandResult_$$"
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local loopErrorsFile="/tmp/smartagent_loopErrors_$$"
+
+  toLogInfo "Executing $* with timeout ${commandTimeout} seconds"
+  (
+    "$@" >${resultFile} 2>${errorFile} &
+    child=$!
+    while [ "${commandTimeout}" -gt 0 ]; do
+      toLogInfo "Time left: ${commandTimeout} s,  pid: ${child}"
+      sleep 1
+      kill -0 "${child}" 2>/dev/null || exit 0
+      commandTimeout=$((commandTimeout - 1))
+    done
+
+    # Be nice, post SIGTERM first.
+    # The 'exit 0' below will be executed if any preceeding command fails.
+    toLogInfo "Killing ${child} with SIGTERM"
+    kill -s 15 "${child}" && kill -0 "${child}" || exit 0
+    sleep 1
+    toLogWarning "Killing ${child} with SIGKILL"
+    kill -s 9 "${child}"
+  ) 2>"${loopErrorsFile}"
+
+  local errorOutput="$(cat "${errorFile}")"
+  local commandOutput="$(cat "${resultFile}")"
+  local loopErrors="$(cat "${loopErrorsFile}")"
+  ${SAFE_RM_EXEC} -f "${errorFile}" "${resultFile}" "${loopErrorsFile}"
+
+  if [ -n "${errorOutput}" ]; then
+    toLogInfo "Failed to execute command, error output: ${errorOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf ""
+    return 1
+  else
+    toLogInfo "Command executed successfully, output: ${commandOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf '%s' "${commandOutput}"
+    return 0
+  fi
+}
+
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+arch_preloadTest() {
+  if [ "${SYSTEM_LIB32}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "32"
+    local returnCode=$?
+    if [ ${returnCode} -ne 0 ]; then
+      return ${returnCode}
+    fi
+  fi
+
+  if [ "${SYSTEM_LIB64}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "64"
+  fi
+}
+
+arch_checkEnvironmentConfiguration() {
+  if ! stat --format='%t,%T' /dev/null | grep -q "1,3"; then
+    toLogInfo "$(stat /dev/null)"
+    toConsoleError "Installer detected corruption of '/dev/null': Not a character device"
+    return "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  return 0
+}
+
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+initLog() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    createLogDirsIfMissing
+  fi
+
+  toLogInfo "Uninstaller started."
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+  logBasicStartupInformation "${@}"
+}
+
+finishUninstaller() {
+  removeInstallerLockFile
+  if [ -z "$(ls -A ${INSTALL_DIR})" ];then
+    ${SAFE_RM_EXEC} -df ${INSTALL_DIR}
+    ${SAFE_RM_EXEC} -f ${CURRENT_VERSION_LINK}
+  fi
+  if [ $1 -eq 0 ]; then
+    toConsoleInfo "Uninstaller finished successfully"
+  else
+    toConsoleInfo "Uninstaller finished failed"
+  fi
+  exit "${EXIT_CODE_OK}"
+}
+
+setLogFilePath() {
+    #== - 反安装日志: uninstallation-{timestamp}.log 如: uninstallation-20220601110912.log
+    readonly LOG_FILE="${INSTALLER_LOG_DIR}/uninstallation-"$(date -u +"%Y%m%d%H%M%S")".log"
+}
+
+removeScriptsFromAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+
+  if ! runAutostartAddingTool "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"; then
+    toConsoleError "Failed to remove ${SERVICE_SCRIPT_FILE} from autostart. For details, see ${LOG_FILE}"
+  fi
+}
+
+removeUnitFromSystemd() {
+  local unit=${1}
+  if [ ! -e "${SYSTEMD_UNIT_FILES_DIR}/${unit}" ]; then
+    toLogInfo "${unit} does not exist and will not be removed from ${INIT_SYSTEM_SYSTEMD} autostart"
+    return
+  fi
+
+  executeSystemctlCommand disable "${unit}"
+  ${SAFE_RM_EXEC} -f "${SYSTEMD_UNIT_FILES_DIR}/${unit}"
+}
+
+removeSystemvAutostart() {
+  toConsoleInfo "Removing ${BRAND_AGENT_PRODUCT_NAME} from autostart"
+
+  #Order of checking is important
+  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/bin/update-rc.d -f " "remove"
+  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/sbin/update-rc.d -f " "remove"
+  elif [ -x /sbin/chkconfig ]; then #RedHat
+    removeScriptsFromAutostart "/sbin/chkconfig --del "
+  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+    removeScriptsFromAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+  elif [ "${ARCH_ARCH}" = "AIX" ]; then
+    #== 不存在
+    arch_removeAutostart
+  else
+    toConsoleError "Couldn't remove ${BRAND_AGENT_PRODUCT_NAME} from autostart."
+  fi
+}
+
+removeSystemdAutostart() {
+  removeUnitFromSystemd "${SYSTEMD_UNIT_FILE_AGENT}"
+  executeSystemctlCommand daemon-reload ""
+}
+
+stopAgentService() {
+  if [ -s "${AGENT_PID_FILE}" ]; then
+    toLogInfo "Stop ${AGENT_PROC}..."
+    commandErrorWrapper ${AGENT_BIN_DIR}/agentctl stop
+  fi
+}
+
+removeSystemvInitScripts() {
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${SERVICE_SCRIPT_FILE} from ${INIT_DIR}"
+  ${SAFE_RM_EXEC} -f "${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+}
+
+removeAutostartScripts() {
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSV}" ]; then
+    removeSystemvAutostart
+    removeSystemvInitScripts
+  else
+    removeSystemdAutostart
+  fi
+}
+
+createUninstallInfoFile() {
+  local reason
+  if [ "${IS_UPGRADING}"x = "true"x ]; then
+    reason="upgrade"
+  else
+    reason="uninstall"
+  fi
+
+  toLogInfo "Creating uninstall.info file: ${reason}"
+}
+
+initializeUninstaller() {
+  local installerPid=
+  local skipRootPrivilegesCheck=
+
+  if [ $# -eq 1 ]; then
+    installerPid="${1}"
+  fi
+  if [ $# -eq 2 ]; then
+     installerPid="${1}"
+     skipRootPrivilegesCheck="${2}"
+  fi
+
+  setPATH
+
+  #setLogFilePath "${installerPid}"
+  setLogFilePath
+
+  if [ "${installerPid}" ] && [ "${installerPid}"x != "online"x ]; then
+    IS_UPGRADING="true"
+  else
+    IS_UPGRADING="false"
+  fi
+  toConsoleInfo "IS_UPGRADING :${IS_UPGRADING}"
+
+  if [ "${skipRootPrivilegesCheck}"x = "true"x ] && [ "${IS_UPGRADING}"x = "true"x ]; then
+    toConsoleInfo "Skipping root privileges check"
+  else
+    if ! checkRootAccess; then
+      toConsoleError "You must run uninstaller with root privileges"
+      exit "${EXIT_CODE_GENERIC_ERROR}"
+    fi
+  fi
+
+  initLog "${@}"
+
+  toLogInfo "Launched during upgrade: ${IS_UPGRADING}"
+  createUninstallInfoFile
+
+  configureSignalHandling "finishUninstaller"
+
+  if [ "${IS_UPGRADING}"x = "false"x ] && [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    if isAnotherInstallerRunning; then
+      exit "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}"
+    fi
+    createInstallerLockFile
+  fi
+
+  if [ ! -f ${PERL_FILE} ]; then
+    toConsoleInfo "/usr/bin/perl file not found so ${SAFE_RM_EXEC} is used."
+  fi
+
+  toConsoleInfo "Using ${SAFE_RM_EXEC} to uninstall"
+}
+
+getVolumeStorageAgentInstallDir() {
+  readLink "$(dirname "${0}")"
+}
+
+cleanUpSELinux() {
+  if [ ! "${ARCH_HAS_SELINUX}" ] || ! isAvailable semodule; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${BRAND_FORMAL_NAME} SELinux policies, this may take a while..."
+}
+
+cleanUpFilesDuringUninstall() {
+	toLogInfo "This is a full uninstaller, cleaning up rest of the files"
+
+  local fileNames="${AGENT_CONF_DIR} ${AGENT_BIN_DIR} ${LOG_DIR} ${AGENT_SCRIPTS_DIR} ${AGENT_LIBS_DIR} ${AGENT_AGENTS_DIR} ${AGENT_RUNTIME_DIR}"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${fileName}"
+  done
+
+  fileNames="uninstall.sh installer.version heartbeat"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${INSTALL_DIR}/${fileName}"
+  done
+
+}
+
+handleRegularUninstaller() {
+
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  if ! setLocationOfInitScripts; then
+    toLogWarning "Cannot determine location of init scripts."
+  fi
+
+  stopAgentService
+
+  cleanUpFilesDuringUninstall
+
+}
+
+main() {
+  initializeUninstaller "$@"
+  handleRegularUninstaller "$@"
+#  finishUninstaller
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+main "$@"

二进制
dist/aarch64/scripts/xzdec


二进制
dist/package_dir/agents/NativeAgent/plugins/cloudwise-javacode-plugin-ssl-socket.jar


+ 6 - 0
dist/x86_64/.gitignore

@@ -0,0 +1,6 @@
+.idea
+installer.version
+Cloudwise-euspace-installer*.sh
+logs/*
+package_dir/logs/*.log
+package_dir/runtime/memdump

+ 87 - 0
dist/x86_64/README.md

@@ -0,0 +1,87 @@
+
+## package.sh 介绍
+
+[scripts/package.sh](../../scripts/package.sh) 主要是将 SmartAgent 安装目录下所有内容(脚本无自动去除无用目录或内容功能)压缩,
+通过`base64`编码后写入 [scripts/install_temp.sh](../../scripts/install_temp.sh) 中,
+最终生成安装文件 `cwserveragent-installer-version.sh` (version为SmartSgent版本)。
+
+### 安装帮助
+
+执行命令 `sh scripts/package.sh -h` 即可查看安装帮助详情。详情如下:
+
+```
+Usage: package.sh [-h] [-v] [DEBUG=true|false] [AGENT_VERSION=8.6]
+
+
+-h, --help     Display this help and exit.
+-v, --version  Print version and exit.
+
+RELEASE        Default true; Whether to delete the publish parameter in the script.
+VERSION        Configure the Cloudwise SmartAgent version.
+DEBUG          Default false; 1、Debug mode executed script package.sh;
+               2、Remove the debug log from the scripts (Cloudwise-SmartAgent-Linux-1.2.0.sh、smartagent、uninstall.sh).
+
+```
+
+### smartagent目录结构
+
+打包前目录结构(请保持目录及内容最简)
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+打包后目录结构
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── installer.version
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+安装后目录结构
+
+```
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   ├── cwserveragent.conf
+│   ├── installation.conf
+│   └── server-agent.ini
+├── installer.version
+├── logs
+│   └── serveragent
+│       ├── CW-ServerAgent.error.log
+│       ├── CW-ServerAgent.fatal.log
+│       └── CW-ServerAgent.panic.log
+├── runtime
+│   └── .pid
+├── scripts
+│   ├── cw-serveragent
+│   └── uninstall.sh
+└── uninstall.sh
+```

二进制
dist/x86_64/package_dir/agents/NativeAgent/lib/apmAgent.jar


二进制
dist/x86_64/package_dir/agents/NativeAgent/lib/apmCore.jar


二进制
dist/x86_64/package_dir/agents/NativeAgent/lib/apmSpy.jar


二进制
dist/x86_64/package_dir/agents/NativeAgent/lib/libnativeAgent.so


二进制
dist/x86_64/package_dir/agents/NativeAgent/plugins/cloudwise-javacode-plugin-ssl-socket.jar


+ 244 - 0
dist/x86_64/package_dir/bin/agentctl

@@ -0,0 +1,244 @@
+#!/bin/bash
+
+#== 脚本执行中遇到不存在的变量就报错
+set -o nounset
+#== 脚本执行中报错即刻退出
+set -o errexit
+#== 脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/current
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+readonly AGENT_LOG_DIR="${INSTALL_DIR}/logs"
+
+#readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#readonly MAIN_CONF_FILE="${AGENT_CONF_DIR}/${AGENT_MAIN_CONF}"
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+readonly AGENT_PROC="euspace"
+readonly AGENT_CONFIG_PATH=""
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+getDaemonPid(){
+    local agent="${1}"
+    #== 约定pid文件
+    local pidFilePath="${AGENT_PID_FILE}"
+    #== 二进制文件绝对路径
+    # shellcheck disable=SC2155
+    local procPath="$(readlink -m "${AGENT_BIN_DIR}/${agent}" 2>&1)"
+    local cmdline
+    local pid
+    #== 查询pid文件
+    if [ -s "${pidFilePath}" ] ; then
+      pid="$(cat "${pidFilePath}" 2>&1)"
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if [ -d "/proc/${pid}" ] && ( [[ ${cmdline} =~ ${procPath} ]] || [[ ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]] ); then
+#        toLogInfo "[${agent}] Find pid by ${pidFilePath} [content:${pid}][cmdline:${cmdline}]"
+          printf '%s' ${pid}
+          return 0
+      else
+        #== 进程绝对路径查询
+#        toLogWarning "[${agent}] Pidfile exist [content:${pid}]. But process not find or not myself. [cmdline:${cmdline}]"
+        if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#          toLogWarning "[${agent}] Not find process grep '${procPath}'"
+          return 1
+        fi
+        cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+        if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#            toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+            return 1
+        fi
+#        toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]"
+      fi
+    else
+      #== 进程绝对路径查询
+#      toLogWarning "[${agent}] Pidfile [${pidFilePath}] not find. Use abs path queries."
+      if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#        toLogWarning "[${agent}] Not find process by grep '${procPath}'"
+        return 1
+      fi
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#          toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+          return 1
+      fi
+#      toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]."
+    fi
+
+    printf '%s' ${pid}
+    return 0
+}
+
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+#  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E "${includeRegex}" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(ps -e -o "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+#    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+getAgentPid() {
+  local DAEMONPID="$(getDaemonPid "${AGENT_PROC}")"
+  if [ -z ${DAEMONPID} ]; then
+      return 1
+  fi
+  printf '%s' "${DAEMONPID}"
+
+}
+
+startAgent() {
+  local pid
+  if [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ]; then
+    export DISABLE_E2E_TRACING=false
+    export DISABLE_STACK_TRACING=true
+    export RUN_IN_OMNIAGENT=true
+#    export LOG_LEVEL=debug
+#    export SEND_NET_DATA=true
+#    export ENABLE_ES=true
+    export SEND=1
+    local params="--listen=0.0.0.0:8123"
+#    if [ -f "/etc/chaosd/pki/ca.crt" ] && [ -f "/etc/chaosd/pki/chaosd.crt" ] && [ -f "/etc/chaosd/pki/chaosd.key" ]; then
+#        params="server  --https-port 31768 --CA=/etc/chaosd/pki/ca.crt --cert=/etc/chaosd/pki/chaosd.crt --key=/etc/chaosd/pki/chaosd.key"
+#    fi
+    pid=$(nohup ${AGENT_BIN_DIR}/${AGENT_PROC} ${params}>>"${AGENT_LOG_DIR}/service.log" 2>&1 & echo $!)
+    echo ${pid} > "${AGENT_PID_FILE}"
+  else
+    echo "${AGENT_BIN_DIR}/${AGENT_PROC} not exist."
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+}
+
+agentStart() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} is running. Agent pid: ${agentPid}."
+    return
+  fi
+
+  startAgent
+	local exitCode=$?
+	if [ "${exitCode}" -ne 0 ]; then
+		echo "Failed to start ${BRAND_AGENT_PRODUCT_NAME} service."
+		exit "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} service started. Agent pid: ${agentPid}."
+  else
+    echo "${BRAND_AGENT_PRODUCT_NAME} not start!"
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+agentStop() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+#    ${AGENT_BIN_DIR}/stop.sh ${agentPid}
+    kill ${agentPid}
+    echo "${BRAND_AGENT_PRODUCT_NAME} service stopped success."
+    cat /dev/null > "${AGENT_PID_FILE}"
+  else
+      echo "${BRAND_AGENT_PRODUCT_NAME} is stopped. service stopped success."
+  fi
+}
+
+agentRestart(){
+  agentStop
+  sleep 1
+  agentStart
+}
+
+agentStatus() {
+  local agentPid
+	local statusOutput
+  if agentPid="$(getAgentPid)"; then
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is running. ${BRAND_AGENT_PRODUCT_NAME} pid: ${agentPid}"
+  else
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is not running. ${BRAND_AGENT_PRODUCT_NAME} process not found or stop."
+  fi
+
+  echo "${statusOutput}"
+}
+
+info() {
+  local agentPid="$(getAgentPid)"
+  local version=$(cat "${INSTALL_DIR}/installer.version" | tr -d '\n')
+  printf '{"pid":%d,"version":"%s","agent_id":"%s","config":"%s","pipe": true}' "${agentPid}" "${version}" "${BRAND_PRODUCT_NAME_LOWER}" "${AGENT_CONFIG_PATH}"
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+
+main() {
+  case "$1" in
+  start)
+    agentStart
+    ;;
+  stop)
+    agentStop
+    ;;
+  restart)
+    agentRestart
+    ;;
+  status)
+    agentStatus
+    ;;
+  info)
+    info
+    ;;
+  *)
+    toConsoleInfo "usage: $0 {start|stop|restart|status}"
+    ;;
+  esac
+
+  exit "${EXIT_CODE_OK}"
+}
+
+main "$@"

+ 0 - 0
dist/x86_64/package_dir/conf/.gitkeep


二进制
dist/x86_64/package_dir/libs/amd64/jvm/cwlibnet.so


二进制
dist/x86_64/package_dir/libs/arm64/jvm/cwlibnet.so


+ 0 - 0
dist/x86_64/package_dir/logs/.gitkeep


+ 0 - 0
dist/x86_64/package_dir/runtime/.gitkeep


二进制
dist/x86_64/package_dir/scripts/cwjattach


+ 2954 - 0
dist/x86_64/scripts/install_temp.sh

@@ -0,0 +1,2954 @@
+#!/bin/bash
+#== AIX(Advanced Interactive eXecutive)是IBM基于AT&T Unix System V开发的一套类UNIX操作系统,运行在IBM专有的Power系列芯片设计的小型机硬件系统之上
+#== 以【#== 】开头的注释会在打包时删除
+#DEBUG== 以【#DEBUG== 】开头的注释会在调试打包时保留,正式打包时删除
+
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE="cloudwise"
+
+readonly AIX_DF_SPECIFIC_FLAG=
+readonly CONF_LD_PRELOAD=
+
+PARAM_DISABLE_SYSTEM_LOGS_ACCESS=
+
+#== 安装包版本项
+readonly AGENT_BUILD_DATE=25.09.2020
+readonly AGENT_INSTALLER_VERSION=1.2.0
+readonly AGENT_BUILD_DATE_INFO=""
+readonly AGENT_BUILD_TAG=""
+readonly AGENT_BUILD_UPLOADER=""
+#== 安装包版本项
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== CW-ServerAgent
+readonly AGENT_PROC="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/oneagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/oneagent/agents/chaosd
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/oneagent/agents/chaosd
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+#== /opt/cloudwise/cwserveragent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/runtime
+readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/lib
+#== readonly AGENT_LIB_DIR="${INSTALL_DIR}/lib"
+
+
+readonly AGENTS_BASE_DIR="${INSTALL_DIR}/agents"
+
+readonly META_BASE_DIR="${INSTALL_DIR}/meta"
+
+readonly LOG_DIR_NAME="logs"
+#== /opt/cloudwise/cwserveragent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/cwserveragent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+
+#== server-agent.ini
+#== hostId文件
+readonly INSTALLER_CONF_FILE_NAME="installation.conf"
+#== 部署在容器内的状态信息
+readonly INSTALLER_CONF_FILE="${AGENT_CONF_DIR}/${INSTALLER_CONF_FILE_NAME}"
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+
+readonly EXTERNAL_TAR_SIZE=233820160
+readonly ARTIFACTS_SIZE=953271934
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#== 【0】【3】【4】=创建不存在的目录,并设置权限
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  toConsoleInfo "#DEBUG== createDirIfNotExistAndSetRights --- ${rights} --- ${dir}"
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarn "Cannot create ${dir} directory."
+      toLogWarn "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarn "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarn "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+createCurrentVersionSymlink() {
+	toLogInfo "Creating symlink to current version..."
+
+	local currentVersionLink="${AGENT_BASE_DIR}/current"
+	local tempLink="${currentVersionLink}.tmp"
+
+	if ! commandErrorWrapper ln -s "${AGENT_INSTALLER_VERSION}" "${tempLink}"; then
+		toLogError "Failed to create current version link"
+		return
+	fi
+
+	if ! commandErrorWrapper arch_moveReplaceTarget "${tempLink}" "${currentVersionLink}"; then
+		toLogError "Failed to set up current version link"
+		commandErrorWrapper rm -f "${tempLink}"
+		return
+	fi
+
+	toLogInfo "Current version link created: ${currentVersionLink} -> ${AGENT_INSTALLER_VERSION}"
+}
+
+#== 【0】=创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  #== agents根目录
+  createDirIfNotExistAndSetRights "${AGENT_BASE_DIR}" 1775
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+#  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+#**********************************************************
+# Platform characteristics detection
+#**********************************************************
+
+#== 【5】=获取系统发型版本
+getOsReleasePath() {
+	local osReleasePath="/etc/os-release"
+	if [ ! -f "${osReleasePath}" ]; then
+		osReleasePath="/usr/lib/os-release"
+	fi
+
+	printf '%s' "${osReleasePath}"
+}
+
+parseOsReleaseFile() {
+	local osReleasePath="$(getOsReleasePath)"
+
+	#shellcheck disable=SC1090
+	. "${osReleasePath}"
+	local distrib="${NAME-}"
+	if [ -z "${distrib}" ]; then
+		distrib="${ID-}"
+	fi
+
+	local version="${VERSION_ID-}"
+	if printf '%s' "${distrib}" | grep -iq "debian"; then
+		version="$(cat /etc/debian_version)"
+	elif printf '%s' "${distrib}" | grep -iq "fedora" && [ "${VARIANT_ID-}" = "coreos" ]; then
+		distrib="${distrib} CoreOS"
+	fi
+
+	printf '%s %s' "${distrib}" "${version}"
+}
+
+#== 【5】=获取系统发型版本
+detectLinuxDistribution() {
+	if [ -f /etc/oracle-release ]; then
+		cat /etc/oracle-release
+	elif [ -f /etc/fedora-release ]; then
+		if [ -f "$(getOsReleasePath)" ]; then
+			(
+				parseOsReleaseFile
+			)
+		else
+			cat /etc/fedora-release
+		fi
+	elif [ -f /etc/redhat-release ]; then
+		cat /etc/redhat-release
+	elif [ -f "$(getOsReleasePath)" ]; then
+		(
+			parseOsReleaseFile
+		)
+	elif [ -f /etc/SuSE-release ]; then
+		head -1 /etc/SuSE-release
+	elif [ -f /etc/lsb-release ]; then
+		(
+			. /etc/lsb-release
+			printf "%s %s" "${DISTRIB_ID-}" "${DISTRIB_RELEASE-}"
+		)
+	elif ls /etc/*release* >/dev/null 2>&1; then
+		# Generic fallback
+		cat /etc/*release*
+	else
+		printf "AIX %s" "$(oslevel -s 2>&1)"
+	fi
+}
+
+#== 【3】=检查系统init (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarn "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+#== 【3】=设置系统初始化脚本目录
+setLocationOfScripts() {
+  toLogInfo "Determining location of scripts..."
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSTEMD}"x ] || [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== /opt/cloudwise/cwserveragent/scripts
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of scripts ${INIT_DIR}"
+  return 0
+}
+
+#== 【0】=检查系统结构(X86_64\IA64\X86)
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    #== arch指令主要用于显示当前主机的硬件结构类型,查询结果与uname一致,我们可以看到它输出的结果有:i386、i486、mips、alpha等
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+#**********************************************************
+# Misc functions
+#**********************************************************
+
+#== 【1】【4】【5】【6】=获取 agent (64\32)位数lib目录(""或lib64)
+getBinariesFolderByBitness() {
+  local bitness="${1}"
+  if [ "${bitness}" -eq 32 ]; then
+    bitness=""
+  fi
+  printf 'lib%s' "${bitness}"
+}
+
+#== 【1】【2】【5】【6】=获取 agent tools/lib64/smartagentctl 路径
+#== getAgentCtlBinPath
+
+#== 【1】【4】【5】【6】=获取 agent 64位数lib路径(lib64/installaction)
+#== getAgentInstallActionPath
+
+#== 【5】【6】=获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+getOsConfigBinPath() {
+  printf "%s" "${INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+#== 【5】=设置agent进程可用
+setProcessAgentEnabled() {
+  local enabled="${1}"
+  toLogInfo "Setting process agent enabled: ${enabled}..."
+  local changeStatus=
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --set-process-agent-enabled
+  changeStatus=$("$(getAgentInstallActionPath)" --set-process-agent-enabled "${enabled}" 2>&1)
+  toLogAdaptive $? "Process agent enable(${enabled}) status: ${changeStatus}"
+}
+
+#== 【1】【4】【5】【6】=获取命令执行错误信息,并写日志
+commandErrorWrapper() {
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarn "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  rm -f "${errorFile}"
+
+  return ${returnCode}
+}
+
+#== 【3】=是否独立的namespace
+isNamespaceIsolated() {
+  local pid="${1}"
+  local namespace="${2}"
+  local initNamespaceId
+  local processNamespaceId
+  initNamespaceId="$(readlink "/proc/1/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+  processNamespaceId="$(readlink "/proc/${pid}/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+
+  if [ ! "${initNamespaceId}" ] || [ ! "${processNamespaceId}" ]; then
+    toLogInfo "Link to /proc/*/ns/${namespace} does not exist"
+    printf 'error'
+    return
+  fi
+
+  if [ "${initNamespaceId}"x != "${processNamespaceId}"x ]; then
+    printf 'true'
+  else
+    printf 'false'
+  fi
+}
+
+#== 【0】=检查是否root
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)"x != "0"x ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+#== 【3】=删除存在的路径
+removeIfExists() {
+  local pathToRemove="${1}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(rm -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarn "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+
+#== 【5】【6】=执行 systemctl 命令
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    #== 执行使用 os config bin
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  #== 执行 systemctl 命令
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+#== 【6】=运行初始化命令(通过 service 方式 或 直接运行可执行命令)
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  output="$("${command}" "${parameters}" 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+#== 【0】=信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback}
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 【0】=配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#== 【0】删除权限
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(LICENSE=\)[[:alnum:]]\{16\}#\1***#'
+}
+
+#== 【3】【5】=检查目录写权限
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #== shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    rm -f "${errorFile}"
+    return 1
+  fi
+
+  rm -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+#== 【0】=设置PATH
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+#== 【5】【6】=是否非root权限
+isNonRootModeEnabled() {
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-drop-root-privileges
+  local output
+  output="$(getValueFromConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "${LEGACY_AGENT_CONF_FILE}" "${PARAM_NON_ROOT_MODE}")"
+  printf '%s' "${output}" | grep -qE "(true|no_ambient)"
+}
+
+#== 【5】=自动启动工具
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  local command="${prefix} ${file} ${suffix}"
+  toLogInfo "Executing ${command}"
+  output="$(${command} 2>&1)"
+  local status=$?
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+#== 【0】【5】=查找软链接真实地址(通过ls命令)
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput
+  local parsedLinkTarget
+  lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarn "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+#== 【0】【5】=查找软链接真实地址(通过readlink 或 ls 命令)
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      path="${PATH}:/opt/freeware/bin"
+    fi
+
+    #== 通过 readlink 命令查找地址
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+#== 【0】【3】【4】【5】【6】=检查命令是否有效/是否存在
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+#== 【0】【1】【3】【5】=读取配置文件配置参数信息
+getValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local defaultValue="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+#== 【1】【5】=删除配置文件中配置
+removeValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local output
+  if ! output="$(cp -p "${configFile}" "${configFile}.tmp" 2>&1)"; then
+    toLogWarn "Unable to initialize ${configFile}.tmp file using source file, privileges and ownership will not be preserved: ${output}"
+  fi
+
+  if sed "/^${key}/d" "${configFile}" >"${configFile}.tmp"; then
+    mv -f "${configFile}.tmp" "${configFile}"
+  else
+    toLogWarn "Failed to remove ${key} from ${configFile}"
+    rm -f "${configFile}.tmp"
+  fi
+}
+
+#== 写配置到配置文件信息
+writeParamToConfigFile() {
+  local key="${1}"
+  local newValue="${2}"
+  local configFile="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== writeParamToConfigFile --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${configFile}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${configFile}" 2>/dev/null
+  else
+    echo "${key}=${newValue}" >>"${configFile}" 2>/dev/null
+  fi
+}
+
+writeContentToConfigFile() {
+  local newValue="${1}"
+  local configFile="${2}"
+  toConsoleInfo "#DEBUG== writeContentToConfigFile --> edit ${newValue}, configFile: ${configFile}"
+
+  echo "${newValue}" > "${configFile}" 2>/dev/null
+}
+
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== editScriptFileParam --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 【0】=是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+isAnotherInstallationRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+  local pidFromLockFile
+  local nameFromLockFile
+  pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarn "Installation lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarn "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #== shellcheck disable=SC2009
+  #== 获取正在执行安装的进程信息
+  local foundProcesses
+  foundProcesses="$(pgrep -f "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIR}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installation process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 1
+}
+
+#== 【0】=创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+createInstallationLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+#== 【0】【6】删除安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+removeInstallationLockFile() {
+  toLogInfo "Removing installation lock file."
+  rm -f "${INSTALLER_LOCK_FILE}"
+}
+
+#== 【0】基础启动日志信息
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+#== 解压文件编码
+readonly UNPACK_BINARY=base64
+#== 解压文件编码参数
+readonly UNPACK_BINARY_ARGS="-di"
+
+readonly ARCH_ARCH="X86"
+
+#== 【0】=检查系统指令集(X86_64\IA64\X86)
+arch_checkArchitectureCompatibility() {
+  #== 【0】=检查系统结构(X86_64)
+  local arch
+  arch="$(detectArchitecture)"
+  if [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "IA64"x ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "AARCH64"x ]
+}
+
+#== 【5】=获取 lib 目录
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #== shellcheck disable=SC2016
+    libMacro="/${LIB}"
+  fi
+  printf "%s" "${libMacro}"
+}
+#== 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+#== 【3】【5】=执行命令超时配置
+
+#== 【0】【3】=获取文件权限信息
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+#== 【4】=替换目录
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+
+#== xz 压缩包文件名 Cloudwise-SmartAgent
+readonly INTERNAL_TAR_FILE_NAME=${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}.tar.xz
+
+#== -安装日志: installation-{timestamp}.log,如,installation-20220601110912.log
+readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation-$(date -u +"%Y%m%d%H%M%S").log"
+#readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation_$$.log"
+
+
+
+#== 【临时目录】安装临时目录
+readonly TMP_DIR=${INSTALL_DIR}_install_$$
+#== 【临时目录】解压缓存目录
+readonly UNPACK_CACHE=${BASE_INSTALL_DIR}/unpack_cache
+#== 【临时目录】从sh文件读取压缩文件 tarfile_$$.base64
+readonly EXTERNAL_TAR_FILE=${BASE_INSTALL_DIR}/tarfile_$$.base64
+#== 【临时目录】xz 压缩包文件
+readonly INTERNAL_TAR_FILE=${INSTALL_DIR}/${INTERNAL_TAR_FILE_NAME}
+
+#== 【0】=
+readonly INSTALLER_FILE=${0}
+#== 旧配置文件
+readonly LEGACY_AGENT_CONF_FILE="${AGENT_CONF_DIR}/${BRAND_PRODUCT_NAME_LOWER}.conf"
+
+#== 【0】=
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+
+#== 【0】=
+readonly HELP_URL=""
+
+readonly CONF_FIELD_NM_DATA_SERVER="DataServer"
+readonly CONF_FIELD_NM_CONFIG_SERVER="ConfigServer"
+readonly CONF_FIELD_NM_LICENSE="License"
+readonly CONF_FIELD_NM_JSON_CONF="JSON_CONF"
+readonly CONF_FIELD_NM_USER="User"
+readonly CONF_FIELD_NM_GROUP="Group"
+readonly CONF_FIELD_NM_DATA_STORAGE="DataStorage"
+readonly CONF_FIELD_NM_NON_ROOT_MODE="NonRootMode"
+
+
+#== 配置默认用户权限
+readonly BASE_OMNI_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}
+readonly BASE_OMNI_INSTALL_CONF_DIR=${BASE_OMNI_INSTALL_DIR}/conf/installation.conf
+BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${BASE_OMNI_INSTALL_CONF_DIR}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE}")"
+
+#== Those are read from params section appended to installer by the server
+#== 【0】=【参数】数据服务地址
+PARAM_DATA_SERVER=
+#== 【0】=【参数】配置服务地址
+PARAM_CONFIG_SERVER=
+#== 【0】=【参数】license
+PARAM_LICENSE=
+
+PARAM_JSON_CONF=
+
+#== 【0】=【参数】安装路径
+PARAM_INSTALL_DIR=${INSTALL_DIR}
+#== 【0】=【参数】用户
+PARAM_USER=
+#== 【0】=【参数】用户组
+PARAM_GROUP=
+#== 【0】=【参数】非root模式
+PARAM_NON_ROOT_MODE=true
+
+PARAM_USER_LOGIN=false
+
+#== 【0】=【参数】不允许root回退
+PARAM_DISABLE_ROOT_FALLBACK=false
+#== 【0】=【参数】数据存储目录
+PARAM_DATA_STORAGE=
+#== 【0】=【参数】通过容器部署
+#== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER=false
+#== 【0】=【参数】跳过SELinux策略安装
+#== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER=false
+#== 【0】=【参数】是否使用解压缓存
+PARAM_INTERNAL_USE_UNPACK_CACHE=false
+#== 【0】=【参数】是否不使用 dump
+#== PARAM_INTERNAL_DISABLE_DUMPPROC=
+#== 【0】=【参数】是否跳过非root检查
+PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=false
+#== 【0】【1】=【参数】额外的配置
+#== PARAM_INTERNAL_PASS_THROUGH_SETTERS=
+#== 【0】=检查是否降级安装
+SKIP_DOWNGRADE_CHECK=false
+
+SKIP_PRIVILEGES_CHECK=false
+
+#== 自定义字符串参数
+PARAM_TEST=
+#== 自定义字BOOL参数
+PARAM_BOOL=
+
+#== 【0】=常规日志
+initializeLog() {
+  toConsoleInfo "Installation started, version ${AGENT_INSTALLER_VERSION}, build date: ${AGENT_BUILD_DATE}, PID $$."
+  toLogInfo "Started from: ${INSTALLER_FILE}"
+
+  if [ -f /proc/version ]; then
+    toLogInfo "System version: $(cat /proc/version)"
+  else
+    toLogInfo "System version: $(uname -a)"
+  fi
+
+  toLogInfo "Path: ${PATH}"
+  toLogInfo "INSTALL_DIR: ${INSTALL_DIR}"
+  toLogInfo "Resolved installation path: $(readLink -e "${INSTALL_DIR}" 2>/dev/null)"
+  logBasicStartupInformation "${@}"
+}
+
+#**********************************************************
+# Signing related stuff
+#**********************************************************
+#== 【0】【4】=通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local linesCount
+  local offset
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    linesCount="$(wc -l "${INSTALLER_FILE}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    offset="$(tail -n"${linesToReadFromEnd}" "${INSTALLER_FILE}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${INSTALLER_FILE}"
+  fi
+}
+
+#== 【0】=从指定行范围读取配置参数
+readParam() {
+  local paramName="${1}"
+  local paramsSectionBeggining="${2}"
+  local paramsSectionEnd="${3}"
+
+  sed -n "${paramsSectionBeggining},${paramsSectionEnd} s/^${paramName}=//p" "${INSTALLER_FILE}"
+}
+
+#== 【0】=读取以【----PARAMETERS】开始到 【----PARAMETERS--】之间的参数
+readParamsSection() {
+  local sectionName="----PARAMETERS"
+  local begin
+  local end
+  begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+  end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+  #== 从指定行范围读取配置参数
+  local value
+  if value="$(readParam PARAM_DATA_SERVER "${begin}" "${end}")"; then
+    PARAM_DATA_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_CONFIG_SERVER "${begin}" "${end}")"; then
+    PARAM_CONFIG_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_LICENSE "${begin}" "${end}")"; then
+    PARAM_LICENSE="${value}"
+  fi
+
+  if value="$(readParam PARAM_JSON_CONF "${begin}" "${end}")"; then
+    PARAM_JSON_CONF="${value}"
+  fi
+
+  if value="$(readParam PARAM_USER "${begin}" "${end}")"; then
+    PARAM_USER="${value}"
+  fi
+  if value="$(readParam PARAM_GROUP "${begin}" "${end}")"; then
+    PARAM_GROUP="${value}"
+  fi
+  if value="$(readParam PARAM_NON_ROOT_MODE "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_NON_ROOT_MODE="${value}"
+    fi
+  fi
+
+  if value="$(readParam PARAM_USER_LOGIN "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_USER_LOGIN="${value}"
+    fi
+  fi
+  toConsoleInfo "#DEBUG== install sh params, DATA_SERVER: ${PARAM_DATA_SERVER}, PARAM_CONFIG_SERVER: ${PARAM_CONFIG_SERVER}, PARAM_LICENSE:${PARAM_LICENSE}"
+  toConsoleInfo "#DEBUG== install sh params, PARAM_USER: ${PARAM_USER}, PARAM_GROUP: ${PARAM_GROUP}, PARAM_USER_LOGIN: ${PARAM_USER_LOGIN}, PARAM_NON_ROOT_MODE:${PARAM_NON_ROOT_MODE}"
+}
+
+#== 【0】【6】=清空安装临时文件
+#== ${INSTALL_DIR}_install_$$
+#== EXTERNAL_TAR_FILE=${INSTALL_DIR}/tarfile_$$.base64
+#== ${INSTALL_DIR}/Dynatrace-OneAgent.tar.xz
+#== ${INSTALL_DIR}/xzdec
+#== /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+cleanInstallationTemporaryFiles() {
+  toLogInfo "Cleaning installation temporary files"
+
+  rm -f "${EXTERNAL_TAR_FILE}" "${INTERNAL_TAR_FILE}" "${INSTALL_DIR}/xzdec"
+  rm -Rf "${TMP_DIR}"
+
+  local keepInstallationLockFile="${1}"
+  if [ -z "${keepInstallationLockFile}" ]; then
+    removeInstallationLockFile
+  fi
+}
+
+#== 完成安装后清理临时目录
+finishInstallation() {
+  if [ $# -eq 2 ]; then
+    cleanInstallationTemporaryFiles "${2}"
+  else
+    cleanInstallationTemporaryFiles ""
+  fi
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  changeWorkingDir "${CURR_PATH}"
+  if [ "${CONF_LD_PRELOAD}"x = "true"x ]; then
+    exec /bin/bash && exit 0
+  fi
+  exit "${1}"
+}
+
+#**********************************************************
+# Create folders, copy files, set rights
+#**********************************************************
+
+#== 【3】=创建临时目录
+prepareTempFolder() {
+  #== 删除存在的路径
+  removeIfExists "${TMP_DIR}"
+
+  toLogInfo "Creating temporary folder $TMP_DIR"
+  createDirIfNotExistAndSetRights "${TMP_DIR}" 755
+}
+
+#== 【4】=设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -e "${file}" ]; then
+    chmod "${perms}" "${file}"
+  fi
+}
+
+setRightsForDir() {
+  local dir="${1}"
+  local perms="${2}"
+
+  if [ -d "${dir}" ]; then
+    chmod -R "${perms}" "${dir}"
+  fi
+}
+
+#== 【4】=安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+#== 【4】=移动目录到指定位置
+moveFolderToDestination() {
+  local source="${1}"
+  local destination="${2}"
+  local fullDestination
+  fullDestination="${destination}/$(basename "${source}")"
+  local output
+
+  toLogInfo "Moving ${source} to ${destination}"
+  if [ ! -e "${fullDestination}" ]; then
+    if output="$(mv -f "${source}" "${destination}" 2>&1)"; then
+      toLogInfo "Moving Successfully."
+      return
+    fi
+    toLogWarn "Failed to move ${source} to ${destination}: ${output}, attempting to copy"
+  else
+    toLogInfo "${fullDestination} already exists, attempting to copy"
+  fi
+
+  if ! output="$(cp -Rfp "${source}" "${destination}" 2>&1)"; then
+    toLogError "Failed to copy ${source} to ${destination}: ${output}"
+  fi
+}
+
+#== 【4】=将 bin 下版本内容移动到安装目录
+installVersionedContent() {
+  toLogInfo "Installing versioned content..."
+  createDirIfNotExistAndSetRights "${AGENT_BIN_DIR}" 755
+
+  local sourceDir="${TMP_DIR}/bin"
+  if [ ! -d "${AGENT_BIN_DIR}" ]; then
+    moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+    return
+  fi
+
+  toLogInfo "Directory ${AGENT_BIN_DIR} already exist, repairing the directory"
+  rm -rf "${AGENT_BIN_DIR}"
+  moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+}
+
+#== 【4】=创建当前版本软连接
+
+#== 【4】=删除存在的目录
+listAndRemoveDirectoryIfExists() {
+  local directory="${1}"
+  if [ -d "${directory}" ]; then
+    toLogInfo "${directory} exists, removing it."
+    toLogInfo "Contents: $(ls -lR "${directory}")"
+    rm -rf "${directory}"
+  fi
+}
+
+#== 【4】=临时目录中conf 移动到 agent 安装目录中
+setupConfFolder() {
+  toLogInfo "Setup conf folder..."
+  #== 移动目录到指定位置
+  moveFolderToDestination "${TMP_DIR}/conf" "${INSTALL_DIR}"
+
+  chmod 755 "${AGENT_CONF_DIR}"
+
+  toLogInfo "Setup conf done."
+}
+
+#== 【4】=给距离当前目录至少 ${mindepth} 个子目录的所有文件设置权限
+chmodFilesWithMindepth() {
+  local dir="${1}"
+  local mindepth="${2}"
+  local mask="${3}"
+
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== 安装类型(f:文件,d:目录)递归设置权限
+    chmod4FilesRecursively "${1}" f "${3}"
+  else
+    #== 查找深度距离当前目录至少 ${mindepth} 个子目录的所有文件
+    find "${dir}" -mindepth "${mindepth}" -print0 | xargs -r -0 chmod "${mask}"
+  fi
+}
+
+#== 【4】=配置其他文件(cwserveragent.service、installer.version)
+setupMiscFiles() {
+  toLogInfo "Setup misc files..."
+  #== 创建 cwserveragent.service
+  createSystemdUnitFile
+  #== echo "${AGENT_INSTALLER_VERSION}" >"${INSTALL_DIR}/installer.version"
+  mv -f "${TMP_DIR}/installer.version" "${INSTALL_DIR}/"
+  toLogInfo "Setup misc done."
+}
+
+#== 【4】=创建 cwserveragent.service
+createSystemdUnitFile() {
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    return
+  fi
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  cat <<EOF >${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}
+[Unit]
+Description=${BRAND_AGENT_PRODUCT_NAME}
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+User=root
+ExecStart=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} start
+ExecStop=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} stop
+Type=forking
+#Restart=always
+KillMode=process
+TimeoutSec=240
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+  setRightsForFiles "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 755
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    cp -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" ${INIT_DIR}
+    toLogInfo "Copy scripts ${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT} to ${INIT_DIR} done."
+  fi
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR} done."
+}
+
+setupAll() {
+#  moveFolderToDestination "${TMP_DIR}/package_dir/*" "${INSTALL_DIR}"
+#ls  ${TMP_DIR}/package_dir/*
+
+  #== 移动所有文件到目录
+  cp -Rfp ${TMP_DIR}/package_dir/* "${INSTALL_DIR}"
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  echo ${AGENT_INSTALLER_VERSION} > ${installVersionFile}
+#  mv ${INSTALL_DIR}/package_dir/* ${INSTALL_DIR}
+#     echo "${TMP_DIR}/*" "${INSTALL_DIR}/"
+}
+
+#== 【4】=配置 lib、conf、bin、cwserveragent.service、installer.version、plugins
+setupOptDir() {
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  #== 临时目录中conf 移动到 agent 安装目录中
+  setupConfFolder
+  #== 将 bin 下版本内容移动到安装目录
+  installVersionedContent
+  #== 配置其他文件(cwserveragent.service、installer.version)
+  setupMiscFiles
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScriptsToDirectory() {
+  local scriptLocation="${1}"
+  #== scripts/cwserveragent
+  local scriptFile="${TMP_DIR}/scripts/${SERVICE_SCRIPT_FILE}"
+
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+
+  local output
+  if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+    toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+    return
+  fi
+
+  setRightsForFiles "${scriptLocation}/${SERVICE_SCRIPT_FILE}" 755
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+
+  #== scripts/uninstall.sh
+#  local scriptLocation="${1}"
+#    #== scripts/cwserveragent
+#    local scriptFile="${TMP_DIR}/scripts/uninstall.sh"
+#
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+#
+#    local output
+#    if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+#      toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+#      return
+#    fi
+#    setRightsForFiles "${scriptLocation}/uninstall.sh" 755
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScripts() {
+  toLogInfo "Copy scripts..."
+  #== 创建目录 /opt/cloudwise/cwserveragent/scripts
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  #== 创建目录 /opt/cloudwise/{product}/agents
+  createDirIfNotExistAndSetRights "${AGENTS_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/meta
+  createDirIfNotExistAndSetRights "${META_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/runtime
+  createDirIfNotExistAndSetRights "${AGENT_RUNTIME_DIR}" 775
+
+  #== scripts/uninstall.sh
+  local uninstallScript="${TMP_DIR}/scripts/uninstall.sh"
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} begin."
+  local output
+  if ! output="$(cp "${uninstallScript}" "${INSTALL_DIR}/uninstall.sh" 2>&1)"; then
+    toLogError "Failed to copy ${uninstallScript} to ${INSTALL_DIR}/uninstall.sh, output: ${output}"
+  else
+    setRightsForFiles "${INSTALL_DIR}/uninstall.sh" 755
+  fi
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} done."
+
+  #== 复制临时目录 scripts/ 到安装目录 /opt/cloudwise/cwserveragent/scripts
+  copyScriptsToDirectory "${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    copyScriptsToDirectory "${INIT_DIR}"
+  fi
+  toLogInfo "Copy scripts done."
+}
+
+#== 【4】=调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+createFirstClusterTimestampFile() {
+  toLogInfo "Creating firstClusterTimestamp file"
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+  "$(getAgentInstallActionPath)" "--create-cluster-timestamp-file" >>"${LOG_FILE}" 2>&1
+}
+
+#== 【4】=配置数据存储目录
+setupDataStorageDir() {
+  toLogInfo "Setup datastorage dir..."
+  local dataStorage
+  dataStorage="$(getValueFromConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}" "${DATA_STORAGE_DIR}")"
+  if [ "${dataStorage}"x = "${LOG_DIR}"x ]; then
+    toLogInfo "Detected legacy data storage setting, changing it to the new default location"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}" ]; then
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  toLogInfo "Setup datastorage dir done."
+}
+
+#**********************************************************
+# Processing command line parameters
+#**********************************************************
+#== 【0】=help
+displayHelp() {
+  printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] "
+  #== printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] [DATA_SERVER=https://server_address:server_port] [CONFIG_SERVER=configService] [LICENSE=license] [INSTALL_DIR=install_path]"
+  local beginStr="Usage: "
+  local pad="${#beginStr}"
+
+  printf "\n\n"
+
+  pad=25
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+printOtherParamMessage() {
+  local param="${1}"
+  toConsoleInfo "---> Parameter ${param}"
+}
+
+#== 【0】=大小写转化,并返回判断结果
+istrcmp() {
+  local s1
+  local s2
+  s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+#== 【0】=
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+#== 【1】【5】=
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 【1】【5】=
+invertBoolValue() {
+  local valueToInvert="${1}"
+  if isParamFalse "${valueToInvert}"; then
+    printf '%s' "true"
+  else
+    printf '%s' "false"
+  fi
+}
+
+#== shellcheck disable=SC2003
+#== 【0】=获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+  local partParam
+  partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+#  partParam=$(substr "${input}" 1 ${paramNameLength})
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value
+  value="$(expr substr "${input}" ${valueSeparator} 1000)"
+#  value=$(substr "${input}" ${valueSeparator} 1000)
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 【0】=获取bool参数
+readBoolParam() {
+  local value
+  value="$(getParamValue "${1}" "${2}")"
+
+  if value="$(getBoolParam "${value}")"; then
+    printf "%s" "${value}"
+    return 0
+  fi
+
+  return 1
+}
+
+#== 【0】=获取bool参数
+getBoolParam() {
+  local value="${1}"
+  if [ "${value}" ]; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 【0】=校验格式是否规范[1、大于4个字符且不能"cw."开头,2、不能超过100字符,3、只能包含字母数字字符,连字符,下划线和点。]
+validateParameter() {
+  local name="${1}"
+  local value="${2}"
+  if [ "$(printf '%s' "${value}" | cut -c -3)"x = "cw."x ]; then
+    toConsoleError "${name} must not begin with 'cw.'"
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${#value}" -gt 100 ]; then
+    toConsoleError "Maximum allowed length of ${name} is 100."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${value}" | grep -q "[^[:alnum:]._-]"; then
+    toConsoleError "${name} can only contain alphanumeric characters, hyphen, underscore and dot."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=验证用户和用户组是否匹配
+validateUserAndGroupParameters() {
+  local user="${1}"
+  local group="${2}"
+  local permittedNameRegex='^[[:alnum:]._][[:alnum:]._-]{2,31}$'
+
+  if [ ! "${group}" ]; then
+    group="${user}"
+  fi
+
+  if ! printf '%s' "${user}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "USER can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if ! printf '%s' "${group}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "GROUP can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${user}" "${group}"
+}
+
+#== 【0】【3】【5】=获取系统权限信息
+getSystemEntityInfo() {
+  local database="${1}"
+  local valueToCheck="${2}"
+
+  #== 校验命令getent是否存在
+  if ! isAvailable "getent"; then
+    toLogInfo "Command getent is not available"
+    return 2
+  fi
+
+  #== 查看系统权限
+  local output
+  output="$(getent "${database}" "${valueToCheck}" 2>&1)"
+  local returnCode=$?
+
+  if [ "${returnCode}" != 0 ]; then
+    if [ "${returnCode}" != 2 ]; then
+      toLogWarn "Failed to check ${valueToCheck} in ${database} database, message: ${output}, code: ${returnCode}"
+    fi
+    return 1
+  elif [ ! "${output}" ]; then
+    toLogWarn "Failed to get user information: getent returned no output"
+  fi
+
+  printf '%s' "${output}"
+  return 0
+}
+
+#== 【0】【3】=查看用户/用户组信息
+isEntityPassedById() {
+  local database="${1}"
+  local name="${2}"
+
+  local output
+  output="$(getSystemEntityInfo "${database}" "${name}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    if [ ${returnCode} -eq 2 ]; then
+      toLogInfo "Installer will not be able to verify whether entity was passed by name or by ID"
+    fi
+    return 1
+  fi
+
+  local nameFromDatabase
+  nameFromDatabase="$(printf '%s' "${output}" | cut -d: -f1)"
+
+  if [ "${nameFromDatabase}"x = "${name}"x ]; then
+    return 1
+  fi
+
+  toLogWarn "Name from config and from ${database} system database do not match"
+  toLogWarn "Config: ${name}, database: ${nameFromDatabase}"
+  return 0
+}
+
+#== 【0】【3】【5】=用户是否存在
+userExistsInSystem() {
+  local user="${1}"
+
+  getSystemEntityInfo "passwd" "${user}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Trying to determine user existence using 'id' command"
+  id "${user}" >/dev/null 2>&1
+}
+
+#== 【5】= 查看group
+groupExistsInSystem() {
+  local group="${1}"
+
+  getSystemEntityInfo "group" "${group}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Installer will not be able to determine group existence"
+  return 1
+}
+
+#== 【3】=用户是否存在
+validateUserExistence() {
+  local user="${1}"
+  if ! userExistsInSystem "${user}"; then
+    toConsoleError "User named '${user}' configured for ${BRAND_PRODUCT_NAME} does not exist. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=检查用户/用户组信息
+checkIfEntityWasNotPassedById() {
+  local database="${1}"
+  local valueToCheck="${2}"
+  local valueTypeToLog="user"
+
+  if [ "${database}"x = "group"x ]; then
+    valueTypeToLog="group"
+  fi
+
+  if isEntityPassedById "${database}" "${valueToCheck}"; then
+    toConsoleError "\"${valueToCheck}\" is not a ${valueTypeToLog} name but its ID. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=用户权限下用户组ID
+getUserPrimaryGroupIdForComparison() {
+  local user="${1}"
+
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getSystemEntityInfo "passwd" "${user}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${userPrimaryGroupId}" | cut -d: -f4
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning user primary group name instead of its id"
+  id -gn "${user}"
+}
+
+#== 【0】【3】=获取用户组信息
+getGroupIdForComparison() {
+  local group="${1}"
+
+  local groupId
+  groupId="$(getSystemEntityInfo "group" "${group}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${groupId}" | cut -d: -f3
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning group name instead of its id"
+  printf '%s' "${group}"
+}
+
+#== 【0】【3】=校验用户/用户组信息
+validateUserPrimaryGroup() {
+  local user="${1}"
+  local group="${2}"
+
+  if ! userExistsInSystem "${user}"; then
+    return
+  fi
+
+  checkIfEntityWasNotPassedById "passwd" "${user}"
+  checkIfEntityWasNotPassedById "group" "${group}"
+
+  #== 获取用户组ID
+  local groupId
+  groupId="$(getGroupIdForComparison "${group}")"
+  #== 获取用户下用户组ID
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getUserPrimaryGroupIdForComparison "${user}")"
+
+  toConsoleInfo "#DEBUG== group \"${group}\" id: \"${groupId}\", user \"${user}\" primaryGroupId: \"${userPrimaryGroupId}\""
+  if [ "${userPrimaryGroupId}"x != "${groupId}"x ]; then
+    toConsoleError "User named \"${user}\" does not have group named \"${group}\" as its primary group. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【3】=检查用户/用户组是否存在和匹配
+checkUserAndGroupFromConfig() {
+  local configUser
+  local configGroup
+  configUser="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  configGroup="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Checking validity of user account '${configUser}:${configGroup}'"
+
+  if [ "${PARAM_UPGRADE}"x = "yes"x ]; then
+    validateUserExistence "${configUser}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${configUser}" "${configGroup}"
+}
+
+#== 【0】=解析命令行中参数
+parseCommandLineParameters() {
+  local dataServerIsEmpty=true
+  local configServerIsEmpty=true
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue DATA_SERVER "${param}"); then
+      PARAM_DATA_SERVER="${value}"
+      dataServerIsEmpty=false
+      printParamMessage "DATA_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG_SERVER "${param}"); then
+      PARAM_CONFIG_SERVER="${value}"
+      configServerIsEmpty=false
+      printParamMessage "CONFIG_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue LICENSE "${param}"); then
+      PARAM_LICENSE="${value}"
+      printParamMessage "LICENSE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue JSON_CONF "${param}"); then
+      PARAM_JSON_CONF="${value}"
+      printParamMessage "JSON_CONF" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INSTALL_DIR "${param}"); then
+      PARAM_INSTALL_DIR="${value}"
+      printParamMessage "INSTALL_DIR" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue DATA_STORAGE "${param}"); then
+      PARAM_DATA_STORAGE="${value}"
+      printParamMessage "DATA_STORAGE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DISABLE_SYSTEM_LOGS_ACCESS "${param}"); then
+      PARAM_DISABLE_SYSTEM_LOGS_ACCESS="${value}"
+      printParamMessage "DISABLE_SYSTEM_LOGS_ACCESS" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INTERNAL_OVERRIDE_CHECKS "${param}"); then
+      if printf '%s' "${value}" | grep -wq "privileges"; then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=true
+      fi
+
+      if printf '%s' "${value}" | grep -wq "downgrade"; then
+        SKIP_DOWNGRADE_CHECK=true
+      fi
+      printParamMessage "INTERNAL_OVERRIDE_CHECKS" "${value}"
+
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam INTERNAL_USE_UNPACK_CACHE "${param}"); then
+      PARAM_INTERNAL_USE_UNPACK_CACHE="${value}"
+      printParamMessage "INTERNAL_USE_UNPACK_CACHE" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+      if value=$(getParamValue USER "${param}"); then
+        PARAM_USER="${value}"
+        printParamMessage "USER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(getParamValue GROUP "${param}"); then
+        PARAM_GROUP="${value}"
+        printParamMessage "GROUP" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE "${param}"); then
+        PARAM_NON_ROOT_MODE="${value}"
+        printParamMessage "NON_ROOT_MODE" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam USER_LOGIN "${param}"); then
+        PARAM_USER_LOGIN="${value}"
+        printParamMessage "USER_LOGIN" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义参数获取
+      if value=$(getParamValue TEST "${param}"); then
+        PARAM_TEST="${value}"
+        printParamMessage "TEST" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义bool参数
+      if value=$(readBoolParam BOOL "${param}"); then
+        PARAM_BOOL="${value}"
+        printParamMessage "BOOL" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DISABLE_ROOT_FALLBACK "${param}"); then
+        PARAM_DISABLE_ROOT_FALLBACK="${value}"
+        printParamMessage "DISABLE_ROOT_FALLBACK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK "${param}"); then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK="${value}"
+        printParamMessage "NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam INTERNAL_SKIP_SELINUX_POLICY_INSTALLER "${param}"); then
+        #== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER="${value}"
+        printParamMessage "INTERNAL_SKIP_SELINUX_POLICY_INSTALLER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DOCKER_ENABLED "${param}"); then
+        #== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER="${value}"
+        printParamMessage "DOCKER_ENABLED" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ] && [ "${ARCH_ARCH}"x != "S390"x ]; then
+      if value=$(readBoolParam INTERNAL_DISABLE_DUMPPROC "${param}"); then
+        #== PARAM_INTERNAL_DISABLE_DUMPPROC="${value}"
+        printParamMessage "INTERNAL_DISABLE_DUMPPROC" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+    
+    printOtherParamMessage ${param}
+    shift
+  done
+
+  if [ "${configServerIsEmpty}"x = "true"x ] && [ "${dataServerIsEmpty}"x != "true"x ]; then
+    PARAM_CONFIG_SERVER="${PARAM_DATA_SERVER}"
+  fi
+}
+
+#== 【0】=校验PARAM_INSTALL_DIR是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的】
+validateInstallPathParameter() {
+  if printf '%s' "${PARAM_INSTALL_DIR}" | grep -q "[[:space:]]"; then
+    toConsoleError "Installation path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_INSTALL_DIR}"x = "/"x ]; then
+    toConsoleError "Installation path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_INSTALL_DIR}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Installation path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=校验PARAM_DATA_STORAGE是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的,4、数据目录不能放在安装目录下】
+validateDataStorageParameter() {
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "[[:space:]]"; then
+    toConsoleError "Data storage path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}"x = "/"x ]; then
+    toConsoleError "Data storage path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_DATA_STORAGE}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Data storage path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "^${INSTALL_DIR}"; then
+    toConsoleError "Data storage path must not be located within ${INSTALL_DIR}."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+
+#**********************************************************
+# Config files
+#**********************************************************
+
+#== 【3】【5】=格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+formatSize() {
+  local sizeInKiB="${1}"
+  local formattedSize
+
+  for symbol in "KiB" "MiB" "GiB" "TiB"; do
+    if printf '%s' "${sizeInKiB}" | awk '$1 >= 1024 { exit 1; }'; then
+      formattedSize="${sizeInKiB} ${symbol}"
+      break
+    fi
+    sizeInKiB="$(printf '%s' "${sizeInKiB}" | awk '{ print $1 / 1024 }')"
+  done
+
+  printf '%s' "${formattedSize}"
+}
+
+#== 【3】【5】=
+cropSizeValue() {
+  local size="${1}"
+  local value
+  local unit
+  value="$(printf '%s' "${size}" | cut -d' ' -f1)"
+  unit="$(printf '%s' "${size}" | cut -d' ' -f2)"
+  printf '%.2f %s' "${value}" "${unit}"
+}
+
+#== 【3】【5】=检查目录可用空间(单位:k)
+checkFreeSpace() {
+  local path="${1}"
+  local requiredSpaceInKiB="${2}"
+  local dfOutput
+  local baseFilesystem
+  local freeSpace
+  local formattedRequiredSpace
+  local formattedFreeSpace
+
+  #== shellcheck disable=SC2086
+  dfOutput="$(df -P ${AIX_DF_SPECIFIC_FLAG} "${path}" | tail -n +2)"
+  #== 文件系统目录
+  baseFilesystem="$(printf "%s" "${dfOutput}" | awk '{ print $NF }')"
+  #== 剩余空间(单位:k)
+  freeSpace="$(printf "%s" "${dfOutput}" | awk '{ print $4 }')"
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedRequiredSpace="$(formatSize "${requiredSpaceInKiB}")"
+  toLogInfo "Filesystem with ${path} is mounted under ${baseFilesystem}. Space required: ${formattedRequiredSpace}."
+
+  if [ ! "${freeSpace}" ]; then
+    printf 'Cannot determine amount of free space on %s (filesystem mounted under %s)' "${path}" "${baseFilesystem}"
+    return 1
+  fi
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedFreeSpace="$(formatSize "${freeSpace}")"
+  toLogInfo "Available free space: ${formattedFreeSpace}"
+
+  if [ "${freeSpace}" -lt "${requiredSpaceInKiB}" ]; then
+    printf 'Not enough free space on %s (filesystem mounted under %s). ' "${path}" "${baseFilesystem}"
+    printf 'Required: %s, available: %s' "$(cropSizeValue "${formattedRequiredSpace}")" "$(cropSizeValue "${formattedFreeSpace}")"
+    return 2
+  fi
+
+  printf 'Free space is sufficient'
+  return 0
+}
+
+#== 【3】=检查文件系统类型
+
+#== 【3】=检查安装目录空间
+checkInstallPathFreeSpace() {
+  local externalTarSize=${EXTERNAL_TAR_SIZE}
+  local artifactsSize=${ARTIFACTS_SIZE}
+  #== use 10% additional margin
+  local requiredSpace=$((externalTarSize + artifactsSize * 11 / 10))
+  #== convert to kibibytes
+  requiredSpace=$((requiredSpace / 1024))
+
+  toConsoleInfo "Checking free space in ${INSTALL_DIR}"
+
+  local message
+  #== 检查目录可用空间(单位:k)
+  message="$(checkFreeSpace "${INSTALL_DIR}" "${requiredSpace}")"
+  case $? in
+  0) toLogInfo "${message}" ;;
+  1) toConsoleWarn "${message}. Installation may be incomplete." ;;
+  2)
+    toConsoleError "${message}"
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_SPACE}"
+    ;;
+  esac
+}
+
+#== 【3】=获取指定路径的文件系统信息
+getFilesystemInfo() {
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    mount | grep " ${1} "
+  else
+    grep " ${1} " /proc/self/mounts
+  fi
+}
+
+#== 【3】=检查目录指定权限信息
+checkAccessRightsTo() {
+  local dir="${1}"
+  toLogInfo "Checking access to ${dir}..."
+  #== 获取文件权限信息
+  local accessRights
+  accessRights="$(arch_getAccessRights "${dir}" | cut -c 2-4)"
+  if ! printf '%s' "${accessRights}" | grep -q rwx; then
+    toConsoleError "Insufficient permissions on ${dir}: '${accessRights}'."
+    toLogInfo "$(ls -dl "${dir}" 2>&1)"
+    finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+  fi
+
+  local dfResult
+  local filesystem
+  local filesystemInfo
+  dfResult="$(df -P "${dir}")"
+  #== 获取目录文件系统路径
+  filesystem="$(printf '%s' "${dfResult}" | tail -1 | awk '{ print $NF }')"
+  #== 获取指定路径的文件系统信息
+  filesystemInfo="$(getFilesystemInfo "${filesystem}")"
+  if ! printf '%s' "${filesystemInfo}" | grep -qw rw; then
+    toLogWarn "df-based check determined filesystem mounted under ${filesystem} as readonly, trying fallback."
+    toLogWarn "Filesystem access rights: '${filesystemInfo}'"
+    toLogWarn "df returned: ${dfResult}"
+
+    if ! testWriteAccessToDir "${dir}"; then
+      toConsoleError "readonly filesystem mounted under ${filesystem}"
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+  fi
+
+  toLogInfo "Rights on directory ${dir} are sufficient"
+} 2>>"${LOG_FILE}"
+
+#== 【3】=目录是否写权限
+checkIfInstallationPathIsWriteable() {
+  for dir in "${@}"; do
+    if [ -d "${dir}" ]; then
+      checkAccessRightsTo "${dir}"
+      break
+    fi
+  done
+}
+
+#== 【3】=检查目录权限
+checkAccessRightsToDirs() {
+  checkIfInstallationPathIsWriteable "${INSTALL_DIR}" "${BASE_INSTALL_DIR}" "${INSTALL_BASE}" /
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    checkAccessRightsTo "${INIT_DIR}"
+  fi
+}
+
+
+#== 【0】=检查系统是 LINUX、AIX及指令集(X86_64\IA64\X86)
+checkSystemCompatibility() {
+  local expectedPlatform="LINUX"
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    expectedPlatform="AIX"
+  fi
+
+  #== 获取系统名称
+  local platform
+  platform="$(uname | sed -e 's/_.*//' | sed -e 's/\///' | tr '[:lower:]' '[:upper:]')"
+  if [ "${platform}"x != "${expectedPlatform}"x ]; then
+    printf "Cannot determine platform or platform not supported: <%s>" "${platform}"
+    return 1
+  fi
+
+  #== 检查系统指令集(X86_64\IA64\X86)
+  local detectedArchitecture
+  if ! detectedArchitecture="$(arch_checkArchitectureCompatibility)"; then
+    printf "Cannot determine architecture or architecture not supported: <%s>" "${detectedArchitecture}"
+    return 1
+  fi
+
+  printf 'Detected platform: %s' "${platform}"
+  if [ "${detectedArchitecture}" ]; then
+    printf ' arch: %s' "${detectedArchitecture}"
+  fi
+
+  return 0
+}
+
+#== 【4】=从sh文件读取压缩文件 tarfile_$$.base64
+separateExternalTar() {
+  toLogInfo "Determining begin of tar archive..."
+  local tarBegin
+  local tarEnd
+  #== 压缩文件开始行数,占位符为【#################ENDOFSCRIPTMARK############】
+  tarBegin="$(locateDelimiter "#################ENDOFSCRIPTMARK############" "")"
+  tarBegin=$((tarBegin + 1))
+  #== 压缩文件结束行数,占位符为【----SIGNED-INSTALLER】
+  tarEnd="$(locateDelimiter "----SIGNED-INSTALLER" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})"
+  toLogInfo "tarBegin=${tarBegin} tarEnd=${tarEnd}"
+
+  if [ ! "${tarEnd}" ]; then
+    toConsoleError "S/MIME signature is missing, installation package is corrupted."
+    finishInstallation "${EXIT_CODE_CORRUPTED_PACKAGE}"
+  fi
+
+  local tarLength=$((tarEnd - tarBegin))
+  toLogInfo "tarLength=${tarLength}"
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  tail -n +"${tarBegin}" "${INSTALLER_FILE}" 2>/dev/null | head -${tarLength} >"${EXTERNAL_TAR_FILE}"
+}
+
+#== 【4】【5】=进入目录
+changeWorkingDir() {
+  if ! cd "${1}"; then
+    toLogError "Failed to change working directory to ${1}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== shellcheck disable=SC2181
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchiveWithoutCache() {
+  #== 解压文件编码
+  local base64Binary="${UNPACK_BINARY}"
+  #== 解压文件编码参数
+  local base64BinaryArgs="${UNPACK_BINARY_ARGS}"
+  #== 使用xzdec命令可以进行liblzma为基础的xz文件解压缩
+  local xzBinary="${INSTALL_DIR}/xzdec"
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+
+  if ! isAvailable tar; then
+    toConsoleError "tar binary not found. Setup can't continue"
+    finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  local totalLines
+  if ! isAvailable "${base64Binary}"; then
+    toLogInfo "${base64Binary} not found. Falling back to openssl decode"
+    if ! isAvailable openssl; then
+      toConsoleError "Neither ${base64Binary} nor openssl can be found. Setup can't continue"
+      finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+    fi
+    #== 如果没有 base64 命令,则使用 openssl 解压文件
+    base64Binary="openssl"
+    base64BinaryArgs="enc -base64 -d -in"
+
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      #truncate the first and the last one line due to specific format of uuencode on aix
+      totalLines="$(wc -l "${EXTERNAL_TAR_FILE}" | awk '{print $1}')"
+      head -$((totalLines - 1)) "${EXTERNAL_TAR_FILE}" 2>/dev/null | tail +2 >"${EXTERNAL_TAR_FILE}.$$"
+      mv -f "${EXTERNAL_TAR_FILE}.$$" "${EXTERNAL_TAR_FILE}"
+    fi
+  fi
+
+  {
+    #== base64 转编码后,执行 tar 解压
+    "${base64Binary}" "${base64BinaryArgs}" "${EXTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  #== base64 转编码 或 tar解压失败
+  if [ $? -gt 0 ]; then
+    toConsoleError "Archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+
+  #== 进入目录
+  changeWorkingDir "${TMP_DIR}"
+  {
+    #== 使用xzdec命令解压后,执行 tar 解压
+    "${xzBinary}" "${INTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  if [ $? -gt 0 ]; then
+    toConsoleError "XZ compressed archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+}
+
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchive() {
+  toConsoleInfo "Unpacking. This may take a while..."
+
+  #== 【参数】是否使用解压缓存
+  if [ "${PARAM_INTERNAL_USE_UNPACK_CACHE}"x = "true"x ]; then
+    if [ -d "${UNPACK_CACHE}" ]; then
+      toLogInfo "Unpack cache will be used."
+      cp -Rp "${UNPACK_CACHE}"/* "${TMP_DIR}"
+    else
+      toLogInfo "Unpack cache does not exist."
+      mkdir -p "${UNPACK_CACHE}"
+      #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+      unpackArchiveWithoutCache
+      cp -Rp "${TMP_DIR}"/* "${UNPACK_CACHE}"
+    fi
+  else
+    toLogInfo "Unpacking without cache"
+    #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+    unpackArchiveWithoutCache
+  fi
+
+  toConsoleInfo "Unpacking complete."
+}
+
+#== 【3】=检查容器部署、运行情况、配置参数
+#isDeployedInsideOpenVZContainer
+#isProcessRunningInContainer
+#isDeployedViaContainer
+
+
+#== Checking if libc is new enough
+#== 【3】=格式化version
+format_version() {
+  printf '%s' "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'
+}
+
+#== 【3】=格式化路径
+format_patch() {
+  printf '%s' "$@" | tail -n 1 | awk -F- '{ printf("%d\n", $3); }'
+}
+
+#== 【3】=检查是否降级安装
+checkIfDowngrade() {
+  if [ "${SKIP_DOWNGRADE_CHECK}"x = "true"x ]; then
+    toConsoleInfo "Skipped downgrade check"
+    return
+  fi
+
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  if [ ! -f "${installVersionFile}" ]; then
+    toLogWarn "Could not perform downgrade check, ${installVersionFile} file is missing"
+    return
+  fi
+
+  local oldVersion
+  oldVersion="$(cat "${installVersionFile}")"
+  if [ "$(format_version "${AGENT_INSTALLER_VERSION}")" -eq "$(format_version "${oldVersion}")" ]; then
+    toConsoleError "${BRAND_PRODUCT_NAME} is already installed, please uninstall the old version using [${INSTALL_DIR}/uninstall.sh]."
+#    toLogError "Attempted downgrade from ${oldVersion} to ${AGENT_INSTALLER_VERSION}"
+    finishInstallation "${EXIT_CODE_UNSUPPORTED_DOWNGRADE}"
+  fi
+}
+
+#== 【3】=检查是否已经安装
+checkIfAlreadyInstalled() {
+  if [ -f "${INSTALL_DIR}/uninstall.sh" ] && [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ] ; then
+#    toConsoleError "Agent already installed. Uninstalling previous version."
+#    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+
+    checkIfDowngrade
+    PARAM_UPGRADE="yes"
+  else
+#  		if [ -f "${SIF_AGENT_INSTALL_PATH}/lib64/${AGENT_BIN}" ]; then
+#  			sif_toConsoleError "Upgrade is not possible because uninstall script is missing"
+#  			finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+#  		fi
+      local fileNum
+      fileNum=`ls ${INSTALL_DIR} | wc -l`
+      if [ "${fileNum}" -gt 1 ];then
+         toConsoleError "${INSTALL_DIR} is not empty. Please clean or change the installation directory manually."
+         finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+      fi
+  fi
+
+
+
+}
+
+#**********************************************************
+# Init related functions
+#**********************************************************
+
+#== clears dependencies in LSB init script
+#== 【5】=清除LSB init脚本中的依赖项
+clearDependenciesInLSBInit() {
+  local file="${1}"
+  toConsoleInfo "Clearing dependencies in file ${file}"
+  awk '
+		BEGIN {
+			req_start_found=0;
+			req_stop_found=0;
+			REQ_START="# Required-Start:";
+			REQ_STOP="# Required-Stop:";
+			PATTERN_REQ_START="^" REQ_START;
+			PATTERN_REQ_STOP="^" REQ_STOP;
+		}
+		{
+			if ($0 ~ PATTERN_REQ_START && req_start_found == 0) {
+				print REQ_START;
+				req_start_found++;
+			} else if ($0 ~ PATTERN_REQ_STOP && req_stop_found == 0) {
+				print REQ_STOP;
+				req_stop_found++;
+			} else
+				print $0
+		}' "${file}" >"${file}.tmp" && mv -f "${file}.tmp" "${file}"
+
+  chmod +x "${file}"
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptToSystemvAutostart() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+
+  toLogInfo "Adding ${file} to autostart"
+  #== 自动启动工具
+  if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+    toLogWarn "Failed to add ${file} script to autostart. Trying without dependencies..."
+    #== 清除LSB init脚本中的依赖项
+    clearDependenciesInLSBInit "${INIT_DIR}/${file}"
+    if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+      toConsoleError "Cannot add ${file} to autostart. For details, see: ${LOG_FILE}"
+    fi
+  fi
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptsToAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+  #== 指定目录添加自动启动脚本
+  addScriptToSystemvAutostart "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"
+}
+
+#== 【5】=设置自动启动
+#setupSystemvAutostart() {
+#  toLogInfo "Adding ${BRAND_AGENT_PRODUCT_NAME} to autostart..."
+#
+#  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+#    #== 指定目录添加自动启动脚本
+#    addScriptsToAutostart "/usr/bin/update-rc.d " "defaults"
+#  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+#    addScriptsToAutostart "/usr/sbin/update-rc.d " "defaults"
+#  elif [ -x /sbin/chkconfig ]; then #RedHat
+#    addScriptsToAutostart "/sbin/chkconfig --add "
+#  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+#    addScriptsToAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+#  elif [ "${ARCH_ARCH}"x = "AIX"x ]; then
+#    #== 【不存在】
+#    arch_setAutostart
+#  else
+#    toConsoleError "Couldn't add ${BRAND_AGENT_PRODUCT_NAME} to autostart. Please adjust and add it manually."
+#  fi
+#}
+
+#== 【5】=设置 cwserveragent.service 中用户
+setServiceScriptUser() {
+  if ! isNonRootModeEnabled; then
+    return
+  fi
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 修改 cwserveragent.service 中用户信息
+  sed -i "s/User=.*/User=${user}/g" "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+}
+
+#== 【5】=配置自动启动 cwserveragent.service
+setupSystemdAutostart() {
+  mv -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" "${SYSTEMD_UNIT_FILES_DIR}/"
+  setRightsForFiles "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 644
+
+  #== 设置 cwserveragent.service 中用户
+  setServiceScriptUser
+
+  if isAvailable restorecon; then
+    restorecon "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  #== 执行 systemctl 命令配置允许开机启动 cwserveragent.service
+  executeSystemctlCommand enable "${SYSTEMD_UNIT_FILE_AGENT}"
+  #== 执行 systemctl 命令重新加载模块
+  executeSystemctlCommand daemon-reload ""
+}
+
+#== 【6】=运行service 脚本
+execIntoServiceScript() {
+  toConsoleInfo "${SERVICE_SCRIPT_FILE} will be started via exec()"
+  #== 清除安装临时文件
+  cleanInstallationTemporaryFiles
+  toLogInfo "Installation finished, PID $$."
+
+  local command="exec ${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} exec"
+  toLogInfo "Executing: ${command}"
+  #== 执行 cwserveragent exec
+  ${command}
+
+  toLogError "Could not execute: ${command}"
+  finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+}
+
+#== 【6】=运行agent
+runAgents() {
+  toConsoleInfo "Starting agents..."
+
+  toLogInfo "Using ${INIT_SYSTEM} to start the agent"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    #== 运行初始化命令(通过 service start 方式 或 直接运行可执行命令)
+    executeInitScriptCommand start "true"
+  else
+    #== 执行 systemctl start 命令
+    executeSystemctlCommand start "${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  if [ $? -eq 0 ]; then
+    toConsoleInfo "${SERVICE_SCRIPT_FILE} service started"
+  else
+    toConsoleError "Failed to start service: ${SERVICE_SCRIPT_FILE}, it is possible that your init system is not functioning properly. For details, see: ${LOG_FILE}"
+  fi
+}
+
+#== 【5】=配置自动启动
+#setupAutostart() {
+#  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+#    #== 设置自动启动
+#    setupSystemvAutostart
+#  else
+#    #== 配置自动启动 cwserveragent.service
+#    setupSystemdAutostart
+#  fi
+#}
+
+#**********************************************************
+# Process agent related functions
+#**********************************************************
+
+#== 【5】=创建 agent 状态文件
+createAgentStateFile() {
+  local path="${1}"
+  local agentStateContents="RUNNING"
+
+  toLogInfo "Writing ${agentStateContents} to ${path} file"
+  {
+    printf "%s" "${agentStateContents}" >"${path}.tmp"
+    mv -f "${path}.tmp" "${path}"
+  } 2>>"${LOG_FILE}"
+}
+
+#== 【0】=检查全路径是否可读权限(目录最深100层)
+checkIfPathIsGloballyReadable() {
+  local path="${1}"
+  local sourcePath="${path}"
+  local maxDepth=100
+  while [ "${path}"x != "/"x ]; do
+    #== 【0】=获取文件权限信息
+    local accessRights
+    accessRights="$(arch_getAccessRights "${path}")"
+    if ! printf '%s' "${accessRights}" | cut -c 8-10 | grep -qE "r.[xt]"; then
+      toConsoleError "Insufficient access rights (${accessRights}) on: ${path}"
+      toConsoleError "${sourcePath} path must be globally readable (r-x permissions for others)."
+      toConsoleError "Please adjust the permissions and then retry the installation."
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+
+    path="$(dirname "${path}")"
+
+    maxDepth=$((maxDepth - 1))
+    if [ "${maxDepth}" -eq 0 ]; then
+      toLogWarn "Unable to verify access rights on ${path}"
+      return
+    fi
+  done
+}
+
+#== 【0】=创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+createSymlinkToInstallLocation() {
+  #== INSTALL_DIR 不能是软链接地址
+  if [ -L "${INSTALL_DIR}" ] && [ ! -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Detected that ${INSTALL_DIR} is a dangling symlink, please remove it and then retry the installation"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== PARAM_INSTALL_DIR 和 INSTALL_DIR 都不能是空地址
+  if [ "${PARAM_INSTALL_DIR}"x = "${INSTALL_DIR}"x ] || [ -z "${PARAM_INSTALL_DIR}" ]; then
+    return
+  fi
+
+  #== INSTALL_DIR 是软连接,并获取 PARAM_INSTALL_DIR 和 INSTALL_DIR 的真实地址
+  if [ -L "${INSTALL_DIR}" ] && [ "$(readLink -m "${PARAM_INSTALL_DIR}")"x = "$(readLink -m "${INSTALL_DIR}")"x ]; then
+    return
+  fi
+
+  #== 目录存在,表示未卸载
+  if [ -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Leftovers from previous agent installation detected"
+    toConsoleError "If you wish to use INSTALL_DIR parameter then perform a cleanup by following these steps:"
+    toConsoleError "1. Uninstall the agent"
+    toConsoleError "2. Restart all applications that have Deep Monitoring enabled (host restart is fine as well)"
+    toConsoleError "3. Remove ${INSTALL_DIR}"
+    toConsoleError "and then retry the installation."
+    toConsoleError "#DEBUG==For further information please visit ${HELP_URL}/command-line-install"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 创建不存在的目录
+  createDirIfNotExistAndSetRights "${PARAM_INSTALL_DIR}" 1775
+  #== 检查全路径是否可读权限(目录最深100层)
+  checkIfPathIsGloballyReadable "${PARAM_INSTALL_DIR}"
+  #== 创建不存在的目录 /opt/cloudwise
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" 755
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR
+  local lnOutput
+  if lnOutput="$(ln -fs "${PARAM_INSTALL_DIR}" "${INSTALL_DIR}" 2>&1)"; then
+    toConsoleInfo "Symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR} created"
+  else
+    toConsoleError "Failed to create symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR}, aborting installation: ${lnOutput}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【5】=修改文件系统用户和用户组
+changeFilesOwnership() {
+  local user
+  local group
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Changing ownership of files to root:${group}"
+  #== /opt/cloudwise/cwserveragent 目录下用户和用户组
+  commandErrorWrapper chown "${user}:${group}" "${AGENT_BASE_DIR}"
+  commandErrorWrapper chown -R "${user}:${group}" "${INSTALL_DIR}"
+  toLogInfo "Recursively changing group ownership of ${AGENT_CONF_DIR} to ${group}"
+
+}
+
+#== 【5】=文件用户权限检查
+fileCapabilitiesCompatibilityCheck() {
+  #== 获取配置用户
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+  if output="$("$(getOsConfigBinPath)" file-capabilities-compatibility-check "${user}" 2>&1)"; then
+    return 0
+  fi
+
+  if [ "${PARAM_NON_ROOT_MODE}" ]; then
+    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogWarn "Capabilities test output: ${output}"
+  else
+    toConsoleInfo "Non-privileged mode was not enabled, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogInfo "Capabilities test output: ${output}"
+  fi
+  #== 设置删除root权限
+  "$(getAgentInstallActionPath)" "--set-drop-root-privileges" "false" >>"${LOG_FILE}" 2>&1
+  return 1
+}
+
+
+#**********************************************************
+# User and group related functions
+#**********************************************************
+
+#== 【5】=添加用户组
+addGroup() {
+  local group="${1}"
+
+  if groupExistsInSystem "${group}"; then
+    toLogInfo "Group '${group}' already exists"
+    return 0
+  fi
+
+  local errorMessage
+  errorMessage="$(groupadd "${group}" 2>&1)"
+  local returnCode=$?
+
+  case ${returnCode} in
+  0) toLogInfo "Group '${group}' successfully created" ;;
+  9) toLogInfo "Group '${group}' already exists" ;;
+  *)
+    toLogError "Error occured while adding '${group}' group, return code: ${returnCode}, message ${errorMessage}"
+    return 1
+    ;;
+  esac
+  return 0
+}
+
+#== 【5】=添加用户
+addUser() {
+  local user="${1}"
+  local group="${2}"
+  local groupCreated="${3}"
+  local errorMessage
+  local mod="/bin/false"
+  if [ "${PARAM_USER_LOGIN}"x == "true"x ]; then
+    mod="/bin/bash"
+  fi
+  if userExistsInSystem "${user}"; then
+    toLogInfo "User '${user}' already exists."
+    #-- 权限更新
+#    errorMessage="$(usermod -s "${mod}" "${user}" 2>&1)"
+    return 0
+  fi
+
+  if [ "${groupCreated}" -eq 0 ]; then
+    errorMessage="$(useradd -r --shell "${mod}" -g "${group}" "${user}" 2>&1)"
+  else
+    errorMessage="$(useradd -r --shell "${mod}" "${user}" 2>&1)"
+  fi
+
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toConsoleError "Failed to create user '${user}'"
+    toLogError "Error occured while adding '${user}' user, return value: ${returnCode}, error message: ${errorMessage}."
+    return 1
+  fi
+
+  toConsoleInfo "User '${user}' added successfully."
+  return 0
+}
+
+#== 【5】=添加用户和用户组信息
+addUserAndGroup() {
+  local user="${1}"
+  local group="${2}"
+  #== 添加用户组
+  addGroup "${group}"
+  #== 添加用户
+  addUser "${user}" "${group}" $?
+}
+
+#== 【5】=配置用户和用户组
+handleUser() {
+  toLogInfo "Processing user and group..."
+  local user
+  local group
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 读取用户组(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-group)
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 添加用户和用户组信息
+  addUserAndGroup "${user}" "${group}"
+}
+
+#== 【4】=获取agent preLoad 安装路径,并校验是否可执行
+checkCompatibilityWithInstallActionBinary() {
+  local output
+  #== 获取agent preLoad 安装路径,并校验是否可执行
+  output="$("$(getAgentInstallActionPathPreInstallation)" "--sanity-check" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Compatibility check exit code = ${exitCode}, output = ${output}"
+  if [ ${exitCode} -ne 0 ] || [ "${output}"x != "SUCCESS"x ]; then
+    toConsoleError "System compatibility check failed, this may be caused by a problem with glibc, dynamic loader or incompatible operating system version."
+    toConsoleError "Detected version: $(detectLinuxDistribution)"
+    toConsoleError "For a list of supported distributions and versions, see: ${HELP_URL}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【部署】解压文件
+extractFiles() {
+  umask 000
+  toConsoleInfo "Extracting..."
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  separateExternalTar
+  #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+  unpackArchive
+  umask 022
+}
+
+#== 【3】=检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+detectInitSystem() {
+  #== 获取初始化系统信息 (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  #== 设置系统初始化脚本目录
+  if ! setLocationOfScripts; then
+    toConsoleError "Cannot determine location of init scripts."
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【旧配置】读取旧配置参数
+readLegacySetting() {
+  local paramName="${1}"
+  local value
+  value="$(sed -n "s|^${paramName}=||p" "${LEGACY_AGENT_CONF_FILE}" | tr -d '\r' 2>/dev/null)"
+  if [ "${value}" ]; then
+    local valueToPrint="${value}"
+    if [ "${paramName}"x = "${CONF_FIELD_NM_LICENSE}"x ]; then
+      valueToPrint="***"
+    fi
+    toLogInfo "Read legacy value: ${paramName} = ${valueToPrint}"
+  fi
+
+  printf '%s' "${value}"
+}
+
+#== 【4】=【旧配置】迁移旧配置文件
+migrateLegacySettingsFromAgentConf() {
+  toLogInfo "Looking for legacy config to migrate."
+  #== 旧配置文件
+  if [ ! -f "${LEGACY_AGENT_CONF_FILE}" ]; then
+    toLogInfo "Unable to read agent config file, skipping legacy config migration"
+    return
+  fi
+
+  if [ ! "${PARAM_CONFIG_SERVER}" ]; then
+    local configServerValue
+    configServerValue="$(readLegacySetting ${CONF_FIELD_NM_CONFIG_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_CONFIG_SERVER}: ${PARAM_CONFIG_SERVER}"
+    if [ "${configServerValue}"x = "http://localhost:8020"x ]; then
+      toLogInfo "Param 'server' has default value set, skipping it"
+    else
+      PARAM_CONFIG_SERVER="${configServerValue}"
+    fi
+  fi
+
+  if [ ! "${PARAM_DATA_SERVER}" ]; then
+    PARAM_DATA_SERVER="$(readLegacySetting ${CONF_FIELD_NM_DATA_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_DATA_SERVER}: ${PARAM_DATA_SERVER}"
+  fi
+
+  if [ ! "${PARAM_LICENSE}" ]; then
+    PARAM_LICENSE="$(readLegacySetting ${CONF_FIELD_NM_LICENSE})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_LICENSE}: ${PARAM_LICENSE}"
+  fi
+
+  if [ ! "${PARAM_JSON_CONF}" ]; then
+    PARAM_JSON_CONF="$(readLegacySetting ${CONF_FIELD_NM_JSON_CONF})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_JSON_CONF}: ${PARAM_JSON_CONF}"
+  fi
+
+  if [ ! "${PARAM_USER}" ]; then
+    PARAM_USER="$(readLegacySetting ${CONF_FIELD_NM_USER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_USER}: ${PARAM_USER}"
+
+    if [ ! "${PARAM_GROUP}" ]; then
+      PARAM_GROUP="$(readLegacySetting ${CONF_FIELD_NM_GROUP})"
+      toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_GROUP}: ${PARAM_GROUP}"
+    fi
+  fi
+}
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E \"${includeRegex}\" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(pgrep -f "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses
+  foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+#== 版本&帮助信息
+preParams() {
+  if [ $# -gt 0 ]; then
+    if [ "${1}"x = "-h"x ] || [ "${1}"x = "--help"x ]; then
+        displayHelp
+        exit "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${1}"x = "-v"x ] || [ "${1}"x = "--version"x ]; then
+       pad=20
+       printf "%-${pad}s%s\n" "Version:" "${AGENT_INSTALLER_VERSION}"
+       printf "%-${pad}s%s\n" "Commit:" "${AGENT_BUILD_TAG}"
+       printf "%-${pad}s%s\n" "Build At(UTC):" "${AGENT_BUILD_DATE_INFO}"
+       printf "%-${pad}s%s\n" "Uploader:" "${AGENT_BUILD_UPLOADER}"
+       exit "${EXIT_CODE_OK}"
+    fi
+  fi
+}
+#**********************************************************
+# Main script functions
+#**********************************************************
+
+#== 【0】=初始化安装环境
+initializeInstallation() {
+  preParams "$@"
+  #== 设置PATH
+  setPATH
+  #== 配置umask权限
+  local initialUmask
+  initialUmask="$(umask)"
+  umask 022
+
+  #== 读取配置参数
+#  readParamsSection
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  #== 校验配置参数
+
+  #== 是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+  if isAnotherInstallationRunning; then
+    #== 结束安装操作
+    finishInstallation "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}" "keep_lock_file"
+  fi
+
+  #== 创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+  createInstallationLockFile
+  #== 配置是否更新
+  PARAM_UPGRADE="no"
+
+  #== 获取并校验系统兼容性【LINUX/AIX  X86_64/IA64】
+  local platformDetectionString
+  if ! platformDetectionString="$(checkSystemCompatibility)"; then
+    toConsoleError "${platformDetectionString}"
+    finishInstallation "${EXIT_CODE_OS_NOT_SUPPORTED}"
+  fi
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+  createSymlinkToInstallLocation
+  #== 创建日志目录及日志文件installation_$$.log
+  createLogDirsIfMissing
+  #== 配置信号捕获,执行清空安装临时目录
+  configureSignalHandling "cleanInstallationTemporaryFiles"
+  #== 常规日志
+  initializeLog "${@}"
+
+  toLogInfo "Initial umask: ${initialUmask}"
+  toConsoleInfo "${platformDetectionString}"
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+}
+
+#== 【3】=安装前检查
+preInstallationChecks() {
+  #== 检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+  detectInitSystem
+
+  #== 检查目录权限
+  checkAccessRightsToDirs
+  #== 创建临时目录
+  prepareTempFolder
+  #== 检查是否已经安装
+  checkIfAlreadyInstalled
+
+  if [ ! "${PARAM_USER}" ] && [ "${ARCH_ARCH}"x != "AIX"x ] ; then
+    #== 配置文件中配置用户和用户组是否存在
+    checkUserAndGroupFromConfig
+  fi
+
+  #== 检查安装目录空间
+  checkInstallPathFreeSpace
+
+}
+
+uninstallAgent() {
+	toConsoleInfo "Agent already installed. Uninstalling previous version."
+#	printf '%s' "upgrade" >"${UNINSTALL_INFO_FILE_LEGACY_PATH}"
+
+	#shellcheck disable=SC2086
+	"${INSTALL_DIR}/uninstall.sh" $$ ${SKIP_PRIVILEGES_CHECK} 2>>"${LOG_FILE}"
+
+	local uninstallExitCode=$?
+	if [ ${uninstallExitCode} -gt 0 ]; then
+		toConsoleError "Error during uninstalling, code: ${uninstallExitCode}. Installation aborted."
+		finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+	toConsoleInfo "Agent uninstalled."
+}
+
+#== 【4】=部署文件
+deployFiles() {
+  if [ "${PARAM_UPGRADE}" = "yes" ]; then
+    uninstallAgent
+  fi
+  #== 【部署】解压文件
+  extractFiles
+  setupAll
+  createCurrentVersionSymlink
+}
+enableRootDropping() {
+  local output=
+  if ! isAvailable setcap; then
+    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. Set NON_ROOT_MODE=false."
+    #== writeParamToConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "false" "${LEGACY_AGENT_CONF_FILE}"
+    #== writeParamToConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "false" "${INSTALLER_CONF_FILE}"
+    #== editScriptFileParam "readonly PARAM_NON_ROOT_MODE" "false" "${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE}"
+    return
+  fi
+
+  #== 设置文件权限 (https://www.cnblogs.com/xzongblogs/p/14106481.html)
+  #== CAP_DAC_OVERRIDE:绕过文件的读,写,和执行权限检查。
+  #== CAP_FOWNER:对于通常要求进程的文件系统 UID 与文件的 UID 匹配的操作,绕过权限检查 (比如,chmod(2),utime(2)),除了那些包含在 CAP_DAC_OVERRIDE 和 CAP_DAC_READ_SEARCH 中的操作
+  #== CAP_IPC_LOCK:允许锁定共享内存片段
+  #== CAP_SYS_PTRACE:允许跟踪任何进程
+  #== CAP_SYS_ADMIN:访问特权 perf 事件信息
+  #== CAP_SYS_RESOURCE:忽略资源限制
+  #== CAP_NET_ADMIN:允许执行多种网络有关的操作
+  commandErrorWrapper setcap cap_setgid,cap_setuid,cap_dac_override,cap_fowner,cap_chown,cap_ipc_lock,cap_sys_ptrace,cap_sys_admin,cap_sys_resource,cap_net_admin+ep "${AGENT_BIN_DIR}/${AGENT_PROC}"
+  local setCapCwServerAgentExitCode=$?
+  if [ ${setCapCwServerAgentExitCode} -eq 0 ] ; then
+    toConsoleInfo "Set file capabilities [${AGENT_PROC}]"
+  else
+    toConsoleWarn "Failed to enable non-privileged mode. Exit Code : ${setCapCwServerAgentExitCode} . For details, see: ${LOG_FILE}"
+    toLogWarn "Set file capabilities output: ${output}"
+  fi
+}
+
+#== 【5】=配置安装
+configureInstallation() {
+  #== 配置参数(param、installation.conf)
+  #== 自定义修改配置, sed ,echo ...  参考 applyAgentSettings
+  if [ "${PARAM_TEST}" ]; then
+    toConsoleOnly "Applying test value is: ${PARAM_TEST}"
+  fi
+  #== 获取bool值参数
+  if [ "${PARAM_BOOL}" ]; then
+    toConsoleOnly "Applying bool value is: ${PARAM_BOOL}"
+  fi
+
+
+  if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+    #== 配置用户和用户组
+    if handleUser; then
+      #== 修改文件系统用户和用户组
+      changeFilesOwnership
+    fi
+    #== 设置root权限
+    enableRootDropping
+  fi
+  #== 系统配置(策略配置、dump proc、agent 进程配置、自动启动)
+#  setupAutostart
+}
+
+#== 【6】=验证安装状态、运行启动脚本
+postInstallationSteps() {
+  #== 运行agent
+#  runAgents
+  finishInstallation "${EXIT_CODE_OK}"
+}
+
+readonly CURR_PATH="$(pwd)"
+main() {
+  #== 【0】=初始化安装环境
+  initializeInstallation "$@"
+  #== 【3】=安装前检查
+  preInstallationChecks
+  #== 【4】=部署文件
+  deployFiles
+  #== 【5】=配置安装
+  configureInstallation
+  #== 【6】=验证安装状态、运行启动脚本
+  postInstallationSteps
+
+}
+
+#**********************************************************
+# Script start
+#**********************************************************
+main "$@"
+
+################################################
+############# DO NOT REMOVE THIS ###############
+#### DO NOT ADD ANYTHING BELOW THIS COMMENT ####
+################################################
+#################ENDOFSCRIPTMARK################
+
+----SIGNED-INSTALLER

+ 611 - 0
dist/x86_64/scripts/package.sh

@@ -0,0 +1,611 @@
+#!/bin/bash
+
+AGENT_INSTALLER_VERSION=1.0.0
+
+#== 当前脚本必须放在 smartagent/scripts 目录下
+readonly SCRIPT_PATH=$(dirname $0)
+readonly SCRIPT_DIR=$(cd $SCRIPT_PATH && pwd)
+readonly CURR_FILE_NAME="$(basename "$0")"
+readonly SMARTAGENT_DIR=$(cd $SCRIPT_DIR/../ && pwd)
+readonly SMARTAGENT_PARENT_DIR=$(cd $SCRIPT_DIR/../../ && pwd)
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+BRAND_PRODUCT_NAME="euspace"
+BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="cwserveragent"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="ucloudwise"
+
+
+readonly BAK_EXT_NAME=".bak"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+readonly XZDEC_NAME="xzdec"
+readonly INSTALL_TEMP_FILE_NAME="install_temp.sh"
+readonly INSTALL_VERSION_FILE_NAME="installer.version"
+#== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+ARCH=$(uname -m)
+TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+#TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+
+TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+
+readonly LOG_DIR="${SMARTAGENT_PARENT_DIR}/logs"
+readonly LOG_FILE_NAME="release.log"
+readonly LOG_FILE="${LOG_DIR}/${LOG_FILE_NAME}"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+readonly EXIT_CODE_SEMANAGE_NOT_FOUND=10
+readonly EXIT_CODE_WATCHDOG_ALREADY_RUNNING=11
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+readonly EXIT_CODE_DEL_DEFAULT_PARAMS=20
+
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#**********************************************************
+# compress
+#**********************************************************
+tarSmartAgent() {
+  toConsoleInfo "There are 5 steps to packing, begin package ${BRAND_AGENT_PRODUCT_NAME}...."
+  #== 将打包脚本(package.sh、xzdec、install_temp.sh、smartagent、uninstall.sh)移动到临时目录
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    mv -f "${SCRIPT_DIR}/${fileNm}" "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" 2>>"${LOG_FILE}"
+  done
+  mv -f "${SCRIPT_DIR}/${XZDEC_NAME}" "${SMARTAGENT_PARENT_DIR}/" 2>>"${LOG_FILE}"
+  cp -f "${SMARTAGENT_PARENT_DIR}/${INSTALL_TEMP_FILE_NAME}${BAK_EXT_NAME}" "${TARGET_INSTALL_TMP_FILE}" 2>>"${LOG_FILE}"
+
+  for fileNm in "uninstall.sh"; do
+    cp -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+  done
+
+  #== 删除安装脚本中注释
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    toConsoleInfo "Delete the type [#==] comment ..."
+    sed -i '/#==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#==/d' "${SCRIPT_DIR}/uninstall.sh"
+    toConsoleInfo "Delete the type [#DEBUG==] comment ..."
+    sed -i '/#DEBUG==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/uninstall.sh"
+  fi
+
+  if [ "${PARAM_RELEASE}"x = "true"x ]; then
+    deleteDefaultParams "${TARGET_INSTALL_TMP_FILE}"
+  fi
+
+  #== 进入 smartagent 目录下
+  cd "${SMARTAGENT_DIR}"
+  cp -f "${SCRIPT_DIR}/uninstall.sh" ./package_dir
+  #== 执行第一阶段压缩(源码压缩)Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【1】start【tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" ./package_dir/*】to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" --exclude="*.gitkeep" --exclude="*.log" ./package_dir/* >>"${LOG_FILE}" 2>&1
+  rm -f "./package_dir/uninstall.sh"
+
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+  local agentTarSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar")"
+
+  cd "${SMARTAGENT_PARENT_DIR}"
+  #== 执行第二阶段压缩(源码压缩 Cloudwise-SmartAgent.tar.xz
+  toConsoleInfo "step【2】start【xz -T0 -z ${BRAND_AGENT_PRODUCT_NAME}.tar】to ${BRAND_AGENT_PRODUCT_NAME}.tar.zx"
+  xz -T0 -1 -z "${BRAND_AGENT_PRODUCT_NAME}.tar" -vv
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第三阶段压缩 Cloudwise-SmartAgent.tar.xz xzdec to Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【3】start【tar -cpf ${BRAND_AGENT_PRODUCT_NAME}.tar ${BRAND_AGENT_PRODUCT_NAME}.tar.xz xzdec】 to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${BRAND_AGENT_PRODUCT_NAME}.tar" "${BRAND_AGENT_PRODUCT_NAME}.tar.xz" xzdec >>"${LOG_FILE}" 2>&1
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第四阶段压缩 Cloudwise-SmartAgent.tar to Cloudwise-SmartAgent.tar.base64
+  toConsoleInfo "step【4】start【base64 ${BRAND_AGENT_PRODUCT_NAME}.tar > ${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${BRAND_AGENT_PRODUCT_NAME}.tar.base64"
+  base64 "${BRAND_AGENT_PRODUCT_NAME}.tar" >"${BRAND_AGENT_PRODUCT_NAME}.tar.base64" 2>>"${LOG_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第五阶段将 base64 文件写入安装脚本
+  toConsoleInfo "step【5】start write 【${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+  #== 设置脚本中版本、创建时间、占用空间信息
+  editInstallScriptParam "${TARGET_INSTALL_TMP_FILE}" ${agentTarSize}
+
+  sed -i "/#################ENDOFSCRIPTMARK################/r ${BRAND_AGENT_PRODUCT_NAME}.tar.base64" "${TARGET_INSTALL_TMP_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  toConsoleInfo "${BRAND_AGENT_PRODUCT_NAME} packaging success."
+  finishPackage "${EXIT_CODE_OK}"
+}
+
+buildVersion() {
+  local oldVersion
+  if oldVersion="$(getOldVersion)"; then
+    setInstallVersion "${oldVersion}"
+    removeIfExist "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+		toConsoleInfo "Delete file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}."
+  fi
+
+  if [ "${PARAM_VERSION}" ]; then
+    setInstallVersion "${PARAM_VERSION}"
+    return
+  fi
+}
+
+setInstallVersion() {
+  local version="${1}"
+  if [ ! "${version}" ]; then
+    toConsoleWarn "Set install version failed,not found version version."
+    return
+  fi
+
+  AGENT_INSTALLER_VERSION="${version}"
+  BRAND_PRODUCT_NAME="${PARAM_NAME}"
+  BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+  TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+  #== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+#  TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+  TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+}
+
+getOldVersion() {
+	local installVersionFile="${SMARTAGENT_DIR}/installer.version"
+	if [ ! -f "${installVersionFile}" ]; then
+		toLogWarn "Could not find ${installVersionFile} file."
+		return 1
+	fi
+
+	local oldVersion="$(cat "${installVersionFile}")"
+	if [ ! "${oldVersion}" ]; then
+		toLogWarn "Could not read install version from ${installVersionFile} file."
+	  return 1
+  fi
+
+  printf '%s' "${oldVersion}"
+  return 0
+}
+
+#== 通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local filePath="${3}"
+
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    local linesCount="$(wc -l "${filePath}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    local offset="$(tail -n"${linesToReadFromEnd}" "${filePath}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${filePath}"
+  fi
+}
+
+#== 删除安装脚本中默认参数
+deleteDefaultParams() {
+  local filePath="${1}"
+  local sectionName="----PARAMETERS"
+  local begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+  local end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+
+  toConsoleInfo "Begin delete default params from line ${begin} to ${end}..."
+  local output="$(sed -i "${begin},${end}d" "${filePath}") 2>&1"
+  if [ ! "${output}" ]; then
+    toConsoleError "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params error:${output}"
+    finishPackage "${EXIT_CODE_DEL_DEFAULT_PARAMS}"
+  fi
+  toConsoleInfo "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params success."
+}
+
+#== 设置脚本中版本、创建时间、占用空间信息
+editInstallScriptParam() {
+  local targetFile="${1}"
+  local agentTarSize="${2}"
+  #== 设置版本信息
+  local buildDate="$(date +"%Y-%m-%d")"
+  local buildTag="$(git rev-parse --short HEAD)"
+  local buildDateInfo="$(date +"%Y-%m-%d %H:%M:%S")"
+  local buildUploader="$(hostname)/${USER}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE" "${buildDate}" "${targetFile}"
+  #== 构建版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${targetFile}"
+  #== commitid
+  editScriptFileParam "readonly AGENT_BUILD_TAG" "'${buildTag}'" "${targetFile}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE_INFO" "'${buildDateInfo}'" "${targetFile}"
+  #== 构建环境信息
+  editScriptFileParam "readonly AGENT_BUILD_UPLOADER" "'${buildUploader}'" "${targetFile}"
+
+  #== 设置占用空间信息
+  local agentDirSize="$(getStorageSize "${SMARTAGENT_DIR}")"
+  local agentBase64FileSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar.base64")"
+  local agentInstallFileSize="$(getStorageSize "${TARGET_INSTALL_TMP_FILE}")"
+  local artifactsSize="$((agentBase64FileSize + agentInstallFileSize + agentDirSize))";
+  editScriptFileParam "readonly EXTERNAL_TAR_SIZE" "${agentTarSize}" "${targetFile}"
+  editScriptFileParam "readonly ARTIFACTS_SIZE" "${artifactsSize}" "${targetFile}"
+}
+
+getStorageSize() {
+  local dirName="${1}"
+  if [ ! -e "${dirName}" ]; then
+    printf '%s' "0"
+    return 0
+  fi
+  printf '%s' "$(du -sb "${dirName}" | awk '{print $1}')"
+}
+
+finishFinally() {
+  #== 将打包脚本文件移动到当前目录下
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    if [ -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" ]; then
+      mv -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+    fi
+  done
+  removeIfExist "${SMARTAGENT_DIR}/uninstall.sh"
+  if [ -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" ]; then
+    mv -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" "${SCRIPT_DIR}/" 2>>"${LOG_FILE}"
+  fi
+
+  if [ -f "${TARGET_INSTALL_TMP_FILE}" ]; then
+    mv -f "${TARGET_INSTALL_TMP_FILE}" "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}" 2>>"${LOG_FILE}"
+  fi
+
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+      removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+    done
+  fi
+
+
+}
+
+removeIfExist() {
+  local filePath="${1}"
+  if [ ! -e "${filePath}" ]; then
+    return
+  fi
+  rm -rf "${filePath}" 2>>"${LOG_FILE}"
+}
+
+deleteParentTmpFile() {
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}"
+  done
+  removeIfExist "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}"
+  removeIfExist "${TARGET_INSTALL_TMP_FILE}"
+
+  for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+  done
+}
+
+#**********************************************************
+# Param
+#**********************************************************
+PARAM_DEBUG=false
+PARAM_RELEASE=false
+#PARAM_VERSION=1.1.0
+PARAM_NAME=euspace
+PARAM_AGENT_PROC=$PARAM_NAME
+PARAM_CONFIG_PATH=
+
+#== 大小写转化,并返回判断结果
+istrcmp() {
+  local s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  local s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+
+  local partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value="$(expr substr "${input}" ${valueSeparator} 1000)"
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 获取bool参数
+readBoolParam() {
+  local value=
+  if value="$(getParamValue "${1}" "${2}")"; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 解析命令行中参数
+parseCommandLineParameters() {
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue RELEASE "${param}"); then
+      PARAM_RELEASE="${value}"
+      printParamMessage "RELEASE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue VERSION "${param}"); then
+      PARAM_VERSION="${value}"
+      printParamMessage "VERSION" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue NAME "${param}"); then
+      PARAM_NAME="${value}"
+      printParamMessage "NAME" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG "${param}"); then
+      PARAM_CONFIG_PATH="${value}"
+      printParamMessage "CONFIG" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue PROC "${param}"); then
+      PARAM_AGENT_PROC="${value}"
+      printParamMessage "PROC" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DEBUG "${param}"); then
+      PARAM_DEBUG="${value}"
+      printParamMessage "DEBUG" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    toConsoleError "Unrecognized parameter: ${param}"
+
+    displayHelp
+    finishPackage "${EXIT_CODE_INVALID_PARAM}"
+  done
+}
+
+displayHelp() {
+  printf '\n'
+  printf '%s\n' "Usage: "${CURR_FILE_NAME}" [-h] [-v] [NAME=pluginName] [PROC=mainProcess] [RELEASE=true|false] [DEBUG=true|false] [VERSION=1.0]"
+  printf '\n\n'
+
+  local pad=15
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+  printf '\n'
+  printf "%-${pad}s%s\n" "RELEASE" "Default true; Whether to delete the publish parameter in the script."
+  printf "%-${pad}s%s\n" "VERSION" "Configure the ${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME} version."
+  printf "%-${pad}s%s\n" "NAME" "Setting agent name like 'bash ./scripts/package.sh NAME=pluginName PROC=mainProcess'"
+  printf "%-${pad}s%s\n" "PROC" "Setting agent main process name."
+  printf "%-${pad}s%s\n" "DEBUG" "Default false; 1、Debug mode executed script ${CURR_FILE_NAME};"
+  printf "%-${pad}s%s\n" "" "2、Remove the debug log from the scripts (${TARGET_INSTALL_FILE_NAME}、${SERVICE_SCRIPT_FILE}、uninstall.sh)."
+}
+
+#== 完成安装后清理临时目录
+finishPackage() {
+  finishFinally
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  exit "${1}"
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+
+  local value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "---> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback} "${signal}"
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    #== shellcheck disable=SC2064
+    trap "signalHandler ${signal} ${callback}" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#**********************************************************
+# main
+#**********************************************************
+main() {
+  mkdir -p "${LOG_DIR}"
+  echo "" >"${LOG_FILE}"
+
+  toConsoleInfo "package script run path: ${SCRIPT_DIR}"
+  toConsoleInfo "package logs path: ${LOG_DIR}"
+
+  chmod +x ${SCRIPT_DIR}/*
+
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  toConsoleInfo "-----params PARAM_RELEASE: ${PARAM_RELEASE}, PARAM_DEBUG: ${PARAM_DEBUG}, PARAM_VERSION: ${PARAM_VERSION}"
+
+  configureSignalHandling "finishFinally"
+
+  #== 设置 package 插件名
+  editScriptFileParam "BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/package.sh"
+  #== 设置 install 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  #== 设置 agentctl 插件名(存在时)
+  if [ -f "${SMARTAGENT_DIR}/package_dir/bin/agentctl" ];then
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  #== 设置 agentctl 配置文件路径(存在时)
+    editScriptFileParam "readonly AGENT_CONFIG_PATH" "\"${PARAM_CONFIG_PATH}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+
+  #== 设置 uninstall 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  #== 设置主进程名
+  if [ -n "${PARAM_AGENT_PROC}" ]; then
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/install_temp.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/uninstall.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+  buildVersion
+  toConsoleInfo "-----Write release version ${AGENT_INSTALLER_VERSION} to ${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME} file."
+  echo "${AGENT_INSTALLER_VERSION}">"${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME}"
+
+  #== 修改uninstall.sh 版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${SCRIPT_DIR}/uninstall.sh"
+
+  deleteParentTmpFile
+
+  #== 压缩 并生成安装脚本
+  tarSmartAgent
+}
+
+main "$@"

+ 1116 - 0
dist/x86_64/scripts/uninstall.sh

@@ -0,0 +1,1116 @@
+#!/bin/bash
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+#== 根路径
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="cloudwise"
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== smartagentd
+readonly AGENT_PROC="euspace"
+readonly AGENT_INSTALLER_VERSION=1.0.0
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly CURRENT_VERSION_LINK=${AGENT_BASE_DIR}/current
+
+#== /opt/cloudwise/omniagent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+
+readonly AGENT_LIBS_DIR="${INSTALL_DIR}/libs"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+
+readonly AGENT_AGENTS_DIR="${INSTALL_DIR}/agents"
+
+readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"
+
+
+
+readonly PERL_FILE="/usr/bin/perl"
+
+readonly SAFE_RM_FILE="safe-rm"
+
+readonly SAFE_RM_BIN="${AGENT_BIN_DIR}/${SAFE_RM_FILE}"
+
+if [ -f ${PERL_FILE} ] && [ -f "${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}" ]; then
+    readonly SAFE_RM_EXEC="${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}"
+else
+    readonly SAFE_RM_EXEC="rm"
+fi
+
+readonly LOG_DIR_NAME=logs
+#== /opt/cloudwise/omniagent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/omniagent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+#readonly INSTALLER_LOG_SUBDIR="installer"
+##== /opt/cloudwise/smartagent/log/installer
+#readonly INSTALLER_LOG_DIR=${LOG_DIR}/${INSTALLER_LOG_SUBDIR}
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+
+
+readonly USER="root"
+readonly GROUP="root"
+#== 非root模式
+readonly PARAM_NON_ROOT_MODE=false
+
+readonly CONTAINER_DEPLOYMENT_STATE_ENTRY=false
+#== 是否有 SELinux
+readonly ARCH_HAS_SELINUX=true
+################################################################################
+#	Logs
+################################################################################
+toLog() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLog "[INFO]" "$@"
+}
+
+toLogWarning() {
+  toLog "[WARN]" "$@"
+}
+
+toLogError() {
+  toLog "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarning() {
+  toConsoleOnly "Warning:" "$@"
+  toLogWarning "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarning "Cannot create ${dir} directory."
+      toLogWarning "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarning "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarning "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+#== 创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+
+################################################################################
+#	Platform characteristics detection
+################################################################################
+
+#== 设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -f "${file}" ]; then
+    chmod "${perms}" "${file}"
+  elif [ -d "${file}" ]; then
+    chmod -R "${perms}" "${file}"
+  fi
+}
+
+#== 安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+detectLinuxDistribution() {
+  if [ -f /etc/redhat-release ]; then
+    cat /etc/redhat-release
+  elif [ -f /etc/os-release ]; then
+    (
+      . /etc/os-release
+      local distrib="${NAME}"
+      if [ -z "${distrib}" ]; then
+        distrib="${ID}"
+      fi
+
+      local version="${VERSION_ID}"
+      if printf "%s" "${distrib}" | grep -iq "debian"; then
+        version="$(cat /etc/debian_version)"
+      fi
+
+      printf "%s %s" "${distrib}" "${version}"
+    )
+  elif [ -f /etc/SuSE-release ]; then
+    head -1 /etc/SuSE-release
+  elif [ -f /etc/lsb-release ]; then
+    (
+      . /etc/lsb-release
+      printf "%s %s" "${DISTRIB_ID}" "${DISTRIB_RELEASE}"
+    )
+  elif ls /etc/*release* >/dev/null 2>&1; then
+    # Generic fallback
+    cat /etc/*release*
+  else
+    printf "AIX %s" "$(oslevel -s 2>&1)"
+  fi
+}
+
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarning "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+setLocationOfInitScripts() {
+  toLogInfo "Determining location of init scripts..."
+
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSTEMD}" ] || [ "${ARCH_ARCH}" = "AIX" ]; then
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of init scripts ${INIT_DIR}"
+  return 0
+}
+
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+################################################################################
+#	Misc functions
+################################################################################
+
+getAgentCtlBinPath() {
+  printf "%s" "${AGENT_TOOLS_DIR}/lib64/${AGENT_CTL_BIN}"
+}
+
+getOsConfigBinPath() {
+  printf "%s" "${AGENT_INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+commandErrorWrapper() {
+  set +e
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarning "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  ${SAFE_RM_EXEC} -f "${errorFile}"
+  set -e
+  return ${returnCode}
+}
+
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)" != "0" ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+removeIfExists() {
+  local pathToRemove="${1}"
+  toLogInfo "${SAFE_RM_EXEC} -rf ${pathToRemove}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(${SAFE_RM_EXEC} -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarning "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+################################################################################
+#	SELinux related functions
+################################################################################
+
+executeUsingOsConfigBin() {
+  local command="${1}"
+  local unit="${2}"
+  if [ "${unit}" ]; then
+    command="${command}-${unit}"
+    unit=""
+  fi
+
+  local output=
+  output="$("$(getOsConfigBinPath)" "${command}" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Executed $(getOsConfigBinPath) ${command} ${unit}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  #shellcheck disable=SC2086
+  output="$("${command}" ${parameters} 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+signalHandler() {
+	end_exit_code=$?
+	if [ ${end_exit_code} -eq ${EXIT_CODE_SIGNAL_RECEIVED} ]; then
+		return
+	fi
+	local signal="${1}"
+	local callback="${2}"
+	if [ ${end_exit_code} -ne 0 ]; then
+		toLogWarning "Process Received [Signal: ${signal}] [Code : ${end_exit_code}]"
+	fi
+	${callback} ${end_exit_code}
+
+	if [ ${end_exit_code} -eq ${EXIT_CODE_OK} ]; then
+		exit ${EXIT_CODE_OK}
+	fi
+
+	exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM EXIT; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(Api-Token=\)[[:alnum:]_-]\{21\}#\1***#' |
+    sed 's#\(TENANT_TOKEN=\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(latest/\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(PROXY=\)[^[:space:]]*#\1***#'
+}
+
+#== waitTime must be divisible by 10
+sendSignalToProcessAndWaitForStop() {
+  local pidCheckingFunction="${1}"
+  local signal="${2}"
+  local action="${3}"
+  local waitTime="${4}"
+  local pidToStop="${5}"
+
+  if ! ${pidCheckingFunction} "${pidToStop}"; then
+    toLogInfo "Process with pid ${pidToStop} doesn't exist"
+    return
+  fi
+
+  toConsoleInfo "Waiting ${waitTime} seconds for process with pid ${pidToStop} to ${action}."
+  while [ "${waitTime}" -gt 0 ]; do
+    if [ "$((waitTime % 10))" -eq 0 ]; then
+      toLogInfo "Sending signal: ${signal} to ${pidToStop}"
+      kill -s "${signal}" "${pidToStop}" 2>>"${LOG_FILE}"
+    fi
+
+    if ! ${pidCheckingFunction} "${pidToStop}"; then
+      return 0
+    fi
+    sleep 1
+    waitTime=$((waitTime - 1))
+  done
+  return 1
+}
+
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    ${SAFE_RM_EXEC} -f "${errorFile}"
+    return 1
+  fi
+
+  ${SAFE_RM_EXEC} -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+systemLibDirSanityCheck() {
+  local dir="${1}"
+  if [ ! -d "${dir}" ]; then
+    toLogWarning "Directory: ${dir} does not exist"
+    printf ""
+    return
+  fi
+
+  if ! testWriteAccessToDir "${dir}"; then
+    toLogWarning "Detected that ${dir} is not writable"
+    dir="${PA_FALLBACK_INSTALLATION_DIR}${dir}"
+    createDirIfNotExistAndSetRights "${dir}" 755
+  fi
+
+  printf "%s" "${dir}"
+}
+
+isNonRootModeEnabled() {
+  printf '%s' "${PARAM_NON_ROOT_MODE}" | grep -qE "(true|no_ambient)"
+}
+
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  if isNonRootModeEnabled && printf '%s' "${prefix}" | grep -q "chkconfig"; then
+    local action="chkconfig-add"
+    if printf '%s' "${prefix}" | grep -q -- "--del"; then
+      action="chkconfig-del"
+    fi
+    toLogInfo "Using ${AGENT_OS_CONFIG_BIN} ${action}-${file} to modify autostart"
+    output="$("$(getOsConfigBinPath)" "${action}-${file}" 2>&1)"
+  else
+    local command="${prefix}${file} ${suffix}"
+    toLogInfo "Executing ${command}"
+    output="$(${command} 2>&1)"
+  fi
+  local status=$?
+
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    local parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarning "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}" = "AIX" ]; then
+      PATH="${PATH}:/opt/freeware/bin"
+    fi
+
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+getValueFromConfigFile() {
+  local key="${1}"
+  local separator="${2}"
+  local configFile="${3}"
+  local defaultValue="${4}"
+
+  local value="$(sed -n "s|${key}${separator}||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+isAnotherInstallerRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+
+  local pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  local nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarning "Installer lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarning "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #shellcheck disable=SC2009
+  local foundProcesses="$(ps -e -o "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIRECTORY}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installer process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 0
+}
+
+createInstallerLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+removeInstallerLockFile() {
+  toLogInfo "Removing installer lock file."
+  ${SAFE_RM_EXEC} -f "${INSTALLER_LOCK_FILE}"
+}
+
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+mapPidsToName() {
+  local pids="${1}"
+  local output
+  for pid in ${pids}; do
+    local name="$(grep 'Name:' "/proc/${pid}/status" 2>/dev/null | awk '{print $2}')"
+    output="${output}, ${pid} (${name})"
+  done
+
+  printf '%s' "${output}" | cut -c 3-
+}
+
+readonly ARCH_ARCH="X86"
+readonly ARCH_VERSIONED_LIB_DIR_PREFIX="linux-x86"
+
+arch_checkArchitectureCompatibility() {
+  local arch="$(detectArchitecture)"
+  if [ "${arch}" = "X86_64" ] || [ "${arch}" = "IA64" ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}" = "X86_64" ]
+}
+
+arch_local_getLibraryPathFromLdd() {
+  local binary="${1}"
+  ldd "${binary}" 2>/dev/null | grep libc.so | awk '{ print $3 }'
+}
+
+arch_local_detectProcessAgentInstallerDirectories() {
+  local useLddOutput="false"
+
+  local systemLib32Prefix
+  systemLib32Prefix="$(arch_local_getSystemLibraryPath 32)"
+  local exitCode=$?
+
+  if [ ! "${systemLib32Prefix}" ]; then
+    if [ "${exitCode}" -eq 0 ]; then
+      toLogWarning "This is a 64-bit platform with 32-bit libraries installed, but ${AGENT_INSTALL_ACTION_BIN} failed to determine their location"
+      useLddOutput="true"
+    else
+      toLogInfo "This is a 64-bit platform and 32-bit libraries were not detected"
+    fi
+  else
+    systemLib32Prefix="$(systemLibDirSanityCheck "/${systemLib32Prefix}")"
+    if [ ! "${systemLib32Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  local systemLib64Prefix="$(arch_local_getSystemLibraryPath 64)"
+  if [ ! "${systemLib64Prefix}" ]; then
+    toLogWarning "This is a 64-bit platform, but ${AGENT_INSTALL_ACTION_BIN} failed to determine location of 64-bit libraries"
+    useLddOutput="true"
+  else
+    systemLib64Prefix="$(systemLibDirSanityCheck "/${systemLib64Prefix}")"
+    if [ ! "${systemLib64Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  if [ "${useLddOutput}" = "true" ]; then
+    arch_local_detectProcessAgentDirectoriesBasedOnLdd
+  else
+    readonly SYSTEM_LIB32="${systemLib32Prefix}"
+    readonly SYSTEM_LIB64="${systemLib64Prefix}"
+  fi
+} 2>>"${LOG_FILE}"
+
+arch_detectProcessAgentInstallerDirectories() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x = "true"x ]; then
+    readonly SYSTEM_LIB32="${PA_FALLBACK_INSTALLATION_DIR}/lib32"
+    readonly SYSTEM_LIB64="${PA_FALLBACK_INSTALLATION_DIR}/lib64"
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB32}" 755
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB64}" 755
+    return 0
+  fi
+
+  arch_local_detectProcessAgentInstallerDirectories
+}
+
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #shellcheck disable=SC2016
+    libMacro='/$LIB'
+  fi
+  printf "%s" "${libMacro}"
+}
+
+arch_checkGlibc() {
+  local glibcVersion="$(ldd --version | awk 'NR==1{ print $NF }')"
+
+  toLogInfo "Detected glibc version: ${glibcVersion}"
+
+  if [ "$(format_version "${glibcVersion}")" -gt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    return
+  elif [ "$(format_version "${glibcVersion}")" -lt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    toConsoleError "We can't continue setup. The glibc version: ${glibcVersion} detected on your system isn't supported."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}."
+    toConsoleError "Stopping installer process..."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  if ! isAvailable rpm; then
+    toLogInfo "RPM not detected - skipping glibc patch version check."
+    return
+  fi
+
+  local glibcFullVersion="$(arch_runCommandWithTimeout 60 "rpm" "-q" "glibc" | tail -n 1)"
+  if [ ! "${glibcFullVersion}" ]; then
+    toConsoleError "Could not determine exact glibc version using RPM."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  # fix for RedHat 5.x with unsufficient glibc 2.5 patch version
+  local glibcPatchVersion="$(format_patch "${glibcFullVersion}")"
+  if [ "$(format_version "${glibcVersion}")" -eq "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ] && [ "${glibcPatchVersion}" -lt "${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}" ]; then
+    toConsoleError "glibc patch version: ${glibcPatchVersion} detected on your system is not supported, setup won't continue."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}-${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+}
+
+# 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+arch_runCommandWithTimeout() {
+  local commandTimeout="${1}"
+  shift
+  local resultFile="/tmp/smartagent_commandResult_$$"
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local loopErrorsFile="/tmp/smartagent_loopErrors_$$"
+
+  toLogInfo "Executing $* with timeout ${commandTimeout} seconds"
+  (
+    "$@" >${resultFile} 2>${errorFile} &
+    child=$!
+    while [ "${commandTimeout}" -gt 0 ]; do
+      toLogInfo "Time left: ${commandTimeout} s,  pid: ${child}"
+      sleep 1
+      kill -0 "${child}" 2>/dev/null || exit 0
+      commandTimeout=$((commandTimeout - 1))
+    done
+
+    # Be nice, post SIGTERM first.
+    # The 'exit 0' below will be executed if any preceeding command fails.
+    toLogInfo "Killing ${child} with SIGTERM"
+    kill -s 15 "${child}" && kill -0 "${child}" || exit 0
+    sleep 1
+    toLogWarning "Killing ${child} with SIGKILL"
+    kill -s 9 "${child}"
+  ) 2>"${loopErrorsFile}"
+
+  local errorOutput="$(cat "${errorFile}")"
+  local commandOutput="$(cat "${resultFile}")"
+  local loopErrors="$(cat "${loopErrorsFile}")"
+  ${SAFE_RM_EXEC} -f "${errorFile}" "${resultFile}" "${loopErrorsFile}"
+
+  if [ -n "${errorOutput}" ]; then
+    toLogInfo "Failed to execute command, error output: ${errorOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf ""
+    return 1
+  else
+    toLogInfo "Command executed successfully, output: ${commandOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf '%s' "${commandOutput}"
+    return 0
+  fi
+}
+
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+arch_preloadTest() {
+  if [ "${SYSTEM_LIB32}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "32"
+    local returnCode=$?
+    if [ ${returnCode} -ne 0 ]; then
+      return ${returnCode}
+    fi
+  fi
+
+  if [ "${SYSTEM_LIB64}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "64"
+  fi
+}
+
+arch_checkEnvironmentConfiguration() {
+  if ! stat --format='%t,%T' /dev/null | grep -q "1,3"; then
+    toLogInfo "$(stat /dev/null)"
+    toConsoleError "Installer detected corruption of '/dev/null': Not a character device"
+    return "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  return 0
+}
+
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+initLog() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    createLogDirsIfMissing
+  fi
+
+  toLogInfo "Uninstaller started."
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+  logBasicStartupInformation "${@}"
+}
+
+finishUninstaller() {
+  removeInstallerLockFile
+  if [ -z "$(ls -A ${INSTALL_DIR})" ];then
+    ${SAFE_RM_EXEC} -df ${INSTALL_DIR}
+    ${SAFE_RM_EXEC} -f ${CURRENT_VERSION_LINK}
+  fi
+  if [ $1 -eq 0 ]; then
+    toConsoleInfo "Uninstaller finished successfully"
+  else
+    toConsoleInfo "Uninstaller finished failed"
+  fi
+  exit "${EXIT_CODE_OK}"
+}
+
+setLogFilePath() {
+    #== - 反安装日志: uninstallation-{timestamp}.log 如: uninstallation-20220601110912.log
+    readonly LOG_FILE="${INSTALLER_LOG_DIR}/uninstallation-"$(date -u +"%Y%m%d%H%M%S")".log"
+}
+
+removeScriptsFromAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+
+  if ! runAutostartAddingTool "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"; then
+    toConsoleError "Failed to remove ${SERVICE_SCRIPT_FILE} from autostart. For details, see ${LOG_FILE}"
+  fi
+}
+
+removeUnitFromSystemd() {
+  local unit=${1}
+  if [ ! -e "${SYSTEMD_UNIT_FILES_DIR}/${unit}" ]; then
+    toLogInfo "${unit} does not exist and will not be removed from ${INIT_SYSTEM_SYSTEMD} autostart"
+    return
+  fi
+
+  executeSystemctlCommand disable "${unit}"
+  ${SAFE_RM_EXEC} -f "${SYSTEMD_UNIT_FILES_DIR}/${unit}"
+}
+
+removeSystemvAutostart() {
+  toConsoleInfo "Removing ${BRAND_AGENT_PRODUCT_NAME} from autostart"
+
+  #Order of checking is important
+  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/bin/update-rc.d -f " "remove"
+  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/sbin/update-rc.d -f " "remove"
+  elif [ -x /sbin/chkconfig ]; then #RedHat
+    removeScriptsFromAutostart "/sbin/chkconfig --del "
+  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+    removeScriptsFromAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+  elif [ "${ARCH_ARCH}" = "AIX" ]; then
+    #== 不存在
+    arch_removeAutostart
+  else
+    toConsoleError "Couldn't remove ${BRAND_AGENT_PRODUCT_NAME} from autostart."
+  fi
+}
+
+removeSystemdAutostart() {
+  removeUnitFromSystemd "${SYSTEMD_UNIT_FILE_AGENT}"
+  executeSystemctlCommand daemon-reload ""
+}
+
+stopAgentService() {
+  if [ -s "${AGENT_PID_FILE}" ]; then
+    toLogInfo "Stop ${AGENT_PROC}..."
+    commandErrorWrapper ${AGENT_BIN_DIR}/agentctl stop
+  fi
+}
+
+removeSystemvInitScripts() {
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${SERVICE_SCRIPT_FILE} from ${INIT_DIR}"
+  ${SAFE_RM_EXEC} -f "${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+}
+
+removeAutostartScripts() {
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSV}" ]; then
+    removeSystemvAutostart
+    removeSystemvInitScripts
+  else
+    removeSystemdAutostart
+  fi
+}
+
+createUninstallInfoFile() {
+  local reason
+  if [ "${IS_UPGRADING}"x = "true"x ]; then
+    reason="upgrade"
+  else
+    reason="uninstall"
+  fi
+
+  toLogInfo "Creating uninstall.info file: ${reason}"
+}
+
+initializeUninstaller() {
+  local installerPid=
+  local skipRootPrivilegesCheck=
+
+  if [ $# -eq 1 ]; then
+    installerPid="${1}"
+  fi
+  if [ $# -eq 2 ]; then
+     installerPid="${1}"
+     skipRootPrivilegesCheck="${2}"
+  fi
+
+  setPATH
+
+  #setLogFilePath "${installerPid}"
+  setLogFilePath
+
+  if [ "${installerPid}" ] && [ "${installerPid}"x != "online"x ]; then
+    IS_UPGRADING="true"
+  else
+    IS_UPGRADING="false"
+  fi
+  toConsoleInfo "IS_UPGRADING :${IS_UPGRADING}"
+
+  if [ "${skipRootPrivilegesCheck}"x = "true"x ] && [ "${IS_UPGRADING}"x = "true"x ]; then
+    toConsoleInfo "Skipping root privileges check"
+  else
+    if ! checkRootAccess; then
+      toConsoleError "You must run uninstaller with root privileges"
+      exit "${EXIT_CODE_GENERIC_ERROR}"
+    fi
+  fi
+
+  initLog "${@}"
+
+  toLogInfo "Launched during upgrade: ${IS_UPGRADING}"
+  createUninstallInfoFile
+
+  configureSignalHandling "finishUninstaller"
+
+  if [ "${IS_UPGRADING}"x = "false"x ] && [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    if isAnotherInstallerRunning; then
+      exit "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}"
+    fi
+    createInstallerLockFile
+  fi
+
+  if [ ! -f ${PERL_FILE} ]; then
+    toConsoleInfo "/usr/bin/perl file not found so ${SAFE_RM_EXEC} is used."
+  fi
+
+  toConsoleInfo "Using ${SAFE_RM_EXEC} to uninstall"
+}
+
+getVolumeStorageAgentInstallDir() {
+  readLink "$(dirname "${0}")"
+}
+
+cleanUpSELinux() {
+  if [ ! "${ARCH_HAS_SELINUX}" ] || ! isAvailable semodule; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${BRAND_FORMAL_NAME} SELinux policies, this may take a while..."
+}
+
+cleanUpFilesDuringUninstall() {
+	toLogInfo "This is a full uninstaller, cleaning up rest of the files"
+
+  local fileNames="${AGENT_CONF_DIR} ${AGENT_BIN_DIR} ${LOG_DIR} ${AGENT_SCRIPTS_DIR} ${AGENT_LIBS_DIR} ${AGENT_AGENTS_DIR} ${AGENT_RUNTIME_DIR}"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${fileName}"
+  done
+
+  fileNames="uninstall.sh installer.version heartbeat"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${INSTALL_DIR}/${fileName}"
+  done
+
+}
+
+handleRegularUninstaller() {
+
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  if ! setLocationOfInitScripts; then
+    toLogWarning "Cannot determine location of init scripts."
+  fi
+
+  stopAgentService
+
+  cleanUpFilesDuringUninstall
+
+}
+
+main() {
+  initializeUninstaller "$@"
+  handleRegularUninstaller "$@"
+#  finishUninstaller
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+main "$@"

二进制
dist/x86_64/scripts/xzdec


+ 12 - 0
ebpftracer/bindata_linux_arm64.go

@@ -6,13 +6,25 @@ package ebpftracer
 import (
 	"embed"
 	"fmt"
+	"github.com/coroot/coroot-node-agent/flags"
 	"golang.org/x/mod/semver"
+	"os"
 )
 
 //go:embed ebpf/bin/arm64
 var binData embed.FS
 
 func EbpfCode(v string) (string, []byte, error) {
+	if *flags.EbpfFilePath != "" {
+		filePath := *flags.EbpfFilePath
+		// Construct the full path to the desired file
+		data, err := os.ReadFile(filePath)
+		if err != nil {
+			return "", nil, err
+		}
+		return filePath, data, nil
+	}
+
 	var filePath string
 	var err error
 	versions := []string{"v5.12", "v5.6", "v4.20", "v4.16"}

+ 1 - 0
ebpftracer/ebpf/config.h

@@ -64,6 +64,7 @@ enum {
 	PROG_DATA_JAVA_FIND_HOST_UP_IDX,
 	PROG_DATA_JAVA_BUILD_HEADER_UP_IDX,
 	PROG_DATA_GO_UPDATE_HEADER_UP_IDX,
+	PROG_GRPC_SERVER_PROCESS_HEADERS_UP_IDX,
 	PROG_UP_NUM
 };
 

+ 10 - 3
ebpftracer/ebpf/ebpf.c

@@ -51,13 +51,20 @@
 #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/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/go/mq/kafka/producer.probe.bpf.c"
+#include "utrace/go/mq/kafka/consumer.probe.bpf.c"
 
 #include "utrace/java/net/server.probe.bpf.c"
 #include "utrace/java/net/client.probe.bpf.c"
@@ -66,8 +73,8 @@
 #if defined(__x86_64__)
 //TODO 支持kernel-4.18+后打开
 #if __KERNEL_FROM >= 512
-#include "utrace/netcore/net/server.probe.bpf.c"
-#include "utrace/netcore/net/client.probe.bpf.c"
+// #include "utrace/netcore/net/server.probe.bpf.c"
+// #include "utrace/netcore/net/client.probe.bpf.c"
 #endif
 #endif
 

+ 65 - 3
ebpftracer/ebpf/include/apm_trace.h

@@ -36,6 +36,20 @@
 //#define CW_STREAM_HEADER_LEN 135 // CW_HEADER_KEY_LENGTH + 2 + CW_HEADER_VAL_LENGTH + 2 + 1
 #define CW_STREAM_HEADER_LEN (CW_HEADER_KEY_LENGTH + 2 + CW_HEADER_VAL_LENGTH + 2 + 1)
 
+// cwother
+// 格式:NN:ParentServiceName:MM[:ParentServiceSysTag]
+// NN: 2位数字(ASCII),表示 ParentServiceName 的长度(0-32)
+// MM: 2位数字(ASCII),表示 ParentServiceSysTag 的长度(0-32)
+// 当 MM=00 时,格式为 NN:ParentServiceName:00(不包含最后的冒号和SysTag)
+#define CW_SYS_HEADER_KEY_LENGTH 7
+#define CW_SYS_HEADER_KEY_VAL "cwother"
+#define CW_SYS_HEADER_KEY_UFIRST_VAL "Cwother"
+#define CW_SYS_LEN_PREFIX_LENGTH 2  // 每个长度字段的长度(NN 或 MM)
+#define CW_SYS_HEADER_VAL_LENGTH 76
+
+#define MAX_MQ_TOPIC_SIZE 256  // Max MQ topic size (e.g., Kafka topic)
+#define MAX_MQ_KEY_SIZE 256     // Max MQ key size (e.g., Kafka message key)
+
 /***********************************************************
  * Trace struct
  ***********************************************************/
@@ -60,6 +74,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];
@@ -75,7 +93,15 @@ struct apm_span_context {
 	unsigned char trace_id[APM_TRACE_ID_SIZE];
 	unsigned char assumed_app_id[APM_ASSUMED_APP_ID_SIZE];
 	unsigned char span_id[APM_SPAN_ID_SIZE];
+	unsigned char sysvs[CW_SYS_HEADER_VAL_LENGTH];
 };
+
+// MQ 信息结构体(用于 Kafka、RabbitMQ 等消息队列)
+struct mq_info_t {
+	char topic[MAX_MQ_TOPIC_SIZE];
+	char key[MAX_MQ_KEY_SIZE];
+};
+
 /*
 	 * Whether traceID is zero ?
 	 * For the client to actively send request, set traceID to zero.
@@ -99,10 +125,46 @@ 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];
-	}
+//	for (int i = 0; i < size; i++) {
+//		dst[i] = src[i];
+//	}
+	__builtin_memcpy(dst, src, size);
 }
 
+// 设置 trace 开始标记
+// 用法: SET_TRACE_START(e, PROTOCOL_HTTP)
+#define SET_TRACE_START(e, p) do { \
+	(e)->trace_start = PROTOCOL_TRACE; \
+	(e)->trace_end = 0; \
+	(e)->protocol = (p); \
+} while (0)
+
+// 设置 trace 结束标记
+// 用法: SET_TRACE_END(e, PROTOCOL_HTTP)
+#define SET_TRACE_END(e, p) do { \
+	(e)->trace_start = 0; \
+	(e)->trace_end = PROTOCOL_TRACE; \
+	(e)->protocol = (p); \
+} while (0)
+
+#define SET_TRACE_METHOD(e) do { \
+	(e)->trace_start = 0; \
+	(e)->trace_end = 0; \
+} while (0)
+
 #endif //EUSPACES_APM_TRACE_H

+ 10 - 10
ebpftracer/ebpf/include/socket_trace.h

@@ -303,16 +303,16 @@ struct go_interface {
 	void *ptr;
 };
 
-struct go_slice {
-	void *ptr;
-	unsigned long long len;
-	unsigned long long cap;
-};
-
-struct go_string {
-	const char *ptr;
-	unsigned long long len;
-};
+//struct go_slice {
+//	void *ptr;
+//	unsigned long long len;
+//	unsigned long long cap;
+//};
+
+//struct go_string {
+//	const char *ptr;
+//	unsigned long long len;
+//};
 
 struct tls_conn {
 	int fd;

+ 30 - 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,
 };
 
@@ -199,10 +201,38 @@ struct ebpf_proc_info {
 	__u64 path_ptr_pos;
 	__u64 status_code_pos;
 	__u64 request_host_pos;
+	__u64 url_host_pos;  // url.URL.Host field offset
 	__u64 request_proto_pos;
 	__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;
+	__u8 is_new_frame_pos;
+	// io writer
+	__u64 io_writer_buf_ptr_pos;
+	__u64 io_writer_n_pos;
+	//Kafka
+	__u64 kafka_message_key_pos;
+	__u64 kafka_message_topic_pos;
+	__u64 kafka_message_headers_pos;
+	__u64 kafka_message_time_pos;
+	__u64 kafka_message_partition_pos;  // Message.Partition field offset
+	__u64 kafka_message_offset_pos;     // Message.Offset field offset
+	__u64 kafka_message_value_pos;      // Message.Value field offset
+	__u64 kafka_writer_topic_pos;
+	__u64 kafka_writer_addr_pos;  // Writer.Brokers field offset
+	__u64 kafka_reader_config_pos;      // Reader.Config field offset
+	__u64 kafka_reader_config_group_id_pos;  // Reader.Config.GroupID field offset
+	__u64 kafka_reader_config_topics_pos;    // Reader.Config.Topics field offset
+	// net.TCPAddr offsets
+	__u64 tcp_addr_ip_offset;    // net.TCPAddr.IP field offset
+	__u64 tcp_addr_port_offset;  // net.TCPAddr.Port field offset
+	// cwother header value: {app_name_len}:app_name:{SysTagLen}[:SysTag]
+	unsigned char sysvc[CW_SYS_HEADER_VAL_LENGTH];  // CW_SYS_HEADER_VAL_LENGTH
 } __attribute__((packed));
 
 enum {

+ 42 - 4
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));
@@ -63,6 +78,20 @@ struct {
 	__uint(max_entries, 10240);
 } pid_of_connection_ptr_maps SEC(".maps");
 
+//struct {
+//	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+//	__uint(key_size, sizeof(__u32));
+//	__uint(value_size, sizeof(struct apm_span_context));
+//	__uint(max_entries, 1);
+//} parent_span_context_storage_map SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(__u32));
+	__uint(value_size, sizeof(struct apm_span_context));
+	__uint(max_entries, 1);
+} cw_parent_span_context_storage_map SEC(".maps");
+
 static __inline __attribute__((__always_inline__))
 struct apm_trace_key_t get_apm_trace_key(__u64 timeout, bool is_socket_io) {
 	__u64 pid_tgid = bpf_get_current_pid_tgid();
@@ -282,8 +311,12 @@ struct apm_trace_info_t *get_trace_info_by_fd(__u32 pid, __u32 fd) {
 static __inline __attribute__((__always_inline__))
 void cw_save_parent_tracking_span(struct apm_span_context *sc) {
 	struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
-	long err = 0;
-	err = bpf_map_update_elem(&apm_parent_span_context_map, &trace_key, sc, BPF_ANY);
+//	__u64 pid_tgid = bpf_get_current_pid_tgid();
+//	cw_bpf_debug("[pid:%d][goid:%d] save psc header", (__u32) (pid_tgid >> 32), trace_key.goid);
+//	for (int i = 0; i < APM_APP_ID_SIZE; i++) {
+//		cw_bpf_debug("trace_enter_write - span_id_from = %02x", sc->app_id[i]);
+//	}
+	long err = bpf_map_update_elem(&apm_parent_span_context_map, &trace_key, sc, BPF_ANY);
 	if (err != 0) {
 		cw_bpf_debug("Failed to update tracked_spans map: %ld", err);
 		return;
@@ -389,8 +422,13 @@ void cw_save_current_tracking_span(struct apm_span_context *sc) {
 
 static __inline __attribute__((__always_inline__))
 struct apm_span_context *cw_get_current_tracking_span(struct apm_trace_info_t *trace_info) {
-	struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
-//	bpf_printk("[get cw sc]%d goid:%d",trace_key.goid,get_current_goroutine());
+	struct apm_trace_key_t trace_key = {0};
+	if (trace_info){
+		trace_key = trace_info->trace_key;
+	}
+//	bpf_printk("[get cw sc]%d",trace_key.goid);
+//	bpf_printk("[get cw sc2]%d",trace_info->trace_key.goid);
+
 	struct apm_span_context *apm_sc = {0};
 	struct apm_span_context *span_contexts = bpf_map_lookup_elem(&apm_current_span_context_map, &trace_key);
 //	bpf_printk("-------");

+ 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;
-}
-
+}

+ 1 - 1
ebpftracer/ebpf/l7/gotls.c

@@ -70,5 +70,5 @@ int go_crypto_tls_read_exit(struct pt_regs *ctx) {
     __u64 goroutine_id = GOROUTINE(ctx);
     __u64 id = pid << 32 | goroutine_id | IS_TLS_READ_ID;
     long int ret = GO_PARAM1(ctx);
-    return trace_exit_read(ctx, id, pid, 1, ret);
+    return trace_exit_read_up(ctx, id, pid, 1, ret);
 }

+ 108 - 66
ebpftracer/ebpf/l7/l7.c

@@ -14,6 +14,8 @@
 #define PROTOCOL_DUBBO2    12
 #define PROTOCOL_DNS       13
 #define PROTOCOL_DM        14
+#define PROTOCOL_MARIADB   15
+#define PROTOCOL_GRPC      16
 
 
 
@@ -29,6 +31,7 @@
 #define METHOD_HTTP2_CLIENT_FRAMES  5
 #define METHOD_HTTP2_SERVER_FRAMES  6
 
+#define ERROR_MSG_PAYLOAD_SIZE 128
 #define MAX_PAYLOAD_SIZE 1024 // must be power of 2
 #define TRUNCATE_PAYLOAD_SIZE(size) ({                                  \
     size = MIN(size, MAX_PAYLOAD_SIZE-1);                               \
@@ -59,7 +62,6 @@
 #include "dubbo2.c"
 #include "dns.c"
 #include "dm.c"
-#include "apm_trace.c"
 
 // go type l7Event struct && type RequestData struct
 struct l7_event {
@@ -78,6 +80,7 @@ struct l7_event {
 	__u64 end_at;
     __u32 trace_start;
     __u32 trace_end;
+    __u32 trace_type;           // 0: normal, 1: grpc-server, 2: https 3 mq
     __u32 event_count;
     __u16 sport;
     __u16 dport;
@@ -85,6 +88,7 @@ struct l7_event {
     __u8 daddr[16];
     __u16 component_sport;
     __u16 component_dport;
+    __u16 is_tls;
     __u8 component_saddr[16];
     __u8 component_daddr[16];
 	unsigned char assumed_app_id[APM_ASSUMED_APP_ID_SIZE];
@@ -94,8 +98,15 @@ struct l7_event {
 	unsigned char instance_id_from[APM_INSTANCE_ID_SIZE];
 	unsigned char app_id_from[APM_APP_ID_SIZE];
 	unsigned char span_id_from[APM_SPAN_ID_SIZE];
+    unsigned char type_from[APM_TYPE_FROM_SIZE];
+    unsigned char sysvc_from[76];  // cwother header value: NN:ParentServiceName:MM:ParentServiceSysTag (CW_SYS_HEADER_VAL_LENGTH)
+    unsigned char target_addr[64];
 
+    // 错误消息字段
+    unsigned char error_message[ERROR_MSG_PAYLOAD_SIZE];
 //    __u32 test_id;
+    // MQ 相关信息(用于 Kafka、RabbitMQ 等消息队列)
+    struct mq_info_t mq;
     char payload[MAX_PAYLOAD_SIZE];
 } ;
 
@@ -365,6 +376,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);
@@ -414,6 +427,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);
@@ -427,14 +441,18 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
 	    // parent sc
 		struct apm_span_context *cw_psc = cw_get_parent_tracking_span_by_trace_key(start_trace_info->trace_key);
 	    if(cw_psc){
+//		    bpf_printk("[pid:%d][goid:%d] find header",pid,start_trace_info->trace_key.goid);
 		    cw_copy_byte_arrays(cw_psc->trace_id, e->trace_id_from, APM_TRACE_ID_SIZE);
 		    cw_copy_byte_arrays(cw_psc->assumed_app_id, e->called_id, APM_ASSUMED_APP_ID_SIZE);
 		    cw_copy_byte_arrays(cw_psc->instance_id, e->instance_id_from, APM_INSTANCE_ID_SIZE);
 		    cw_copy_byte_arrays(cw_psc->app_id, e->app_id_from, APM_APP_ID_SIZE);
 		    cw_copy_byte_arrays(cw_psc->span_id, e->span_id_from, APM_SPAN_ID_SIZE);
-//		    for (int i = 0; i < APM_TRACE_ID_SIZE; i++) {
-//			    cw_bpf_debug("trace_enter_write - span_id_from = %02x", e->span_id_from[i]);
-//		    }
+            cw_copy_byte_arrays(cw_psc->type_from, e->type_from, APM_TYPE_FROM_SIZE);
+            cw_copy_byte_arrays(cw_psc->sysvs, e->sysvc_from, CW_SYS_HEADER_VAL_LENGTH);
+//		    bpf_printk("trace_end %s",cw_psc->sysvs);
+		    // for (int i = 0; i < APM_TYPE_FROM_SIZE; i++) {
+			//     bpf_printk("trace_enter_write - type_from = %02x", e->type_from[i]);
+		    // }
 	    }
         struct l7_request *req = bpf_map_lookup_elem(&active_l7_requests, &k);
         if (!req)
@@ -442,22 +460,23 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
 	        cw_clear_trace(pid, tid, fd);
             return 0;
         }
+	    SET_TRACE_END(e, PROTOCOL_HTTP);
 	    e->start_at = req->ns;
 	    cw_bpf_debug("req->ns:%llu",req->ns);
 	    e->end_at = bpf_ktime_get_ns();
         e->duration = e->end_at - e->start_at;
 //        cw_bpf_debug("[Response][HTTP]:duration->ns:%d\n",e->duration);
-        e->protocol = PROTOCOL_TRACE;
         e->status = http_status;
         e->pid = k.pid;
         e->fd = k.fd;
         // e->connection_timestamp = get_connection_timestamp(k.pid, k.fd);
-        e->trace_start = 0;
-        e->trace_end = 1;
+        e->trace_type = 0;
         e->trace_id = trace_id;
         e->payload_size = size;
         e->event_count = event_count;
         COPY_PAYLOAD(e->payload, size, payload);
+        e->is_tls = is_tls;
+
         bpf_map_delete_elem(&active_l7_requests, &k);
 		// 清除事件计数
 	    bpf_map_delete_elem(&trace_event_count_heap, &trace_id);
@@ -586,6 +605,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);
@@ -660,26 +680,33 @@ 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);
         if (prev_req && prev_req->protocol == PROTOCOL_KAFKA) {
             req->ns = prev_req->ns;
         }
-    } else if (looks_like_http2_frame(payload, size, METHOD_HTTP2_CLIENT_FRAMES)) {
-        struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
-        if (!e) {
-            return 0;
-        }
-        e->protocol = PROTOCOL_HTTP2;
-        e->method = METHOD_HTTP2_CLIENT_FRAMES;
-        e->duration = bpf_ktime_get_ns();
-        e->payload_size = size;
-        e->trace_id = get_apm_trace_id(pid, tid);
-        COPY_PAYLOAD(e->payload, size, payload);
-        send_event(ctx, e, cid, conn);
-        return 0;
+    // } else if (looks_like_http2_frame(payload, size, METHOD_HTTP2_CLIENT_FRAMES)) {
+    //     struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+    //     if (!e) {
+    //         return 0;
+    //     }
+    //     e->protocol = PROTOCOL_HTTP2;
+    //     e->method = METHOD_HTTP2_CLIENT_FRAMES;
+    //     e->duration = bpf_ktime_get_ns();
+    //     e->payload_size = size;
+    //     e->trace_id = get_apm_trace_id(pid, tid);
+    //     COPY_PAYLOAD(e->payload, size, payload);
+    //     send_event(ctx, e, cid, conn);
+    //     return 0;
     } else if (is_dubbo2_request(payload, size)) {
         req->protocol = PROTOCOL_DUBBO2;
     } else if (is_dns_request(payload, size, &k.stream_id)) {
@@ -699,16 +726,11 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
 
 static inline __attribute__((__always_inline__))
 int trace_enter_read(__u64 id, __u32 pid, __u64 fd, char *buf, __u64 *ret, __u64 iovlen) {
-    // struct connection_id cid = {};
-    // cid.pid = pid;
-    // cid.fd = fd;
 
-    // struct connection *conn = bpf_map_lookup_elem(&active_connections, &cid);
-    // if (!conn) {
-    //     // cw_bpf_debug("trace_enter_read no conn\n");
-    //     return 0;
-    // }
-    struct read_args args = {};
+	if (load_filter_pid() != 0 && pid != load_filter_pid()) {
+		return 0;
+	}
+	struct read_args args = {};
     args.fd = fd;
     args.buf = buf;
     args.ret = ret;
@@ -717,18 +739,21 @@ int trace_enter_read(__u64 id, __u32 pid, __u64 fd, char *buf, __u64 *ret, __u64
     return 0;
 }
 
+// 通用的trace_exit_read逻辑,通过参数控制是否执行bpf_tail_call
 static inline __attribute__((__always_inline__))
-int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret) {
+int trace_exit_read_common(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret, int tp_tail_call) {
     __u32 tid =  (__u32)id;
 
     if (load_filter_pid() != 0 && pid != load_filter_pid()) {
         return 0;
     }
 
+
     struct read_args *args = bpf_map_lookup_elem(&active_reads, &id);
     if (!args) {
         return 0;
     }
+
     struct l7_request_key k = {};
     k.pid = pid;
     k.fd = args->fd;
@@ -763,10 +788,11 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
         }
     }
 
-    struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+	struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
     if (!e) {
         return 0;
     }
+
     e->fd = k.fd;
     e->pid = k.pid;
     e->protocol = PROTOCOL_UNKNOWN;
@@ -775,27 +801,17 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
     e->statement_id = 0;
     e->payload_size = 0;
     e->trace_id = 0;
+    e->is_tls = is_tls;
+
 	__u8 b[8];
-	bpf_read(payload, b);
-//    __u32 k0 = 0;
-//    struct member_fields_offset *offset = members_offset__lookup(&k0);
-//    if (!offset)
-//        return -1;
-//    void *sk = get_socket_from_fd(args->fd, offset);
-//    struct conn_info_t *conn_info, __conn_info = { 0 };
-//    conn_info = &__conn_info;
-////
-//    init_conn_info(id >> 32, args->fd, &__conn_info, sk, offset);
-////
-//    infer_dns_message(payload, (int)PT_REGS_RC((struct pt_regs *)ctx),
-//                      conn_info);
+//	bpf_read(payload, b);
 
     struct connection_id cid = {};
     cid.pid = pid;
     cid.fd = args->fd;
     // 被调用方http入口
     // 作为服务端在走。coroot 原有逻辑是没有的
-    if (is_http_request(payload)) {
+	if (is_http_request(payload)) {
 
         //处理http请求之前,确认进程信息是否存在
         struct ebpf_proc_info *proc_info = bpf_map_lookup_elem(&proc_info_map, &pid);
@@ -840,13 +856,12 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
 //	    __u64 uid_base = bpf_ktime_get_ns();
 //	    trace_info.trace_id = bpf_get_current_pid_tgid() + uid_base;
 
-        e->trace_start = 1;
-        e->trace_end = 0;
-        e->protocol = PROTOCOL_TRACE;
+        SET_TRACE_START(e, PROTOCOL_HTTP);
+        e->trace_type = 0;
         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);
 
@@ -867,10 +882,12 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
         //     __sync_fetch_and_add(&accept_conn->bytes_received, total_size);
         // }
 
-        //bpf_tail_call PROGUP(l7_http_request)
+		//bpf_tail_call PROGUP(l7_http_request)
         cw_bpf_debug("======== PROG_DATA_L7_HTTP_TRACE_ID_UP_IDX ========== __KERNEL_FROM < 512 pid:[%d] ",tid);
-        bpf_tail_call(ctx, &NAME(progs_jmp_tp_map), PROG_DATA_L7_HTTP_TRACE_ID_TP_IDX);
-         return 0;
+        if (tp_tail_call && proc_info->code_type != CodeTypeGo) {
+            bpf_tail_call(ctx, &NAME(progs_jmp_tp_map), PROG_DATA_L7_HTTP_TRACE_ID_TP_IDX);
+        }
+        return 0;
     }
 
     struct connection *conn = bpf_map_lookup_elem(&active_connections, &cid);
@@ -929,6 +946,15 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
             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;
@@ -991,9 +1017,14 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
         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);
@@ -1020,12 +1051,12 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
             char resp_combined_packet[5];
             bpf_probe_read(resp_combined_packet,MYSQL_PACKAGE_HEADER_LEN, resp_packet_header);
             bpf_probe_read(&resp_combined_packet[4],1, payload);
-            response = is_mysql_response(resp_combined_packet,sizeof(resp_combined_packet), req->request_type, &e->statement_id, &e->status);
+            response = is_mysql_response(resp_combined_packet,sizeof(resp_combined_packet), req->request_type, &e->statement_id, &e->status, e->error_message);
             if(response) {
                  bpf_map_delete_elem(&active_l7_requests_mysql_resp_header_ctx, &k);
             }
         } else {
-            response = is_mysql_response(payload, ret, req->request_type, &e->statement_id, &e->status);
+            response = is_mysql_response(payload, ret, req->request_type, &e->statement_id, &e->status, e->error_message);
         }
 		if (req->request_type == MYSQL_COM_STMT_PREPARE) {
 			e->method = METHOD_STATEMENT_PREPARE;
@@ -1212,10 +1243,21 @@ int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret)
 	e->end_at = bpf_ktime_get_ns();
 	e->start_at = req->ns;
     e->duration = e->end_at - e->start_at;
+	SET_TRACE_METHOD(e);
     send_event(ctx, e, cid, conn);
     return 0;
 }
 
+static inline __attribute__((__always_inline__))
+int trace_exit_read_tp(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret) {
+    return trace_exit_read_common(ctx, id, pid, is_tls, ret, 1); // enable_tail_call = 1
+}
+
+static inline __attribute__((__always_inline__))
+int trace_exit_read_up(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret) {
+    return trace_exit_read_common(ctx, id, pid, is_tls, ret, 0); // enable_tail_call = 0
+}
+
 SEC("tracepoint/syscalls/sys_enter_write")
 int sys_enter_write(struct trace_event_raw_sys_enter_rw__stub* ctx) {
     return trace_enter_write(ctx, ctx->fd, 0, ctx->buf, ctx->size, 0);
@@ -1267,14 +1309,14 @@ SEC("tracepoint/syscalls/sys_enter_read")
 int sys_enter_read(struct trace_event_raw_sys_enter_rw__stub* ctx) {
     __u64 id = bpf_get_current_pid_tgid();
     __u32 pid = id >> 32;
-    return trace_enter_read(id, pid, ctx->fd, ctx->buf, 0, 0);
+	return trace_enter_read(id, pid, ctx->fd, ctx->buf, 0, 0);
 }
 
 SEC("tracepoint/syscalls/sys_enter_readv")
 int sys_enter_readv(struct trace_event_raw_sys_enter_rw__stub* ctx) {
     __u64 id = bpf_get_current_pid_tgid();
     __u32 pid = id >> 32;
-    return trace_enter_read(id, pid, ctx->fd, ctx->buf, 0, ctx->size);
+	return trace_enter_read(id, pid, ctx->fd, ctx->buf, 0, ctx->size);
 }
 
 SEC("tracepoint/syscalls/sys_enter_recvmsg")
@@ -1285,42 +1327,42 @@ int sys_enter_recvmsg(struct trace_event_raw_sys_enter_rw__stub* ctx) {
         return 0;
     }
     __u32 pid = id >> 32;
-    return trace_enter_read(id, pid, ctx->fd, (char*)msghdr.msg_iov, 0, msghdr.msg_iovlen);
+	return trace_enter_read(id, pid, ctx->fd, (char *) msghdr.msg_iov, 0, msghdr.msg_iovlen);
 }
 
 SEC("tracepoint/syscalls/sys_enter_recvfrom")
 int sys_enter_recvfrom(struct trace_event_raw_sys_enter_rw__stub* ctx) {
     __u64 id = bpf_get_current_pid_tgid();
     __u32 pid = id >> 32;
-    return trace_enter_read(id, pid, ctx->fd, ctx->buf, 0, 0);
+	return trace_enter_read(id, pid, ctx->fd, ctx->buf, 0, 0);
 }
 
 SEC("tracepoint/syscalls/sys_exit_read")
 int sys_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     __u64 pid_tgid = bpf_get_current_pid_tgid();
     __u32 pid = pid_tgid >> 32;
-    return trace_exit_read(ctx, pid_tgid, pid, 0, ctx->ret);
+    return trace_exit_read_tp(ctx, pid_tgid, pid, 0, ctx->ret);
 }
 
 SEC("tracepoint/syscalls/sys_exit_readv")
 int sys_exit_readv(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     __u64 pid_tgid = bpf_get_current_pid_tgid();
     __u32 pid = pid_tgid >> 32;
-    return trace_exit_read(ctx, pid_tgid, pid, 0, ctx->ret);
+    return trace_exit_read_tp(ctx, pid_tgid, pid, 0, ctx->ret);
 }
 
 SEC("tracepoint/syscalls/sys_exit_recvmsg")
 int sys_exit_recvmsg(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     __u64 pid_tgid = bpf_get_current_pid_tgid();
     __u32 pid = pid_tgid >> 32;
-    return trace_exit_read(ctx, pid_tgid, pid, 0, ctx->ret);
+    return trace_exit_read_tp(ctx, pid_tgid, pid, 0, ctx->ret);
 }
 
 SEC("tracepoint/syscalls/sys_exit_recvfrom")
 int sys_exit_recvfrom(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     __u64 pid_tgid = bpf_get_current_pid_tgid();
     __u32 pid = pid_tgid >> 32;
-    return trace_exit_read(ctx, pid_tgid, pid, 0, ctx->ret);
+    return trace_exit_read_tp(ctx, pid_tgid, pid, 0, ctx->ret);
 }
 //
 //SEC("tracepoint/syscalls/sys_exit_recvfrom")

+ 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

+ 0 - 2
ebpftracer/ebpf/l7/mongo.c

@@ -12,7 +12,6 @@ struct mongo_header {
 
 static __always_inline
 int is_mongo_query(char *buf, __u64 buf_size) {
-	return 0;
     struct mongo_header h = {};
     if (buf_size < sizeof(h)) {
         return 0;
@@ -26,7 +25,6 @@ int is_mongo_query(char *buf, __u64 buf_size) {
 
 static __always_inline
 int is_mongo_response(char *buf, __u64 buf_size, __u8 partial) {
-	return 0;
     if (partial == 0 && buf_size == 4) { //partial read
         return 2;
     }

+ 26 - 6
ebpftracer/ebpf/l7/mysql.c

@@ -38,7 +38,7 @@ int is_mysql_query(char *buf, __u64 buf_size, __u8 *request_type) {
 }
 
 static __always_inline
-int is_mysql_response(char *buf, __u64 buf_size, __u8 request_type, __u32 *statement_id, __u32 *status) {
+int is_mysql_response(char *buf, __u64 buf_size, __u8 request_type, __u32 *statement_id, __u32 *status,unsigned char *error_message) {
     __u8 b[5];
     bpf_read(buf, b);
     if (b[3] < 1) { // sequence must be > 0
@@ -56,9 +56,29 @@ int is_mysql_response(char *buf, __u64 buf_size, __u8 request_type, __u32 *state
         *status = STATUS_OK;
         return 1;
     }
-    if (b[4] == MYSQL_RESPONSE_ERROR) {
-        *status = STATUS_FAILED;
-        return 1;
-    }
-    return 0;
+	if (b[4] == MYSQL_RESPONSE_ERROR) {
+		// MySQL错误包格式:
+		// packet_length(3) + packet_number(1) + 0xff(1) + error_code(2) + sql_state_marker(1) + sql_state(5) + error_message(variable)
+		__u16 error_code = 0;
+//		char error_message[128] = {};
+		__u8 error_message_len = 0;
+
+		// 读取错误代码(2字节,小端序)
+		if (buf_size >= 7) {
+			bpf_probe_read(&error_code, sizeof(error_code), buf + 5);
+			error_code = bpf_ntohs(error_code); // 转换为主机字节序
+		}
+
+		// 读取错误消息(从第13字节开始,跳过sql_state)
+		if (buf_size > 13) {
+			error_message_len = (__u8) (length - 9); // 总长度 - 头部(4) - 错误码(2) - sql_state(6)
+			bpf_probe_read(error_message, error_message_len, buf + 13);
+			error_message[error_message_len] = '\0'; // 确保字符串结束
+		}
+		// 输出错误信息到内核日志
+		cw_bpf_debug("MySQL Error %d: %s", error_code, error_message);
+		*status = STATUS_FAILED;
+		return 1;
+	}
+	return 0;
 }

+ 1 - 1
ebpftracer/ebpf/l7/openssl.c

@@ -141,5 +141,5 @@ int openssl_SSL_read_exit(struct pt_regs *ctx) {
     __u64 pid = pid_tgid >> 32;
     __u64 id = pid_tgid | IS_TLS_READ_ID;
     int ret = (int)PT_REGS_RC(ctx);
-    return trace_exit_read(ctx, id, pid, 1, ret);
+    return trace_exit_read_up(ctx, id, pid, 1, ret);
 }

+ 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)

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

@@ -0,0 +1,445 @@
+//
+// 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);
+    }
+    // 发送事件
+	SET_TRACE_METHOD(e);
+    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;
+    }
+
+    // 发送事件
+	SET_TRACE_METHOD(e);
+    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;
+}

+ 186 - 116
ebpftracer/ebpf/utrace/go/include/alloc.h

@@ -35,30 +35,73 @@ struct
 //    __uint(pinning, LIBBPF_PIN_BY_NAME);
 } alloc_map SEC(".maps");
 
+struct
+{
+	__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
+	__type(key, u64);
+	__type(value, u64);
+	__uint(max_entries, MAX_ENTRIES);
+//    __uint(pinning, LIBBPF_PIN_BY_NAME);
+} proc_alloc_map SEC(".maps");
+
+static __always_inline u64 get_area_start_ot(u64 start_addr,u64 end_addr,u64 total_cpus) {
+	s64 partition_size = (end_addr - start_addr) / total_cpus;  // Fixed: was (start_addr - end_addr)
+	u32 current_cpu = bpf_get_smp_processor_id();
+	s32 start_index = 0;
+	u64 *start = (u64 *)bpf_map_lookup_elem(&alloc_map, &start_index);
+	if (start == NULL || *start == 0) {
+		u64 current_start_addr = start_addr + (partition_size * current_cpu);
+		bpf_map_update_elem(&alloc_map, &start_index, &current_start_addr, BPF_ANY);
+		return current_start_addr;
+	} else {
+		return *start;
+	}
+}
+
+static __always_inline u64 get_area_end_ot(u64 start,u64 start_addr,u64 end_addr,u64 total_cpus) {
+	s64 partition_size = (end_addr - start_addr) / total_cpus;
+	s32 end_index = 1;
+	u64 *end = (u64 *)bpf_map_lookup_elem(&alloc_map, &end_index);
+	if (end == NULL || *end == 0) {
+		u64 current_end_addr = start + partition_size;
+		bpf_map_update_elem(&alloc_map, &end_index, &current_end_addr, BPF_ANY);
+		return current_end_addr;
+	} else {
+		return *end;
+	}
+}
+
 static __always_inline u64 get_area_start(u64 start_addr,u64 end_addr)
 {
-    u32 k0 = 0;
+	u32 k0 = 0;
 	struct trace_conf_t *trace_conf = trace_conf_map__lookup(&k0);
 	if (!trace_conf) {
-        return 0;
+		return 0;
+	}
+	s64 partition_size = (end_addr - start_addr) / trace_conf->total_cpus;
+	u32 current_cpu = bpf_get_smp_processor_id();
+	s32 start_index = 0;
+
+	u64 alloc_map_index = ((u64)(bpf_get_current_pid_tgid() >> 32) << 32) | start_index;
+	u64 *start = (u64 *)bpf_map_lookup_elem(&proc_alloc_map, &alloc_map_index);
+	if (start == NULL || *start == 0)
+	{
+//	    bpf_printk("start == NULL || *start == 0");
+		u64 current_start_addr = start_addr + (partition_size * current_cpu);
+		bpf_map_update_elem(&proc_alloc_map, &alloc_map_index, &current_start_addr, BPF_ANY);
+		return current_start_addr;
+	}
+	else
+	{
+//	    bpf_printk("else return");
+		return *start;
 	}
-    s64 partition_size = (end_addr - start_addr) / trace_conf->total_cpus;
-    u32 current_cpu = bpf_get_smp_processor_id();
-    s32 start_index = 0;
-    u64 *start = (u64 *)bpf_map_lookup_elem(&alloc_map, &start_index);
-    if (start == NULL || *start == 0)
-    {
-        u64 current_start_addr = start_addr + (partition_size * current_cpu);
-        bpf_map_update_elem(&alloc_map, &start_index, &current_start_addr, BPF_ANY);
-        return current_start_addr;
-    }
-    else
-    {
-        return *start;
-    }
 }
 
-static __always_inline u64 get_area_end(u64 start,u64 start_addr,u64 end_addr)
+
+
+
+/*static __always_inline u64 get_area_end(u64 start,u64 start_addr,u64 end_addr)
 {
     u32 k0 = 0;
 	struct trace_conf_t *trace_conf = trace_conf_map__lookup(&k0);
@@ -78,8 +121,33 @@ static __always_inline u64 get_area_end(u64 start,u64 start_addr,u64 end_addr)
     {
         return *end;
     }
+}*/
+
+static __always_inline u64 get_area_end(u64 start,u64 start_addr,u64 end_addr)
+{
+	u32 k0 = 0;
+	struct trace_conf_t *trace_conf = trace_conf_map__lookup(&k0);
+	if (!trace_conf) {
+		return 0;
+	}
+	s64 partition_size = (end_addr - start_addr) / trace_conf->total_cpus;
+	s32 end_index = 1;
+	u64 alloc_map_index = ((u64)(bpf_get_current_pid_tgid() >> 32) << 32) | end_index;
+
+	u64 *end = (u64 *)bpf_map_lookup_elem(&proc_alloc_map, &alloc_map_index);
+	if (end == NULL || *end == 0)
+	{
+		u64 current_end_addr = start + partition_size;
+		bpf_map_update_elem(&proc_alloc_map, &alloc_map_index, &current_end_addr, BPF_ANY);
+		return current_end_addr;
+	}
+	else
+	{
+		return *end;
+	}
 }
 
+
 static __always_inline s32 bound_number(s32 num, s32 min, s32 max)
 {
     if (num < min)
@@ -93,75 +161,74 @@ static __always_inline s32 bound_number(s32 num, s32 min, s32 max)
     return num;
 }
 
-static __always_inline void *write_target_data(void *data, s32 size)
-{
-    if (!data || data == NULL)
-    {
-        return NULL;
-    }
-	u64 start_from_proc;
-	u64 end_from_proc;
+static __always_inline void *write_target_data(void *data, s32 size) {
 	__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 0;
+	}
+	u64 start_addr = info->start_addr;
+	u64 end_addr = info->end_addr;
+
+	if (!data || data == NULL) {
 		return NULL;
 	}
-	start_from_proc = info->start_addr;
-	end_from_proc = info->end_addr;
 
-	u64 start = get_area_start(start_from_proc, end_from_proc);
-    u64 end = get_area_end(start,start_from_proc,end_from_proc);
-    if (end - start < size)
-    {
-        cw_bpf_debug("reached end of CPU memory block, going to the start again");
-        s32 start_index = 0;
-        bpf_map_delete_elem(&alloc_map, &start_index);
-        start = get_area_start(start_from_proc, end_from_proc);
-    }
+	// Use get_area_start/get_area_end which support multi-process (use proc_alloc_map with process ID in key)
+	u64 start = get_area_start(start_addr, end_addr);
+	u64 end = get_area_end(start, start_addr, end_addr);
+	if (end - start < size) {
+		bpf_printk("reached end of CPU memory block, going to the start again");
+		s32 start_index = 0;
+		u64 alloc_map_index = ((u64)(bpf_get_current_pid_tgid() >> 32) << 32) | start_index;
+		bpf_map_delete_elem(&proc_alloc_map, &alloc_map_index);
+		start = get_area_start(start_addr, end_addr);
+	}
 
-    void *target = (void *)start;
-    size = bound_number(size, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
-    u64 page_offset = (u64)target & 0xFFF;
-    u64 dist_to_next_page = 4096 - page_offset;
-    if (dist_to_next_page < size)
-    {
-        target += dist_to_next_page;
-    }
-    u64 target_u = (u64)target;
-    if (target_u > end_from_proc || target_u < start_from_proc) {
-	    cw_bpf_debug("TARGET ADDRESS IS OUT OF BOUNDS: 0x%llx", target);
-        return NULL;
-    }
+	void *target = (void *)start;
+	size = bound_number(size, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
+	u64 page_offset = (u64)target & 0xFFF;
+	u64 dist_to_next_page = 4096 - page_offset;
+	if (dist_to_next_page < size) {
+		target += dist_to_next_page;
+	}
+	u64 target_u = (u64)target;
+	if (target_u > end_addr || target_u < start_addr) {
+		bpf_printk("TARGET ADDRESS IS OUT OF BOUNDS: 0x%llx", target);
+		return NULL;
+	}
 
-    long success = bpf_probe_write_user(target, data, size);
-    if (success == 0)
-    {
-        s32 start_index = 0;
-        // Update the start position of this chunk, taking into account possible adjustments
-        // we made to be page aligned
-        u64 updated_start = target_u + size;
-
-        // align updated_start to 8 bytes
-        if (updated_start % 8 != 0) {
-            updated_start += 8 - (updated_start % 8);
-        }
-
-        bpf_map_update_elem(&alloc_map, &start_index, &updated_start, BPF_ANY);
-        return target;
-    }
-    else
-    {
-	    cw_bpf_debug("failed to write to userspace, error code: %d, addr: %lx, size: %d", success, target, size);
-        return NULL;
-    }
+	long success = bpf_probe_write_user(target, data, size);
+	if (success == 0) {
+		s32 start_index = 0;
+		u64 alloc_map_index = ((u64)(bpf_get_current_pid_tgid() >> 32) << 32) | start_index;
+		// Update the start position of this chunk, taking into account possible adjustments
+		// we made to be page aligned
+		u64 updated_start = target_u + size;
+
+		// align updated_start to 8 bytes
+		if (updated_start % 8 != 0) {
+			updated_start += 8 - (updated_start % 8);
+		}
+
+		bpf_map_update_elem(&proc_alloc_map, &alloc_map_index, &updated_start, BPF_ANY);
+		return target;
+	} else {
+		bpf_printk("failed to write to userspace, error code: %d, addr: %lx, size: %d",
+		           success,
+		           target,
+		           size);
+		return NULL;
+	}
 }
 
 static __always_inline void *cw_write_target_data(void *data, s32 size, struct ebpf_proc_info* info)
 {
-    if (!data || data == NULL)
-    {
-        return NULL;
-    }
+	if (!data || data == NULL)
+	{
+		bpf_printk("163 err");
+		return NULL;
+	}
 	u64 start_from_proc;
 	u64 end_from_proc;
 //	__u32 tgid = (__u32)(bpf_get_current_pid_tgid() >> 32);
@@ -173,50 +240,53 @@ static __always_inline void *cw_write_target_data(void *data, s32 size, struct e
 	end_from_proc = info->end_addr;
 
 	u64 start = get_area_start(start_from_proc, end_from_proc);
-    u64 end = get_area_end(start,start_from_proc,end_from_proc);
-    if (end - start < size)
-    {
-        cw_bpf_debug("reached end of CPU memory block, going to the start again");
-        s32 start_index = 0;
-        bpf_map_delete_elem(&alloc_map, &start_index);
-        start = get_area_start(start_from_proc, end_from_proc);
-    }
+	u64 end = get_area_end(start,start_from_proc,end_from_proc);
+	s32 start_index = 0;
+	u64 alloc_map_index = ((u64)(bpf_get_current_pid_tgid() >> 32) << 32) | start_index;
+	if (end - start < size)
+	{
+		cw_bpf_debug("reached end of CPU memory block, going to the start again");
+		bpf_map_delete_elem(&proc_alloc_map, &alloc_map_index);
+		start = get_area_start(start_from_proc, end_from_proc);
+	}
 
-    void *target = (void *)start;
-    size = bound_number(size, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
-    u64 page_offset = (u64)target & 0xFFF;
-    u64 dist_to_next_page = 4096 - page_offset;
-    if (dist_to_next_page < size)
-    {
-        target += dist_to_next_page;
-    }
-    u64 target_u = (u64)target;
-    if (target_u > end_from_proc || target_u < start_from_proc) {
-	    cw_bpf_debug("TARGET ADDRESS IS OUT OF BOUNDS: 0x%llx", target);
-        return NULL;
-    }
+	void *target = (void *)start;
+	size = bound_number(size, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
+	u64 page_offset = (u64)target & 0xFFF;
+	u64 dist_to_next_page = 4096 - page_offset;
+	if (dist_to_next_page < size)
+	{
+		target += dist_to_next_page;
+	}
+	u64 target_u = (u64)target;
+	if (target_u > end_from_proc || target_u < start_from_proc) {
+		cw_bpf_debug("TARGET ADDRESS IS OUT OF BOUNDS: 0x%llx", target);
+//	    bpf_printk("no target_u:%llu start:%llu end:%llu", target_u, start_from_proc, end_from_proc);
+		bpf_printk("197 err");
+		return NULL;
+	}
+//	bpf_printk("ok target_u:%llu start:%llu end:%llu", target_u, start, end);
 
-    long success = bpf_probe_write_user(target, data, size);
-    if (success == 0)
-    {
-        s32 start_index = 0;
-        // Update the start position of this chunk, taking into account possible adjustments
-        // we made to be page aligned
-        u64 updated_start = target_u + size;
-
-        // align updated_start to 8 bytes
-        if (updated_start % 8 != 0) {
-            updated_start += 8 - (updated_start % 8);
-        }
-
-        bpf_map_update_elem(&alloc_map, &start_index, &updated_start, BPF_ANY);
-        return target;
-    }
-    else
-    {
-        bpf_printk("failed to write to userspace, error code: %d, addr: %lx, size: %d", success, target, size);
-        return NULL;
-    }
+	long success = bpf_probe_write_user(target, data, size);
+	if (success == 0)
+	{
+//        s32 start_index = 0;
+		// Update the start position of this chunk, taking into account possible adjustments
+		// we made to be page aligned
+		u64 updated_start = target_u + size;
+
+		// align updated_start to 8 bytes
+		if (updated_start % 8 != 0) {
+			updated_start += 8 - (updated_start % 8);
+		}
+		bpf_map_update_elem(&proc_alloc_map, &alloc_map_index, &updated_start, BPF_ANY);
+		return target;
+	}
+	else
+	{
+		bpf_printk("failed to write to userspace, error code: %d, addr: %lx, size: %d", success, target, size);
+		return NULL;
+	}
 }
 
 #endif

+ 58 - 0
ebpftracer/ebpf/utrace/go/include/go_net.h

@@ -0,0 +1,58 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef GO_NET_H
+#define GO_NET_H
+
+#include "common.h"
+#include "go_types.h"
+
+typedef struct net_addr {
+    u8 ip[16];
+    u32 port;
+} net_addr_t;
+
+/*
+type TCPAddr struct {
+	IP   IP
+	Port int
+	Zone string // IPv6 scoped addressing zone
+}
+*/
+const volatile u64 TCPAddr_IP_offset;
+const volatile u64 TCPAddr_Port_offset;
+
+static __always_inline long
+get_tcp_net_addr_from_tcp_addr(struct pt_regs *ctx, net_addr_t *addr, void *tcpAddr_ptr) {
+    go_slice_ot ip;
+    long res = bpf_probe_read_user(&ip, sizeof(ip), (void *)(tcpAddr_ptr + TCPAddr_IP_offset));
+    if (res != 0) {
+        bpf_printk("failed to read ip slice %d", res);
+        return res;
+    }
+
+    u8 ip_slice_len = 4;
+    if (ip.len != 4 && ip.len != 16) {
+        bpf_printk("invalid ip slice length: %d", ip.len);
+        return -1;
+    }
+
+    if (ip.len == 16) {
+        ip_slice_len = 16;
+    }
+
+    res = bpf_probe_read_user(addr->ip, ip_slice_len, ip.array);
+    if (res != 0) {
+        bpf_printk("failed to read ip array");
+        return res;
+    }
+
+    res = bpf_probe_read_user(
+        &addr->port, sizeof(addr->port), (void *)(tcpAddr_ptr + TCPAddr_Port_offset));
+    if (res != 0) {
+        bpf_printk("failed to read port");
+    }
+    return res;
+}
+
+#endif

+ 125 - 21
ebpftracer/ebpf/utrace/go/include/go_types.h

@@ -23,24 +23,35 @@
  Keep a power of 2 to help with masks */
 #define MAX_SLICE_ARRAY_SIZE 1024
 
-struct go_string_ot
+typedef struct go_string {
+	char *str;
+	s64 len;
+} go_string;
+
+typedef struct go_string_ot {
+	char *str;
+	s64 len;
+} go_string_ot;
+
+typedef struct go_slice
 {
-    char *str;
-    s64 len;
-};
+	void *array;
+	s64 len;
+	s64 cap;
+} go_slice;
 
-struct go_slice_ot
+typedef struct go_slice_ot
 {
-    void *array;
-    s64 len;
-    s64 cap;
-};
+	void *array;
+	s64 len;
+	s64 cap;
+} go_slice_ot;
 
-struct go_iface
+typedef struct go_iface
 {
-    void *tab;
-    void *data;
-};
+	void *type;
+	void *data;
+} go_iface_t;
 
 struct map_bucket {
     char tophash[8];
@@ -55,20 +66,40 @@ struct map_bucket {
 	u8 bucket_index;
 
 };
-
-struct slice_array_buff
-{
-    unsigned char buff[MAX_SLICE_ARRAY_SIZE];
+// a map bucket type with the given key and value types
+#define MAP_BUCKET_TYPE(key_type, value_type) struct map_bucket_##key_type##_##value_type##_t
+// a map bucket struct definition with the given key and value types
+// for more details about the structure of a map bucket see:
+// https://github.com/golang/go/blob/639cc0dcc0948dd02c9d5fc12fbed730a21ebebc/src/runtime/map.go#L143
+#define MAP_BUCKET_DEFINITION(key_type, value_type) \
+MAP_BUCKET_TYPE(key_type, value_type) { \
+    char tophash[8]; \
+    key_type keys[8]; \
+    value_type values[8]; \
+    void *overflow; \
 };
 
 struct
 {
-    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-    __type(key, u32);
-    __type(value, struct slice_array_buff);
-    __uint(max_entries, 1);
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, struct slice_array_buff);
+	__uint(max_entries, 1);
 } slice_array_buff_map SEC(".maps");
 
+struct slice_array_buff
+{
+    unsigned char buff[MAX_SLICE_ARRAY_SIZE];
+};
+//
+//struct
+//{
+//    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+//    __type(key, u32);
+//    __type(value, struct slice_array_buff);
+//    __uint(max_entries, 1);
+//} slice_array_buff_map SEC(".maps");
+
 // In Go, interfaces are represented as a pair of pointers: a pointer to the
 // interface data, and a pointer to the interface table.
 // See: runtime.iface in https://golang.org/src/runtime/runtime2.go
@@ -120,6 +151,7 @@ static __always_inline void append_item_to_slice(void *new_item, u32 item_size,
         u32 alloc_size = item_size * slice_len;
         if (alloc_size >= MAX_SLICE_ARRAY_SIZE)
         {
+            bpf_printk("enter the uprobe_LoopyWriter_HeaderHandler99999\n");
             return;
         }
     
@@ -128,6 +160,7 @@ static __always_inline void append_item_to_slice(void *new_item, u32 item_size,
         struct slice_array_buff *map_buff = bpf_map_lookup_elem(&slice_array_buff_map, &index);
         if (!map_buff)
         {
+            bpf_printk("enter the uprobe_LoopyWriter_HeaderHandler88888\n");
             return;
         }
     
@@ -137,6 +170,7 @@ static __always_inline void append_item_to_slice(void *new_item, u32 item_size,
         if (alloc_size + item_size > MAX_SLICE_ARRAY_SIZE)
         {
             // No room for new item
+            bpf_printk("enter the uprobe_LoopyWriter_HeaderHandler7777\n");
             return;
         }
         // Append to buffer
@@ -195,4 +229,74 @@ static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char
 
     return true;
 }
+
+static __always_inline void
+cw_append_item_to_slice(void *new_item, u32 item_size, void *slice_user_ptr) {
+	// read the slice descriptor
+	struct go_slice_ot slice = {0};
+	bpf_probe_read(&slice, sizeof(slice), slice_user_ptr);
+	long res = 0;
+
+	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
+		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)
+		u32 alloc_size = item_size * slice_len;
+		if (alloc_size >= MAX_SLICE_ARRAY_SIZE) {
+			return;
+		}
+
+		// Get temporary buffer
+		u32 index = 0;
+		struct slice_array_buff *map_buff = bpf_map_lookup_elem(&slice_array_buff_map, &index);
+		if (!map_buff) {
+			return;
+		}
+
+		unsigned char *new_slice_array = map_buff->buff;
+		// help the verifier
+		alloc_size &= (MAX_SLICE_ARRAY_SIZE - 1);
+		if (alloc_size + item_size > MAX_SLICE_ARRAY_SIZE) {
+			// No room for new item
+			return;
+		}
+		// Append to buffer
+		if (slice.array != NULL) {
+			bpf_probe_read_user(new_slice_array, alloc_size, slice.array);
+		}
+		copy_byte_arrays(new_item, new_slice_array + alloc_size, item_size);
+
+		// Copy buffer to userspace
+		u32 new_array_size = alloc_size + item_size;
+
+		__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;
+		}
+		void *new_array = cw_write_target_data(new_slice_array, new_array_size, info);
+		if (new_array == NULL) {
+			cw_bpf_debug("append_item_to_slice: failed to copy new array to userspace");
+			return;
+		}
+
+		// Update array pointer of slice
+		slice.array = new_array;
+		slice.cap++;
+
+		// cw_bpf_debug("enter the cw_append_item_to_slice222222\n");
+	}
+
+	// Update len
+	slice.len++;
+	long success = bpf_probe_write_user(slice_user_ptr, &slice, sizeof(slice));
+	if (success != 0) {
+		cw_bpf_debug("append_item_to_slice: failed to update slice in userspace");
+		return;
+	}
+}
+
 #endif

+ 4 - 2
ebpftracer/ebpf/utrace/go/include/span_context.h

@@ -22,8 +22,10 @@
 #define W3C_VAL_LENGTH 55
 
 struct span_context {
-	unsigned char TraceID[TRACE_ID_SIZE];
-	unsigned char SpanID[SPAN_ID_SIZE];
+	u8 TraceID[TRACE_ID_SIZE];
+	u8 SpanID[SPAN_ID_SIZE];
+	u8 TraceFlags;
+	u8 padding[7];
 };
 
 

+ 6 - 5
ebpftracer/ebpf/utrace/go/include/utils.h

@@ -70,11 +70,12 @@ static __always_inline void hex_string_to_bytes(char *str, u32 size, unsigned ch
 
 static __always_inline void copy_byte_arrays(unsigned char *src, unsigned char *dst, u32 size)
 {
-#pragma clang loop unroll(full)
-    for (int i = 0; i < size; i++)
-    {
-        dst[i] = src[i];
-    }
+	cw_copy_byte_arrays(src,dst,size);
+//#pragma clang loop unroll(full)
+//    for (int i = 0; i < size; i++)
+//    {
+//        dst[i] = src[i];
+//    }
 }
 
 static __always_inline void custom_hash(const char *str, unsigned char *hash,u32 size) {

+ 406 - 0
ebpftracer/ebpf/utrace/go/mq/kafka/consumer.probe.bpf.c

@@ -0,0 +1,406 @@
+//
+// Created by Carl.Guo on 2025/11/17.
+//
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "arguments.h"
+#include "span_context.h"
+#include "go_context.h"
+#include "go_types.h"
+#include "uprobe.h"
+#include "apm_trace.h"
+
+#define MAX_CONCURRENT 50
+// https://github.com/apache/kafka/blob/0.10.2/core/src/main/scala/kafka/common/Topic.scala#L30C3-L30C34
+#define MAX_TOPIC_SIZE 256
+// No constraint on the key size, but we must have a limit for the verifier
+#define MAX_KEY_SIZE 256
+#define MAX_CONSUMER_GROUP_SIZE 128
+#define MAX_HEADERS 20
+
+struct kafka_consumer_request_t {
+	u64 start_time;
+	u64 end_time;
+//	struct apm_span_context sc;
+	struct apm_span_context psc;
+	char topic[MAX_TOPIC_SIZE];
+	char key[MAX_KEY_SIZE];
+	char consumer_group[MAX_CONSUMER_GROUP_SIZE];
+	s64 offset;
+	s64 partition;
+} __attribute__((packed));
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__type(key, void*);
+	__type(value, struct kafka_consumer_request_t);
+	__uint(max_entries, MAX_CONCURRENT);
+} kafka_consumer_events SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__type(key, void*);
+	__type(value, void*);
+	__uint(max_entries, MAX_CONCURRENT);
+} goroutine_to_go_context SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__type(key, void*);
+	__type(value, void*);
+	__uint(max_entries, MAX_CONCURRENT);
+} kafka_reader_to_conn SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct kafka_consumer_request_t));
+	__uint(max_entries, 1);
+} kafka_consumer_request_storage_map SEC(".maps");
+
+// Storage for temporary strings in extract_span_context_from_headers to avoid stack overflow
+struct kafka_extract_temp_t {
+	char key[CW_HEADER_KEY_LENGTH];
+	char current_key[CW_HEADER_KEY_LENGTH];
+	char val[CW_HEADER_VAL_LENGTH];
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct kafka_extract_temp_t));
+	__uint(max_entries, 1);
+} kafka_extract_temp_storage_map SEC(".maps");
+
+// Storage for parent span context to avoid stack overflow
+
+
+static __always_inline long extract_span_context_from_headers(void *message, struct apm_span_context *parent_span_context, struct ebpf_proc_info *proc_info) {
+
+	// Read the headers slice descriptor
+	void *headers = (void *)(message + proc_info->kafka_message_headers_pos);
+	struct go_slice headers_slice = {0};
+	bpf_probe_read(&headers_slice, sizeof(headers_slice), headers);
+
+	char key[CW_HEADER_KEY_LENGTH] = CW_HEADER_KEY_VAL;
+	char current_key[CW_HEADER_KEY_LENGTH];
+
+	for (u64 i = 0; i < headers_slice.len; i++) {
+		if (i >= MAX_HEADERS) {
+			break;
+		}
+		// Read the header
+		struct kafka_header_t header = {0};
+		bpf_probe_read(&header, sizeof(header), headers_slice.array + (i * sizeof(header)));
+		// Check if it is the traceparent header
+		if (header.key.len == CW_HEADER_KEY_LENGTH && header.value.len == CW_HEADER_VAL_LENGTH) {
+			bpf_probe_read_user(current_key, sizeof(current_key), header.key.str);
+			if (bpf_memcmp(key, current_key, sizeof(key))) {
+				// Found the traceparent header, extract the span context
+//				char val[CW_HEADER_VAL_LENGTH];
+//				bpf_probe_read(val, CW_HEADER_VAL_LENGTH, header.value.array);
+//				w3c_string_to_span_context(val, parent_span_context);
+				cw_string_to_span_context(header.value.array, parent_span_context);
+				return 0;
+			}
+		}
+	}
+	return -1;
+}
+
+// This instrumentation attaches uprobe to the following function:
+// func (r *Reader) FetchMessage(ctx context.Context) (Message, error)
+// Entry probe: End the span that was started when the last call returned
+SEC("uprobe/FetchMessage")
+int uprobe_FetchMessage(struct pt_regs *ctx) {
+	/* FetchMessage is a blocking function, hence its execution time is not a good indication for the time it took to handle the message.
+	Instead, we use the entry to this function to end the span which was started when it's last call returned. (A typical consumer calls FetchMessage in a loop)
+	A less confusing way of looking at it is as follows 
+	1. Entry to FetchMessage
+	2. internal kafka code before blocking
+	3. Blocking wait for message
+	4. internal kafka code after blocking
+	5. Return from FetchMessage
+	Steps 2-4 are executed in a separate goroutine from the one the user of the library.
+	*/
+	void *reader = get_argument(ctx, 1);
+//	struct go_iface go_context = {0};
+//	get_Go_context(ctx, 2, 0, true, &go_context);
+	void *goroutine = (void *)GOROUTINE(ctx);
+
+	// Save reader to map for use in return probe
+	if (reader != NULL) {
+		void *reader_ptr = reader;
+		bpf_map_update_elem(&kafka_reader_to_conn, &goroutine, &reader_ptr, BPF_ANY);
+	}
+	
+	struct kafka_consumer_request_t *kafka_request = bpf_map_lookup_elem(&kafka_consumer_events, &goroutine);
+	if (kafka_request == NULL || reader == NULL) {
+//		cw_bpf_debug("kafka goto end");
+		// The current goroutine has no kafka request,
+		// this can happen in the first time FetchMessage is called
+		// Save the context for the return probe for in-process context propagation
+		goto save_context;
+	}
+
+	// Get consumer group from reader config
+	__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 0;
+	}
+	get_go_string_from_user_ptr((void *)(reader + proc_info->kafka_reader_config_pos + proc_info->kafka_reader_config_group_id_pos),
+	                            kafka_request->consumer_group,
+	                            sizeof(kafka_request->consumer_group));
+
+
+	kafka_request->end_time = bpf_ktime_get_ns();
+
+	// Output span event
+//	__u64 pid_tgid = bpf_get_current_pid_tgid();
+//	__u32 tgid = pid_tgid >> 32;
+
+	u32 zero = 0;
+	struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+	if (e) {
+		struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
+		struct apm_trace_info_t * start_trace_info = get_apm_trace_info_by_trace_key(trace_key);
+		if (!start_trace_info) {
+			return -1;
+		}
+
+		cw_copy_byte_arrays(kafka_request->psc.trace_id, e->trace_id_from, APM_TRACE_ID_SIZE);
+		cw_copy_byte_arrays(kafka_request->psc.assumed_app_id, e->called_id, APM_ASSUMED_APP_ID_SIZE);
+		cw_copy_byte_arrays(kafka_request->psc.instance_id, e->instance_id_from, APM_INSTANCE_ID_SIZE);
+		cw_copy_byte_arrays(kafka_request->psc.app_id, e->app_id_from, APM_APP_ID_SIZE);
+		cw_copy_byte_arrays(kafka_request->psc.span_id, e->span_id_from, APM_SPAN_ID_SIZE);
+		cw_copy_byte_arrays(kafka_request->psc.type_from, e->type_from, APM_TYPE_FROM_SIZE);
+
+		__u32 event_count = cw_get_event_count(start_trace_info->trace_id);
+
+		e->method = METHOD_CONSUME;
+		SET_TRACE_END(e, PROTOCOL_KAFKA);
+		e->start_at = kafka_request->start_time;
+		e->end_at = kafka_request->end_time;
+		e->duration = e->end_at - e->start_at;
+		e->trace_id = start_trace_info->trace_id;
+		e->event_count = event_count;
+
+		// 填充 MQ 相关信息
+		bpf_probe_read(&e->mq.topic, sizeof(e->mq.topic), kafka_request->topic);
+		bpf_probe_read(&e->mq.key, sizeof(e->mq.key), kafka_request->key);
+		cw_bpf_debug("kafka_request->key %s",kafka_request->key);
+		cw_bpf_debug("kafka_request->topic %s",kafka_request->topic);
+
+		bpf_map_delete_elem(&trace_event_count_heap, &e->trace_id);
+		// 清除业务层trace信息
+		clear_parent_span_context_by_trace_key(start_trace_info->trace_key);
+		// 清除trace信息
+		__u32 tid =  (__u32)pid_tgid;
+		cw_clear_trace(tgid, tid, 0);
+
+		bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
+
+	}
+
+//	stop_tracking_span(&kafka_request->sc, &kafka_request->psc);
+	bpf_map_delete_elem(&kafka_consumer_events, &goroutine);
+
+save_context:
+	// Save the context for the return probe
+//	bpf_map_update_elem(&goroutine_to_go_context, &goroutine, &go_context.data, 0);
+	return 0;
+}
+
+// This instrumentation attaches uprobe to the following function:
+// func (r *Reader) FetchMessage(ctx context.Context) (Message, error)
+// Return probe: Start tracking the span for the message that was just fetched
+SEC("uprobe/FetchMessage")
+int uprobe_FetchMessage_Returns(struct pt_regs *ctx) {
+	/* The FetchMessage function returns a message to the user after it read it from a channel.
+	The user consuming this message will handle it after this probe,
+	thus it is a good place to start track the span corresponds to this message. In addition we save the message
+	in a hash map to be read by the entry probe of FetchMessage, which will end this span */
+	void *goroutine = (void *)GOROUTINE(ctx);
+	u32 map_id = 0;
+	struct kafka_consumer_request_t *kafka_request = bpf_map_lookup_elem(&kafka_consumer_request_storage_map, &map_id);
+	if (kafka_request == NULL) {
+		cw_bpf_debug("uprobe/FetchMessage_Returns: kafka_request is NULL");
+		return 0;
+	}
+
+	// Get proc_info to access Kafka offsets
+	__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 == NULL) {
+		cw_bpf_debug("uprobe/FetchMessage_Returns: proc_info is NULL");
+		return 0;
+	}
+
+	// The message returned on the stack since it returned as a struct and not a pointer
+	void *message = (void *)(PT_REGS_SP(ctx) + 8);
+	if (proc_info->kafka_message_value_pos != 0) {
+		struct go_slice_ot value_slice = {0};
+		bpf_probe_read(&value_slice, sizeof(value_slice), (void *)(message + proc_info->kafka_message_value_pos));
+//		cw_bpf_debug("len %d",value_slice.len);
+		if (value_slice.array == NULL) {
+//			cw_bpf_debug("no val");
+			return 0;
+		} else {
+			cw_bpf_debug("has val %llu", goroutine);
+		}
+	} else {
+		return 0;
+	}
+
+	// Zero the request
+	u32 zero_id = 0;
+	struct kafka_consumer_request_t *zero_kafka_request = bpf_map_lookup_elem(&kafka_consumer_request_storage_map, &zero_id);
+	if (zero_kafka_request != NULL) {
+		__builtin_memcpy(kafka_request, zero_kafka_request, sizeof(struct kafka_consumer_request_t));
+	}
+
+	kafka_request->start_time = bpf_ktime_get_ns();
+
+//	cw_bpf_debug("start %llu",message);
+
+//	struct go_iface go_context = {0};
+//	get_Go_context(ctx, 2, 0, true, &go_context);
+
+	// Get the parent span context from the message headers
+	// Use per-cpu map to avoid stack overflow
+//	u32 parent_sc_storage_id = 0;
+//	struct apm_span_context *parent_span_context = bpf_map_lookup_elem(&cw_parent_span_context_storage_map, &parent_sc_storage_id);
+//	if (parent_span_context == NULL) {
+//		cw_bpf_debug("uprobe/FetchMessage_Returns: Failed to get parent_span_context storage");
+//		return 0;
+//	}
+	// Manual zeroing to avoid memset issues
+//	parent_span_context->TraceFlags = 0;
+//	__builtin_memset(parent_span_context->trace_id, 0, TRACE_ID_SIZE);
+//	__builtin_memset(parent_span_context->span_id, 0, SPAN_ID_SIZE);
+
+	// Print message Value field before extracting span context from headers
+	// Value is a byte slice, similar to Key
+
+	long extract_result = extract_span_context_from_headers(message, &kafka_request->psc, proc_info);
+//	if (extract_result != 0) {
+//		generate_random_bytes(kafka_request->psc.trace_id, TRACE_ID_SIZE);
+//	}
+	if (extract_result == 0) {
+		cw_bpf_debug("find");
+		for (int i = 0; i < APM_TRACE_ID_SIZE; i++) {
+			cw_bpf_debug("cw_get_current_tracking_span-trace_id[%d] = %02x", i, kafka_request->psc.trace_id[i]);
+		}
+		// Successfully extracted parent span context from headers
+//		kafka_request->psc = *parent_span_context;
+	} else {
+		// No parent span context in headers, try to get from Go context
+		generate_random_bytes(kafka_request->psc.trace_id, TRACE_ID_SIZE);
+//		struct span_context *parent_sc = get_parent_span_context(go_context.data);
+//		if (parent_sc != NULL) {
+//			kafka_request->psc = *parent_sc;
+//		}
+		cw_bpf_debug("no header");
+	}
+
+	// Collecting message attributes
+	// topic
+	get_go_string_from_user_ptr(
+			(void *)(message + proc_info->kafka_message_topic_pos), kafka_request->topic, sizeof(kafka_request->topic));
+	cw_bpf_debug("topic %s",kafka_request->topic);
+	// Key is a byte slice, first read the slice
+	if (proc_info->kafka_message_key_pos != 0) {
+		struct go_slice_ot key_slice = {0};
+		bpf_probe_read(&key_slice, sizeof(key_slice), (void *)(message + proc_info->kafka_message_key_pos));
+		u64 size_to_read = key_slice.len > MAX_KEY_SIZE ? MAX_KEY_SIZE : key_slice.len;
+		size_to_read &= 0xFF;
+		// Then read the actual key
+		if (size_to_read > 0 && key_slice.array != NULL) {
+			bpf_probe_read_user(kafka_request->key, size_to_read, key_slice.array);
+		}
+	}
+
+	u32 zero = 0;
+	struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+
+	if (e) {
+		e->method = METHOD_CONSUME;
+
+		SET_TRACE_START(e, PROTOCOL_KAFKA);
+		e->start_at = kafka_request->start_time;
+		e->end_at = kafka_request->end_time;
+		e->duration = e->end_at - e->start_at;
+
+		struct apm_trace_info_t trace_info = cw_save_trace_info(pid_tgid, tgid, 0);
+		e->trace_id = trace_info.trace_id;
+
+		// 填充 MQ 相关信息
+		bpf_probe_read(&e->mq.topic, sizeof(e->mq.topic), kafka_request->topic);
+		bpf_probe_read(&e->mq.key, sizeof(e->mq.key), kafka_request->key);
+
+		// Send event
+		long error = bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
+		if (error == 0) {
+			cw_bpf_debug("send start ok");
+//			cw_add_event_count(e->trace_id);
+//			cw_bpf_debug("[kafka consumer] send success trace %llu", e->trace_id);
+		}
+	} else {
+		cw_bpf_debug("no e");
+	}
+//	return 0;
+
+	// Initialize child span context
+//	generate_random_bytes(kafka_request->sc.trace_id, TRACE_ID_SIZE);
+//	generate_random_bytes(kafka_request->sc.span_id, SPAN_ID_SIZE);
+//
+//	// If we have a parent span, use its trace ID
+//	if (extract_result == 0 && parent_span_context != NULL) {
+//		copy_byte_arrays(parent_span_context->trace_id, kafka_request->sc.trace_id, TRACE_ID_SIZE);
+//	}
+//
+//	// Collecting message attributes
+//	// topic
+//	get_go_string_from_user_ptr((void *)(message + proc_info->kafka_message_topic_pos), kafka_request->topic, sizeof(kafka_request->topic));
+//	// partition
+//	if (proc_info->kafka_message_partition_pos != 0) {
+//		bpf_probe_read(&kafka_request->partition, sizeof(kafka_request->partition), (void *)(message + proc_info->kafka_message_partition_pos));
+//	}
+//	// offset
+//	if (proc_info->kafka_message_offset_pos != 0) {
+//		bpf_probe_read(&kafka_request->offset, sizeof(kafka_request->offset), (void *)(message + proc_info->kafka_message_offset_pos));
+//	}
+//	// Key is a byte slice, first read the slice descriptor
+//	struct go_slice_ot key_slice = {0};
+//	bpf_probe_read(&key_slice, sizeof(key_slice), (void *)(message + proc_info->kafka_message_key_pos));
+//	u64 size_to_read = key_slice.len > MAX_KEY_SIZE ? MAX_KEY_SIZE : key_slice.len;
+//	size_to_read &= 0xFF;
+//	// Then read the actual key
+//	if (size_to_read > 0 && key_slice.array != NULL) {
+//		bpf_probe_read_user(kafka_request->key, size_to_read, key_slice.array);
+//	}
+//
+//	// Set app_id and instance_id from proc_info
+//	// proc_info is already obtained above
+//	if (proc_info) {
+//		copy_byte_arrays(proc_info->app_id, kafka_request->sc.app_id, APM_APP_ID_SIZE);
+//		copy_byte_arrays(proc_info->instance_id, kafka_request->sc.instance_id, APM_INSTANCE_ID_SIZE);
+//	}
+
+	bpf_map_update_elem(&kafka_consumer_events, &goroutine, kafka_request, 0);
+	// We are start tracking the consumer span in the return probe,
+	// hence we can't read Go's context directly from the registers as we usually do.
+	// Using the goroutine address as a key to the map that contains the context.
+//	void *context_data_ptr = bpf_map_lookup_elem(&goroutine_to_go_context, &goroutine);
+//	if (context_data_ptr != NULL) {
+//		bpf_probe_read_kernel(&context_data_ptr, sizeof(context_data_ptr), context_data_ptr);
+//		start_tracking_span(context_data_ptr, &kafka_request->sc);
+//		bpf_map_delete_elem(&goroutine_to_go_context, &goroutine);
+//	}
+
+	return 0;
+}

+ 552 - 0
ebpftracer/ebpf/utrace/go/mq/kafka/producer.probe.bpf.c

@@ -0,0 +1,552 @@
+//
+// Created by Carl.Guo on 2025/11/17.
+//
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "arguments.h"
+#include "span_context.h"
+#include "go_context.h"
+#include "go_types.h"
+#include "go_net.h"
+#include "uprobe.h"
+#include "apm_trace.h"
+//#include "span_output.h"
+//#include "trace/start_span.h"
+
+
+
+#define MAX_CONCURRENT 50
+// https://github.com/segmentio/kafka-go/blob/main/writer.go#L118
+// TODO: (this value is directly impact the map sizes as well as the verification complexity)
+// limitation on map entry size: https://github.com/iovisor/bcc/issues/2519#issuecomment-534359316
+// the default value is 100, but it can be changed by the user
+// we must specify a limit for the verifier
+#if __KERNEL_FROM >= 512
+#define MAX_BATCH_SIZE 10
+#else
+#define MAX_BATCH_SIZE 1
+#endif
+
+// https://github.com/apache/kafka/blob/0.10.2/core/src/main/scala/kafka/common/Topic.scala#L30C3-L30C34
+#define MAX_TOPIC_SIZE 256
+// No constraint on the key size, but we must have a limit for the verifier
+#define MAX_KEY_SIZE 256
+#define MAX_BROKER_ADDR_SIZE 21  // Max broker address length (e.g., "192.168.1.1:9092")
+#define MAX_HOSTNAME_SIZE 256    // Max hostname/broker address size
+#define MAX_PAYLOAD_SIZE 1024    // Max payload size (must match l7.c definition)
+
+struct message_attributes_t {
+	struct apm_span_context sc;
+	char topic[MAX_TOPIC_SIZE];
+	char key[MAX_KEY_SIZE];
+};
+
+struct kafka_request_t {
+	// common attributes to all the produced messages
+	u64 start_time;
+	u64 end_time;
+	struct apm_span_context apm_sc;
+	// attributes per message
+	struct message_attributes_t msgs[MAX_BATCH_SIZE];
+	char global_topic[MAX_TOPIC_SIZE];
+	u64 valid_messages;
+	char host[MAX_HOSTNAME_SIZE];// First broker address (e.g., "192.168.1.1:9092")
+}__attribute__((packed));
+
+struct kafka_request_t2 {
+	// common attributes to all the produced messages
+	u64 start_time;
+	u64 end_time;
+	struct apm_span_context apm_sc;
+	char host[MAX_HOSTNAME_SIZE];
+
+	// attributes per message
+//	struct message_attributes_t msgs[MAX_BATCH_SIZE];
+//	char global_topic[MAX_TOPIC_SIZE];
+//	char broker_addr[MAX_BROKER_ADDR_SIZE];  // First broker address (e.g., "192.168.1.1:9092")
+//	u64 valid_messages;
+
+}__attribute__((packed));
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__type(key,void*);
+	__type(value,struct kafka_request_t);
+	__uint(max_entries, MAX_CONCURRENT);
+} kafka_events SEC(".maps");
+
+struct kafka_header_t {
+	struct go_string_ot key;
+	struct go_slice_ot value;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct kafka_request_t));
+	__uint(max_entries, 2);
+} kafka_request_storage_map SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct kafka_request_t2));
+	__uint(max_entries, 2);
+} kafka_request_storage_map2 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct kafka_header_t));
+	__uint(max_entries, 1);
+} kafka_header_storage_map SEC(".maps");
+
+// Storage for temporary strings in build_contxet_header to avoid stack overflow
+struct kafka_temp_strings_t {
+	char key[CW_HEADER_KEY_LENGTH];
+	char val[CW_HEADER_VAL_LENGTH];
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct kafka_temp_strings_t));
+	__uint(max_entries, 1);
+} kafka_temp_strings_storage_map SEC(".maps");
+
+// Storage for temporary Go types in broker address reading to avoid stack overflow
+struct kafka_broker_temp_t {
+	struct go_slice_ot brokers_slice;
+	struct go_string_ot broker_str;
+	struct go_iface addr_iface;  // For net.Addr interface
+	struct go_slice_ot ip_slice;  // For net.TCPAddr.IP
+	u8 ip_bytes[16];              // For IP bytes
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct kafka_broker_temp_t));
+	__uint(max_entries, 1);
+} kafka_broker_temp_storage_map SEC(".maps");
+
+// https://github.com/segmentio/kafka-go/blob/main/protocol/record.go#L48
+
+struct tcp_addr {
+	struct go_slice_ot IP;  // offset 0
+	int Port;            // offset 24
+	// 忽略 Zone
+};
+
+
+static __always_inline int build_contxet_header(struct kafka_header_t *header, struct apm_span_context *span_ctx) {
+	if (header == NULL || span_ctx == NULL) {
+		cw_bpf_debug("build_contxt_header: Invalid arguments");
+		return -1;
+	}
+
+	// Get temporary string storage from per-cpu map to avoid stack overflow
+	u32 temp_strings_id = 0;
+	struct kafka_temp_strings_t *temp_strings = bpf_map_lookup_elem(&kafka_temp_strings_storage_map, &temp_strings_id);
+	if (temp_strings == NULL) {
+		cw_bpf_debug("build_contxt_header: Failed to get temp strings storage");
+		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 == NULL) {
+		cw_bpf_debug("uprobe/WriteMessages: proc_info is NULL");
+		return 0;
+	}
+
+	// Prepare the key string for the user - use per-cpu storage
+	__builtin_memcpy(temp_strings->key, CW_HEADER_KEY_VAL, CW_HEADER_KEY_LENGTH);
+	void *ptr = cw_write_target_data(temp_strings->key, CW_HEADER_KEY_LENGTH, proc_info);
+	if (ptr == NULL) {
+		cw_bpf_debug("build_contxt_header: Failed to write key to user");
+		return -1;
+	}
+
+	// build the go string of the key
+	header->key.str = ptr;
+	header->key.len = CW_HEADER_KEY_LENGTH;
+
+	// Convert span_context to apm_span_context for CW format
+//	struct apm_span_context apm_sc = {0};
+	// Copy trace_id and span_id from span_context
+//	copy_byte_arrays(span_ctx->TraceID, apm_sc.trace_id, TRACE_ID_SIZE);
+//	copy_byte_arrays(span_ctx->SpanID, apm_sc.span_id, SPAN_ID_SIZE);
+
+	// Set type_from and sample
+//	apm_sc.type_from[0] = '0';
+//	apm_sc.sample[0] = '0';
+
+	// Get host_id from trace_conf_map
+//	u32 k0 = 0;
+//	struct trace_conf_t *trace_conf = trace_conf_map__lookup(&k0);
+//	if (trace_conf) {
+//		copy_byte_arrays(trace_conf->host_id, apm_sc.host_id, APM_HOST_ID_SIZE);
+//	}
+
+	// Get app_id and instance_id from proc_info
+//	copy_byte_arrays(proc_info->app_id, apm_sc.app_id, APM_APP_ID_SIZE);
+
+	// Set assumed_app_id (for Kafka, we can use app_id as assumed_app_id)
+//	copy_byte_arrays(proc_info->app_id, apm_sc.assumed_app_id, APM_ASSUMED_APP_ID_SIZE);
+
+	// Prepare the value string for the user - use per-cpu storage
+//	generate_random_bytes(apm_sc.trace_id, APM_TRACE_ID_SIZE);
+//	generate_random_bytes(apm_sc.span_id, APM_SPAN_ID_SIZE);
+
+	span_context_to_cw_string(span_ctx, temp_strings->val);
+
+	cw_bpf_debug("%s",temp_strings->val);
+	ptr = cw_write_target_data(temp_strings->val, CW_HEADER_VAL_LENGTH, proc_info);
+	if (ptr == NULL) {
+		cw_bpf_debug("build_contxt_header: Failed to write value to user");
+		return -1;
+	}
+
+	// build the go slice of the value
+	header->value.array = ptr;
+	header->value.len = CW_HEADER_VAL_LENGTH;
+	header->value.cap = CW_HEADER_VAL_LENGTH;
+	cw_bpf_debug("build_contxt_header success");
+	return 0;
+}
+
+static __always_inline int inject_kafka_header(void *message, struct kafka_header_t *header, u64 msg_headers_pos) {
+//	append_item_to_slice(header, sizeof(*header), (void *) (message + msg_headers_pos));
+	cw_append_item_to_slice(header, sizeof(*header), (void *) (message + msg_headers_pos));
+	return 0;
+}
+
+
+static __always_inline long collect_kafka_attributes(void *message, struct message_attributes_t *attrs, bool collect_topic, u64 msg_key_pos, u64 msg_topic_pos) {
+	if (collect_topic) {
+		// Topic might be globally set for a writer, or per message
+		get_go_string_from_user_ptr((void *) (message + msg_topic_pos), attrs->topic, sizeof(attrs->topic));
+	}
+
+	// Key is a byte slice, first read the slice
+	struct go_slice_ot key_slice = {0};
+	bpf_probe_read(&key_slice, sizeof(key_slice), (void *) (message + msg_key_pos));
+	u64 size_to_read = key_slice.len > MAX_KEY_SIZE ? MAX_KEY_SIZE : key_slice.len;
+	size_to_read &= 0xFF;
+	// Then read the actual key
+	return bpf_probe_read(attrs->key, size_to_read, key_slice.array);
+}
+
+// This instrumentation attaches uprobe to the following function:
+// func (w *Writer) WriteMessages(ctx context.Context, msgs ...Message) error
+SEC("uprobe/WriteMessages")
+int uprobe_WriteMessages(struct pt_regs *ctx) {
+	// In Go, "..." is equivalent to passing a slice: https://go.dev/ref/spec#Passing_arguments_to_..._parameters
+	void *writer = get_argument(ctx, 1);
+	void *msgs_array = get_argument(ctx, 4);
+	u64 msgs_array_len = (u64) get_argument(ctx, 5);
+
+	void *key = (void *) GOROUTINE(ctx);
+
+	void *kafka_request_ptr = bpf_map_lookup_elem(&kafka_events, &key);
+	if (kafka_request_ptr != NULL) {
+		cw_bpf_debug("uprobe/WriteMessages already tracked with the current context");
+		return 0;
+	}
+
+	// Get proc_info to access Kafka offsets
+	__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 == NULL) {
+		cw_bpf_debug("uprobe/WriteMessages: proc_info is NULL");
+		return 0;
+	}
+
+
+	u32 zero_id = 0;
+	struct kafka_request_t *zero_kafka_request = bpf_map_lookup_elem(&kafka_request_storage_map, &zero_id);
+	if (zero_kafka_request == NULL) {
+		cw_bpf_debug("uuprobe/WriteMessages: zero_kafka_request is NULL");
+		return 0;
+	}
+
+	u32 actual_id = 1;
+	// Zero the span we are about to build, eBPF doesn't support memset of large structs (more than 1024 bytes)
+	bpf_map_update_elem(&kafka_request_storage_map, &actual_id, zero_kafka_request, BPF_ANY);
+	// Get a pointer to the zeroed span
+	struct kafka_request_t *kafka_request = bpf_map_lookup_elem(&kafka_request_storage_map, &actual_id);
+	if (kafka_request == NULL) {
+		cw_bpf_debug("uprobe/WriteMessages: Failed to get kafka_request");
+		return 0;
+	}
+
+	kafka_request->start_time = bpf_ktime_get_ns();
+
+	void *writer_addr_iface_ptr = writer + 0;
+	void *na_ptr = NULL;
+	bpf_probe_read(&na_ptr, sizeof(void *), get_go_interface_instance(writer_addr_iface_ptr));
+	if (!na_ptr)
+		return 0;
+
+	// Zero the host
+	__builtin_memset(kafka_request->host, 0, sizeof(kafka_request->host));
+
+
+//	void * user_str_ptr = na_ptr + 0x10;
+//	if (user_str_ptr == NULL)
+//	{
+//		return false;
+//	}
+//
+//	struct go_string_ot user_str = {0};
+//	long success = 0;
+//	success = bpf_probe_read(&user_str, sizeof(struct go_string_ot), user_str_ptr);
+//	if (success != 0 || user_str.len < 1)
+//	{
+//		return 0;
+//	}
+
+//	u64 size_to_read = user_str.len > 22 ? 22 : user_str.len;
+//	cw_bpf_debug("%s",user_str.str);
+//	return 0;
+//	success = bpf_probe_read(dst, size_to_read, user_str.str);
+
+	// 设置 appid
+	copy_byte_arrays(proc_info->app_id, kafka_request->msgs[0].sc.app_id, APM_APP_ID_SIZE);
+
+	// setting assumed_app_id
+	if (!get_go_string_from_user_ptr((void *) (na_ptr + 0x10), kafka_request->host, sizeof(kafka_request->host))) {
+		cw_bpf_debug("target write failed, aborting ebpf probe");
+		return 0;
+	}
+	set_assumed_app_id_arrays(kafka_request->host, kafka_request->msgs[0].sc.assumed_app_id, APM_ASSUMED_APP_ID_STRING_SIZE);
+
+//	copy_byte_arrays(kafka_request->apm_sc.assumed_app_id, kafka_request->msgs[0].sc.assumed_app_id, APM_ASSUMED_APP_ID_SIZE);
+
+//	if (!kafka_request->broker_addr){
+//		return 0;
+//	}
+//	unsigned char assumed_app_id[APM_TRACE_ID_SIZE];
+//	__builtin_memset(assumed_app_id, 0, sizeof(assumed_app_id));
+//
+//	set_app_id_numeric16(kafka_request->broker_addr, assumed_app_id);
+//	cw_bpf_debug("digit[%d]=%c", 0, assumed_app_id[0]);
+//	return 0;
+
+//	set_assumed_app_id_arrays2(kafka_request->broker_addr, assumed_app_id);
+	/*临时map绕一下*/
+//	struct kafka_request_t2 *kafka_request2 = bpf_map_lookup_elem(&kafka_request_storage_map2, &actual_id);
+//	if (kafka_request2 == NULL) {
+//		cw_bpf_debug("uprobe/WriteMessages: Failed to get kafka_request");
+//		return 0;
+//	}
+//	__builtin_memset(kafka_request2, 0, sizeof(struct kafka_request_t2));
+//
+//
+//	if (!get_go_string_from_user_ptr((void *) (na_ptr + 0x10), kafka_request2->host, sizeof(kafka_request2->host))) {
+//		cw_bpf_debug("target write failed, aborting ebpf probe");
+//		return 0;
+//	}
+//	set_assumed_app_id_arrays(kafka_request2->host, kafka_request2->apm_sc.assumed_app_id, APM_ASSUMED_APP_ID_STRING_SIZE);
+//	copy_byte_arrays(kafka_request2->apm_sc.assumed_app_id, kafka_request->msgs[0].sc.assumed_app_id, APM_ASSUMED_APP_ID_SIZE);
+	/*临时map绕一下*/
+
+
+
+//	__builtin_memcpy(psc.assumed_app_id, psc.assumed_app_id, APM_ASSUMED_APP_ID_SIZE);
+
+
+
+	cw_bpf_debug("Kafka BrokerAddr = %s", kafka_request->host);
+	struct apm_span_context *cw_psc = cw_get_parent_tracking_span();
+
+	if(cw_psc){
+		__builtin_memcpy(kafka_request->msgs[0].sc.trace_id, cw_psc->trace_id, APM_TRACE_ID_SIZE);
+	} else {
+		generate_random_bytes(kafka_request->msgs[0].sc.trace_id, APM_TRACE_ID_SIZE);
+	}
+
+//	generate_random_bytes(kafka_request->msgs[0].sc.span_id, APM_SPAN_ID_SIZE);
+
+
+	// Try to get a global topic from Writer
+	bool global_topic = get_go_string_from_user_ptr((void *) (writer + proc_info->kafka_writer_topic_pos), kafka_request->global_topic,
+	                                                sizeof(kafka_request->global_topic));
+
+	cw_bpf_debug("global_topic %d %s",global_topic,kafka_request->global_topic);
+
+
+	void *msg_ptr = msgs_array;
+	// Get header from per-cpu storage instead of stack to avoid stack limit
+	u32 header_storage_id = 0;
+	struct kafka_header_t *header = bpf_map_lookup_elem(&kafka_header_storage_map, &header_storage_id);
+	if (header == NULL) {
+		cw_bpf_debug("uprobe/WriteMessages: Failed to get header storage");
+		return 0;
+	}
+	// Zero the header
+	__builtin_memset(header, 0, sizeof(*header));
+
+	// This is hack to get the message size. This calculation is based on the following assumptions:
+	// 1. "Time" is the last field in the message struct. This looks to be correct for all the versions according to
+	//      https://github.com/segmentio/kafka-go/blob/v0.2.3/message.go#L24C2-L24C6
+	// 2. the time.Time struct is 24 bytes. This looks to be correct for all the reasnobaly latest versions according to
+	//      https://github.com/golang/go/blame/master/src/time/time.go#L135
+	// In the future if more libraries will need to get structs sizes we probably want to have similar
+	// mechanism to the one we have for the offsets
+	u16 msg_size = proc_info->kafka_message_time_pos + 8 + 8 + 8;
+	kafka_request->valid_messages = 0;
+	// Iterate over the messages
+	for (u8 i = 0; i < MAX_BATCH_SIZE; i++) {
+		if (i >= msgs_array_len) {
+			break;
+		}
+		// Optionally collect the topic, and always collect key
+		collect_kafka_attributes(msg_ptr, &kafka_request->msgs[i], !global_topic, proc_info->kafka_message_key_pos, proc_info->kafka_message_topic_pos);
+		if (global_topic) {
+			__builtin_memcpy(kafka_request->msgs[i].topic, kafka_request->global_topic, MAX_TOPIC_SIZE);
+		}
+		// Generate span id for each message
+		generate_random_bytes(kafka_request->msgs[i].sc.span_id, APM_SPAN_ID_SIZE);
+		if (i > 0) {
+			// Copy the trace id and trace flags from the first message. This means the sampling decision is done on the first message,
+			// and all the messages in the batch will have the same trace id and trace flags.
+//			kafka_request->msgs[i].sc.TraceFlags = kafka_request->msgs[0].sc.TraceFlags;
+			__builtin_memcpy(kafka_request->msgs[i].sc.trace_id, kafka_request->msgs[0].sc.trace_id, APM_TRACE_ID_SIZE);
+			__builtin_memcpy(kafka_request->msgs[i].sc.app_id, kafka_request->msgs[0].sc.app_id, APM_APP_ID_SIZE);
+			__builtin_memcpy(kafka_request->msgs[i].sc.assumed_app_id, kafka_request->msgs[0].sc.assumed_app_id, APM_APP_ID_SIZE);
+		}
+
+		// Build the header
+		if (build_contxet_header(header, &kafka_request->msgs[i].sc) != 0) {
+			cw_bpf_debug("uprobe/WriteMessages: Failed to build header");
+			return 0;
+		}
+		// Inject the header
+		inject_kafka_header(msg_ptr, header, proc_info->kafka_message_headers_pos);
+		// Zero the header for next iteration to avoid stale data
+		__builtin_memset(header, 0, sizeof(*header));
+		kafka_request->valid_messages++;
+		msg_ptr = msg_ptr + msg_size;
+	}
+
+	bpf_map_update_elem(&kafka_events, &key, kafka_request, 0);
+	// don't need to start tracking the span, as we don't have a context to propagate locally
+	return 0;
+}
+
+// This instrumentation attaches uprobe to the following function:
+// func (w *Writer) WriteMessages(ctx context.Context, msgs ...Message) error
+SEC("uprobe/WriteMessages")
+int uprobe_WriteMessages_Returns(struct pt_regs *ctx) {
+	void *key = (void *) GOROUTINE(ctx);
+
+	struct kafka_request_t *kafka_request = bpf_map_lookup_elem(&kafka_events, &key);
+	if (kafka_request == NULL) {
+		cw_bpf_debug("kafka_request is null\n");
+		return 0;
+	}
+	kafka_request->end_time = bpf_ktime_get_ns();
+
+
+	u32 zero = 0;
+	struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+	if (!e) {
+		return 0;
+	}
+	__u64 pid_tgid = bpf_get_current_pid_tgid();
+	__u32 tgid = pid_tgid >> 32;
+
+	e->protocol = PROTOCOL_KAFKA;
+	e->method = METHOD_PRODUCE;
+	e->trace_end = 0;
+	e->trace_start = 0;
+	e->start_at = kafka_request->start_time;
+	e->end_at = kafka_request->end_time;
+	e->duration = e->end_at - e->start_at;
+
+
+	bpf_probe_read(&e->target_addr, sizeof(kafka_request->host), kafka_request->host);
+
+	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;
+	}
+
+	for (u8 i = 0; i < MAX_BATCH_SIZE; i++) {
+		if (i >= kafka_request->valid_messages) {
+			break;
+		}
+		// Build payload from host, topic, and key
+//		build_kafka_payload(e, kafka_request, i);
+//		bpf_probe_read(&e->payload, 22, kafka_request->host);
+//		bpf_probe_read(&e->payload , 50, kafka_request->msgs[i].topic);
+
+//		cw_bpf_debug("tttttttttttttt %s", kafka_request->msgs[i].topic);
+//		cw_bpf_debug("kkkkkkkkkk %s", kafka_request->msgs[i].key);
+
+		// 填充 MQ 相关信息
+		bpf_probe_read(&e->mq.topic, sizeof(e->mq.topic), kafka_request->msgs[i].topic);
+		bpf_probe_read(&e->mq.key, sizeof(e->mq.key), kafka_request->msgs[i].key);
+
+//		// 复制 topic(找到实际长度,避免复制过多)
+//		u32 topic_len = 0;
+//		for (u32 j = 0; j < MAX_TOPIC_SIZE && j < sizeof(e->mq_topic) - 1; j++) {
+//			if (kafka_request->msgs[i].topic[j] == '\0') {
+//				topic_len = j;
+//				break;
+//			}
+//		}
+//		if (topic_len == 0) {
+//			topic_len = (MAX_TOPIC_SIZE < sizeof(e->mq_topic) - 1) ? MAX_TOPIC_SIZE : (sizeof(e->mq_topic) - 1);
+//		}
+//		if (topic_len > 0) {
+//			__builtin_memcpy(e->mq_topic, kafka_request->msgs[i].topic, topic_len);
+//			e->mq_topic[topic_len] = '\0';
+//		}
+//
+//		// 复制 key(找到实际长度,避免复制过多)
+//		u32 key_len = 0;
+//		for (u32 j = 0; j < MAX_KEY_SIZE && j < sizeof(e->mq_key) - 1; j++) {
+//			if (kafka_request->msgs[i].key[j] == '\0') {
+//				key_len = j;
+//				break;
+//			}
+//		}
+//		if (key_len == 0) {
+//			key_len = (MAX_KEY_SIZE < sizeof(e->mq_key) - 1) ? MAX_KEY_SIZE : (sizeof(e->mq_key) - 1);
+//		}
+//		__builtin_memcpy(e->mq_key, kafka_request->msgs[i].key, key_len);
+//		e->mq_key[key_len] = '\0';
+
+		cw_copy_byte_arrays(kafka_request->msgs[i].sc.assumed_app_id, e->assumed_app_id, APM_ASSUMED_APP_ID_SIZE);
+		cw_copy_byte_arrays(kafka_request->msgs[i].sc.span_id, e->span_id, APM_SPAN_ID_SIZE);
+		// 发送事件
+		SET_TRACE_METHOD(e);
+		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("[kafka] send success trace %llu",e->trace_id);
+		}
+
+//		kafka_request->msgs[i].sc.span_id;
+//		for (int j = 0; j < 1; ++j) {
+//			cw_bpf_debug("valid_messages %02x",kafka_request->msgs[i].sc.span_id[j]);
+//		}
+	}
+
+
+
+//	output_span_event(ctx, kafka_request, sizeof(*kafka_request), &kafka_request->msgs[0].sc);
+	bpf_map_delete_elem(&kafka_events, &key);
+	// don't need to stop tracking the span, as we don't have a context to propagate locally
+	return 0;
+}

+ 189 - 347
ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c

@@ -12,6 +12,8 @@
 #define MAX_METHOD_SIZE 10
 #define MAX_CONCURRENT 50
 
+#define CLIENT_MAP_KEY 1
+
 struct http_request_t {
     BASE_SPAN_PROPERTIES
     char host[MAX_HOSTNAME_SIZE];
@@ -46,6 +48,39 @@ struct
     __uint(max_entries, 1);
 } http_client_uprobe_storage_map SEC(".maps");
 
+
+struct {
+	__uint(type, BPF_MAP_TYPE_LRU_HASH);
+	__type(key, void*); // the headers ptr
+	__type(value, void*); // request key, goroutine or context ptr
+	__uint(max_entries, MAX_CONCURRENT);
+} http_headers SEC(".maps");
+
+struct cw_header_storage {
+	char tp[CW_HEADER_VAL_LENGTH];
+	char tp_str[CW_HEADER_KEY_LENGTH + 2 + CW_HEADER_VAL_LENGTH + 2];
+	char end[2];
+};
+
+struct cw_sys_header_storage {
+	char sys_val[CW_SYS_HEADER_VAL_LENGTH];
+	char sys_str[CW_SYS_HEADER_KEY_LENGTH + 2 + CW_SYS_HEADER_VAL_LENGTH + 2];
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct cw_header_storage));
+	__uint(max_entries, 1);
+} cw_header_storage_map SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct cw_sys_header_storage));
+	__uint(max_entries, 1);
+} cw_sys_header_storage_map SEC(".maps");
+
 //struct {
 //	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
 //} events SEC(".maps");
@@ -61,339 +96,7 @@ 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) {
-    // 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");
-            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");
-            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[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
-
-    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");
-            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");
-            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");
-            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");
-            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};
-
-    // 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_half(void* headers_ptr, char * header_str,struct ebpf_proc_info* proc_info) {
@@ -413,11 +116,12 @@ static __always_inline long cw_inject_header_half(void* headers_ptr, char * head
 
     // 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;
+    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 =
@@ -561,7 +265,9 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
     {
         return 0;
     }
-    void *key = get_consistent_key(ctx, context_ptr_val);
+	void *key = (void *)GOROUTINE(ctx);
+//	cw_bpf_debug("[start]:%llu",key);
+
     void *httpReq_ptr = bpf_map_lookup_elem(&http_events, &key);
     if (httpReq_ptr != NULL)
     {
@@ -624,12 +330,22 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
 //        return 0;
 //    }
 
-    // get host from Request
-    if (!get_go_string_from_user_ptr((void *)(req_ptr+proc_info->request_host_pos), httpReq->host, sizeof(httpReq->host))) {
-        cw_bpf_debug("uprobe_Transport_roundTrip: Failed to get host from Request");
+    // get host from Request.Host first
+    bool host_read_success = get_go_string_from_user_ptr((void *)(req_ptr+proc_info->request_host_pos), httpReq->host, sizeof(httpReq->host));
+    
+    // If Request.Host is empty or failed, try to get from Request.URL.Host
+    if (!host_read_success || httpReq->host[0] == '\0') {
+        // Get Request.URL pointer
+        void *url_ptr = 0;
+        if (!bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr+proc_info->url_ptr_pos))) {
+            // Use runtime-detected offset for url.URL.Host field
+            if (proc_info->url_host_pos > 0) {
+                if (!get_go_string_from_user_ptr((void *)(url_ptr + proc_info->url_host_pos), httpReq->host, sizeof(httpReq->host))) {
+                    cw_bpf_debug("[Client] Failed to get host from Request.URL.Host");
+                }
+            }
+        }
     }
-	// TODO set request_host_pos
-	cw_bpf_debug("[Client] httpReq->host:%llu",proc_info->request_host_pos);
 //	for (int i = 0; i < MAX_HOSTNAME_SIZE; ++i) {
 //		if (httpReq->host[i] == '\0') {
 //			break;
@@ -673,6 +389,10 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
     // get headers from Request
     void *headers_ptr = 0;
     bpf_probe_read(&headers_ptr, sizeof(headers_ptr), (void *)(req_ptr+proc_info->headers_ptr_pos));
+	if (headers_ptr) {
+		bpf_map_update_elem(&http_headers, &headers_ptr, &key, 0);
+	}
+
 //    long res = inject_header(headers_ptr, &httpReq->sc);
 //    long res = cw_inject_header(headers_ptr, &httpReq->apm_sc);
 //    if (res < 0) {
@@ -681,16 +401,17 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
 	char val[CW_HEADER_VAL_LENGTH];
 	span_context_to_cw_string(&httpReq->apm_sc, val);
 
-	long res2 = cw_inject_header_half(headers_ptr, val,proc_info);
-	if (res2 < 0) {
-		cw_bpf_debug("uprobe_Transport_roundTrip: Failed to inject header");
-		return -1;
-	}
+//	long res2 = cw_inject_header_half(headers_ptr, val,proc_info);
+//	if (res2 < 0) {
+//		cw_bpf_debug("uprobe_Transport_roundTrip: Failed to inject header");
+//		return -1;
+//	}
+
 // Write event
 	bpf_map_update_elem(&http_events, &key, httpReq, 0);
 	start_tracking_span(context_ptr_val, &httpReq->sc);
 
-	bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_DATA_GO_UPDATE_HEADER_UP_IDX);
+//	bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_DATA_GO_UPDATE_HEADER_UP_IDX);
 
 //	bpf_tail_call(ctx, &jmp_table2, 0);
 
@@ -715,7 +436,7 @@ PROGUP(go_update_header)(struct pt_regs *ctx) {
 //	if (!tail_calls_context){
 //		return -1;
 //	}
-	u32 map_id = 0;
+	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) {
@@ -829,7 +550,8 @@ int uprobe_Transport_roundTrip_Returns(struct pt_regs *ctx) {
     }
 
     void *req_ctx_ptr = get_Go_context(ctx, 2, proc_info->ctx_ptr_pos, false);
-    void *key = get_consistent_key(ctx, req_ctx_ptr);
+	void *key = (void *)GOROUTINE(ctx);
+//	bpf_printk("[end]:%llu",key);
 
     struct http_request_t *http_req_span = bpf_map_lookup_elem(&http_events, &key);
     if (http_req_span == NULL) {
@@ -853,4 +575,124 @@ int uprobe_Transport_roundTrip_Returns(struct pt_regs *ctx) {
 
     bpf_map_delete_elem(&http_events, &key);
     return 0;
+}
+
+
+SEC("uprobe/header_writeSubset")
+int uprobe_writeSubset(struct pt_regs *ctx) {
+	u64 headers_pos = 1;
+	void *headers_ptr = get_argument(ctx, headers_pos);
+
+	u64 io_writer_pos = 3;
+	void *io_writer_ptr = get_argument(ctx, io_writer_pos);
+
+	__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 0;
+	}
+
+	void **key_ptr = bpf_map_lookup_elem(&http_headers, &headers_ptr);
+	if (key_ptr) {
+		void *key = *key_ptr;
+		struct http_request_t *http_req_span = bpf_map_lookup_elem(&http_events, &key);
+		cw_bpf_debug("[writeSubset]:%llu",key);
+		if (http_req_span) {
+			// Get storage from per-cpu array map instead of stack
+			u32 map_id = 0;
+			struct cw_header_storage *storage = bpf_map_lookup_elem(&cw_header_storage_map, &map_id);
+			if (!storage) {
+				cw_bpf_debug("uprobe_writeSubset: Failed to get storage from map");
+				goto done;
+			}
+			__builtin_memset(storage, 0, sizeof(struct cw_header_storage));
+			span_context_to_cw_string(&http_req_span->apm_sc, storage->tp);
+			void *buf_ptr = 0;
+			bpf_probe_read(&buf_ptr, sizeof(buf_ptr),
+			               (void *) (io_writer_ptr + proc_info->io_writer_buf_ptr_pos)); // grab buf ptr
+			if (!buf_ptr) {
+				cw_bpf_debug("uprobe_writeSubset: Failed to get buf from io writer");
+				goto done;
+			}
+
+			s64 size = 0;
+			if (bpf_probe_read(&size, sizeof(s64), (void *) (io_writer_ptr + proc_info->io_writer_buf_ptr_pos +
+			                                                 offsetof(struct go_slice, cap)))) { // grab capacity
+				cw_bpf_debug("uprobe_writeSubset: Failed to get size from io writer");
+				goto done;
+			}
+
+			s64 len = 0;
+			if (bpf_probe_read(&len, sizeof(s64), (void *) (io_writer_ptr + proc_info->io_writer_n_pos))) { // grab len
+				cw_bpf_debug("uprobe_writeSubset: Failed to get len from io writer");
+				goto done;
+			}
+
+			if (len < (size - CW_HEADER_VAL_LENGTH - CW_HEADER_KEY_LENGTH - 4)) { // 4 = strlen(": ") + strlen("\r\n")
+				// Initialize tp_str with "cwtrace: "
+				__builtin_memcpy(storage->tp_str, "cwtrace: ", CW_HEADER_KEY_LENGTH + 2);
+				__builtin_memcpy(&storage->tp_str[CW_HEADER_KEY_LENGTH + 2], storage->tp, sizeof(storage->tp));
+				__builtin_memcpy(storage->end, "\r\n", 2);
+				__builtin_memcpy(&storage->tp_str[CW_HEADER_KEY_LENGTH + 2 + CW_HEADER_VAL_LENGTH], storage->end, sizeof(storage->end));
+				if (bpf_probe_write_user(buf_ptr + (len & 0x0ffff), storage->tp_str, sizeof(storage->tp_str))) {
+					cw_bpf_debug("uprobe_writeSubset: Failed to write trace parent key in buffer");
+					goto done;
+				}
+				len += CW_HEADER_KEY_LENGTH + 2 + CW_HEADER_VAL_LENGTH + 2;
+				if (bpf_probe_write_user((void *) (io_writer_ptr + proc_info->io_writer_n_pos), &len, sizeof(len))) {
+					cw_bpf_debug("uprobe_writeSubset: Failed to change io writer n");
+					goto done;
+				}
+			}
+			
+			// 插入 cwother header
+			// 直接从 proc_info->sysvc 获取值
+			u32 sys_map_id = 0;
+			struct cw_sys_header_storage *sys_storage = bpf_map_lookup_elem(&cw_sys_header_storage_map, &sys_map_id);
+			if (sys_storage) {
+				__builtin_memset(sys_storage, 0, sizeof(struct cw_sys_header_storage));
+				
+				// 从 proc_info->sysvc 读取值
+				// sysvc 格式:{app_name_len}:app_name:{SysTagLen}[:SysTag]
+				// 例如:08:eBPF-APP:00 或 08:eBPF-APP:03:tag
+				bpf_probe_read(sys_storage->sys_val, sizeof(sys_storage->sys_val), proc_info->sysvc);
+				
+				// 检查 sysvc 是否有值(第一个字符不是 '\0')
+				if (sys_storage->sys_val[0] != '\0') {
+					u32 sys_val_len = CW_SYS_HEADER_VAL_LENGTH;
+					#pragma unroll
+					for (int i = 0; i < CW_SYS_HEADER_VAL_LENGTH; i++) {
+						char c = sys_storage->sys_val[i];
+						if (c == '\0') {
+							sys_val_len = i;
+							break;
+						}
+					}
+
+					// 检查缓冲区空间
+					u32 total_header_len = CW_SYS_HEADER_KEY_LENGTH + 2 + sys_val_len + 2; // "cwother: " + sys_val + "\r\n"
+					if (len + total_header_len <= size) {
+						// Initialize sys_str with "cwother: "
+						__builtin_memcpy(sys_storage->sys_str, "cwother: ", CW_SYS_HEADER_KEY_LENGTH + 2);
+						__builtin_memcpy(&sys_storage->sys_str[CW_SYS_HEADER_KEY_LENGTH + 2], sys_storage->sys_val, CW_SYS_HEADER_VAL_LENGTH);
+						__builtin_memcpy(&sys_storage->sys_str[CW_SYS_HEADER_KEY_LENGTH + 2 + sys_val_len], "\r\n", 2);
+
+						if (bpf_probe_write_user(buf_ptr + (len & 0x0ffff), sys_storage->sys_str, total_header_len)) {
+							cw_bpf_debug("uprobe_writeSubset: Failed to write cwother header in buffer");
+							goto done;
+						}
+						len += total_header_len;
+						if (bpf_probe_write_user((void *) (io_writer_ptr + proc_info->io_writer_n_pos), &len, sizeof(len))) {
+							cw_bpf_debug("uprobe_writeSubset: Failed to change io writer n for cwother");
+							goto done;
+						}
+					}
+				}
+			}
+		}
+	}
+	done:
+	bpf_map_delete_elem(&http_headers, &headers_ptr);
+	return 0;
 }

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

@@ -0,0 +1,448 @@
+
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "arguments.h"
+#include "span_context.h"
+#include "go_types.h"
+#include "go_context.h"
+#include "uprobe.h"
+
+
+// #define MAX_SIZE 50
+#define MAX_CONCURRENT 50
+#define MAX_ERROR_LEN 128
+
+// #define PROTOCOL_GRPC 15
+
+struct grpc_client_request_t {
+    BASE_SPAN_PROPERTIES
+    char err_msg[MAX_ERROR_LEN];
+    char method[MAX_SIZE];
+    char target[MAX_SIZE];
+    u32 status_code;
+    u64 method_size;
+    u64 target_size;
+
+    struct apm_span_context apm_sc;
+    struct apm_span_context apm_psc;
+};
+
+// struct hpack_header_field {
+//     struct go_string_ot name;
+//     struct go_string_ot value;
+//     bool sensitive;
+// };
+
+struct {
+    __uint(type, BPF_MAP_TYPE_HASH);
+    __type(key, void *);
+    __type(value, struct grpc_client_request_t);
+    __uint(max_entries, MAX_CONCURRENT);
+} grpc_client_events SEC(".maps");
+
+struct {
+    __uint(type, BPF_MAP_TYPE_HASH);
+    __type(key, u32);
+    __type(value, struct apm_trace_key_t);
+    __uint(max_entries, MAX_CONCURRENT);
+} streamid_to_span_contexts SEC(".maps");
+
+// 用于 ClientConn_Invoke 函数的临时存储,避免栈空间超限
+struct {
+    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+    __uint(key_size, sizeof(u32));
+    __uint(value_size, sizeof(struct grpc_client_request_t));
+    __uint(max_entries, 1);
+} grpc_client_storage_map SEC(".maps");
+
+// 用于 loopyWriter_headerHandler 的临时存储
+struct header_handler_storage {
+    struct span_context sc;
+    char val[CW_HEADER_VAL_LENGTH];
+    struct hpack_header_field hf;
+};
+
+struct {
+    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+    __uint(key_size, sizeof(u32));
+    __uint(value_size, sizeof(struct header_handler_storage));
+    __uint(max_entries, 1);
+} header_handler_storage_map SEC(".maps");
+
+// Injected in init
+// volatile const u64 clientconn_target_ptr_pos;
+#define clientconn_target_ptr_pos  24 //使用固定值24即可,不再处理多版本场景。
+// volatile const u64 httpclient_nextid_pos;
+// u64 httpclient_nextid_pos = 404;    //处理多版本,通过变量获取吧
+// volatile const u64 headerFrame_streamid_pos;
+#define headerFrame_streamid_pos  0   //使用固定值0即可,不再处理多版本场景。
+// volatile const u64 headerFrame_hf_pos;
+#define headerFrame_hf_pos  8          //使用固定值8即可,不再处理多版本场景。
+// volatile const u64 error_status_pos;
+#define error_status_pos  0           //使用固定值0即可,不再处理多版本场景。
+// volatile const u64 status_s_pos;
+// static u64 status_s_pos  0;
+// volatile const u64 status_message_pos;
+#define status_message_pos  48        //使用固定值48即可,不再处理多版本场景。
+// volatile const u64 status_code_pos;
+// static u64 status_code_pos = 40;
+
+// volatile const bool write_status_supported;
+
+// This instrumentation attaches uprobe to the following function:
+// 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) {
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke \n");
+    // positions
+    u64 clientconn_pos = 1;
+    u64 method_ptr_pos = 4;
+    u64 method_len_pos = 5;
+
+    // struct go_iface go_context = {0};
+    // get_Go_context(ctx, 2, 0, true);
+    void *context_ptr_val = get_Go_context(ctx, 2, 0, true);
+    if (context_ptr_val == NULL)
+    {
+        return 0;
+    }
+
+    // Get key
+    void *key = (void *)GOROUTINE(ctx);
+    // 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) {
+        cw_bpf_debug("uprobe/ClientConn_Invoke already tracked with the current context");
+        return 0;
+    }
+
+    // 使用 per-cpu array map 存储大变量,避免栈空间超限
+    u32 zero = 0;
+    struct grpc_client_request_t *grpcReq = bpf_map_lookup_elem(&grpc_client_storage_map, &zero);
+    if (grpcReq == NULL) {
+        cw_bpf_debug("grpc:client:ClientConn_Invoke: failed to get storage");
+        return -1;
+    }
+    
+    // 清零并初始化
+    __builtin_memset(grpcReq, 0, sizeof(struct grpc_client_request_t));
+    grpcReq->start_time = bpf_ktime_get_ns();
+
+    // Read Method
+    void *method_ptr = get_argument(ctx, method_ptr_pos);
+    u64 method_len = (u64)get_argument(ctx, method_len_pos);
+    u64 method_size = sizeof(grpcReq->method);
+    method_size = method_size < method_len ? method_size : method_len;
+    bpf_probe_read(&grpcReq->method, method_size, method_ptr);
+    grpcReq->method_size = method_size;
+
+    // Read ClientConn.Target
+    void *clientconn_ptr = get_argument(ctx, clientconn_pos);
+    if (!get_go_string_from_user_ptr((void *)(clientconn_ptr + clientconn_target_ptr_pos),
+                                     grpcReq->target,
+                                     sizeof(grpcReq->target))) {
+        cw_bpf_debug("target write failed, aborting ebpf probe");
+        return 0;
+    }
+
+
+    grpcReq->target_size = sizeof(grpcReq->target);
+
+    struct apm_span_context *cw_psc = cw_get_parent_tracking_span();
+	if(cw_psc){
+        set_assumed_app_id_arrays(grpcReq->target, cw_psc->assumed_app_id, APM_ASSUMED_APP_ID_STRING_SIZE);
+        cw_save_parent_tracking_span(cw_psc);
+	}
+
+    // cw_bpf_debug("grpcReq->target is %s\n", grpcReq->target);
+
+    // start_span_params_t start_span_params = {
+    //     .ctx = ctx,
+    //     .go_context = &go_context,
+    //     .psc = &grpcReq->psc,
+    //     .sc = &grpcReq->sc,
+    //     .get_parent_span_context_fn = NULL,
+    //     .get_parent_span_context_arg = NULL,
+    // };
+    // start_span(&start_span_params);
+
+    // Write event
+    bpf_map_update_elem(&grpc_client_events, &key, grpcReq, 0);
+    start_tracking_span(context_ptr_val, &grpcReq->sc);
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke start_tracking_span\n");
+    return 0;
+}
+
+// This instrumentation attaches uprobe to the following function:
+// 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) {
+    // 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) {
+        cw_bpf_debug("event is NULL in ret probe");
+        return 0;
+    }
+
+    // if (!write_status_supported) {
+    //     goto done;
+    // }
+    // Getting the returned response (error)
+    // The status code is embedded 3 layers deep:
+    // Invoke() error
+    // the `error` interface concrete type here is a gRPC `internal.Error` struct
+    // type Error struct {
+    //   s *Status
+    // }
+    // The `Error` struct embeds a `Status` proto object
+    // type Status struct {
+    //   s *Status
+    // }
+    // The `Status` proto object contains a `Code` int32 field, which is what we want
+    // type Status struct {
+    //     Code int32
+    //     Message string
+    //     Details []*anypb.Any
+    // }
+    void *resp_ptr = get_argument(ctx, 2);
+    if (resp_ptr == 0) {
+        // err == nil
+        goto done;
+    }
+    void *status_ptr = 0;
+    // get `s` (Status pointer field) from Error struct
+    bpf_probe_read_user(&status_ptr, sizeof(status_ptr), (void *)(resp_ptr + error_status_pos));
+    // get `s` field from Status object pointer
+    void *s_ptr = 0;
+    bpf_probe_read_user(&s_ptr, sizeof(s_ptr), (void *)(status_ptr + status_s_pos));
+    // Get status code from Status.s pointer
+    bpf_probe_read_user(
+        &grpc_span->status_code, sizeof(grpc_span->status_code), (void *)(s_ptr + status_code_pos));
+    get_go_string_from_user_ptr(
+        (void *)(s_ptr + status_message_pos), grpc_span->err_msg, sizeof(grpc_span->err_msg));
+
+done:
+    // 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);
+    bpf_map_delete_elem(&grpc_client_events, &key);
+
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u64 pid = pid_tgid >> 32;
+
+	struct ebpf_proc_info *info = bpf_map_lookup_elem(&proc_info_map, &pid);
+	if (!info) {
+		return 0;
+	}
+
+	cw_bpf_debug("[Go] [uprobe/setNodeEnter]: proc_info_map::%ld, %d, %d\n", info->code_type);
+
+
+    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 == NULL) {
+        cw_bpf_debug("enter the trace_info is NULL\n");
+        trace_info = get_apm_trace_info_v3(trace_key,pid_tgid, pid, pid_tgid);
+    }
+    
+    u32 zero = 0;
+    struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+    if (!e) {
+        return 0;
+    }
+
+    // e->fd = 0;
+    // e->pid = pid;
+    e->protocol = PROTOCOL_GRPC;
+    e->trace_end = 0;
+    e->trace_start = 0;
+    // e->status = STATUS_UNKNOWN;
+    // e->method = METHOD_GRPC;
+    e->statement_id = 0;
+    e->payload_size = grpc_span->method_size;
+    if (trace_info) {
+        // 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);
+        cw_bpf_debug("e->trace_id is %llu\n", e->trace_id);
+    }
+    // 使用固定长度读取 target 信息,避免验证器复杂性
+    if (grpc_span->target_size > 0) {
+        // 使用固定的小长度来避免验证器问题
+        u32 fixed_size = 32; // 固定32字节,足够大多数target
+        if (grpc_span->target_size < fixed_size) {
+            fixed_size = (u32)grpc_span->target_size;
+        }
+        bpf_probe_read(&e->target_addr, fixed_size, grpc_span->target);
+    }
+    COPY_PAYLOAD(e->payload, grpc_span->method_size, grpc_span->method);
+
+    // 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);
+    // 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);
+    if (sc) {
+        cw_copy_byte_arrays(sc->assumed_app_id, e->assumed_app_id, APM_ASSUMED_APP_ID_SIZE);
+        cw_copy_byte_arrays(sc->span_id, e->span_id, APM_SPAN_ID_SIZE);
+    }
+    
+    e->end_at = bpf_ktime_get_ns();
+    e->start_at = grpc_span->start_time;
+    e->duration = e->end_at - e->start_at;
+	SET_TRACE_METHOD(e);
+    // 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);
+	}
+
+
+    // cw_bpf_debug("enter the uprobe_ClientConn_Invoke_Returns done\n");
+    return 0;
+}
+
+
+
+SEC("uprobe/loopyWriter_headerHandler")
+int uprobe_LoopyWriter_HeaderHandler(struct pt_regs *ctx) {
+    void *headerFrame_ptr = get_argument(ctx, 2);
+    // cw_bpf_debug("enter the get header handler storage\n");
+
+    __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 0;
+    }
+
+    u32 stream_id = 0;
+    bpf_probe_read(
+        &stream_id, sizeof(stream_id), (void *)(headerFrame_ptr + (headerFrame_streamid_pos)));
+    struct apm_trace_key_t *sc_ptr = bpf_map_lookup_elem(&streamid_to_span_contexts, &stream_id);
+    if (sc_ptr == NULL) {
+        bpf_map_delete_elem(&streamid_to_span_contexts, &stream_id);
+        return 0;
+    }
+    
+    // Get storage from per-cpu map
+    u32 zero = 0;
+    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);
+        cw_bpf_debug("Failed to get header handler storage\n");
+        return 0;
+    }
+    
+    // Generate span context
+    generate_random_bytes(storage->sc.TraceID, TRACE_ID_SIZE);
+    generate_random_bytes(storage->sc.SpanID, SPAN_ID_SIZE);
+
+
+    u32 map_id = 0;
+    struct grpc_client_request_t *grpcClientReq = bpf_map_lookup_elem(&grpc_client_storage_map, &map_id);
+    if (grpcClientReq == NULL)
+    {
+        bpf_map_delete_elem(&streamid_to_span_contexts, &stream_id);
+        cw_bpf_debug("uprobe_LoopyWriter_HeaderHandler: grpcClientReq is NULL");
+        return 0;
+    }
+
+    __builtin_memset(grpcClientReq, 0, sizeof(struct grpc_client_request_t));
+    grpcClientReq->start_time = bpf_ktime_get_ns();
+
+    // 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){
+		bpf_probe_read(&grpcClientReq->apm_psc, sizeof(grpcClientReq->apm_psc), cw_psc);
+		copy_byte_arrays(grpcClientReq->apm_psc.trace_id, grpcClientReq->apm_sc.trace_id, APM_TRACE_ID_SIZE);
+		generate_random_bytes(grpcClientReq->apm_sc.span_id, APM_SPAN_ID_SIZE);
+        copy_byte_arrays(grpcClientReq->apm_psc.assumed_app_id, grpcClientReq->apm_sc.assumed_app_id, APM_ASSUMED_APP_ID_SIZE);
+	}
+    bpf_map_delete_elem(&streamid_to_span_contexts, &stream_id);
+    u32 k0 = 0;
+	struct trace_conf_t *trace_conf = trace_conf_map__lookup(&k0);
+	if (trace_conf) {
+		copy_byte_arrays(trace_conf->host_id, grpcClientReq->apm_sc.host_id, APM_HOST_ID_SIZE);
+	}
+
+    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);
+    // 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);
+
+    // Strategy: Write key and value separately using write_target_data
+    // Both key and value can use write_target_data as it automatically manages memory allocation
+    // First write the key
+    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) {
+        cw_bpf_debug("Key data write failed\n");
+        return 0;
+    }
+    
+    // Prepare and write the value
+    char val[CW_HEADER_VAL_LENGTH];
+    __builtin_memset(val, 0, sizeof(val));
+    span_context_to_cw_string(&grpcClientReq->apm_sc, val);
+    
+    // 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) {
+        cw_bpf_debug("Val data write failed\n");
+        return 0;
+    }
+    
+    struct go_string_ot key_str = {.str = key_data_addr, .len = sizeof(tp_key)};
+    struct go_string_ot val_str = {.str = val_data_addr, .len = sizeof(val)};
+    
+    // Build header field
+    storage->hf.name = key_str;
+    storage->hf.value = val_str;
+    storage->hf.sensitive = false;
+    
+    // Append to slice
+    void *slice_ptr = (void *)(headerFrame_ptr + headerFrame_hf_pos);
+    cw_append_item_to_slice(&storage->hf, sizeof(storage->hf), slice_ptr);
+
+    return 0;
+}
+
+SEC("uprobe/http2Client_NewStream")
+// func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error)
+int uprobe_http2Client_NewStream(struct pt_regs *ctx) {
+    // 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;
+	}
+    // 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);
+    void *httpclient_ptr = get_argument(ctx, 1);
+    u32 nextid = 0;
+    bpf_probe_read(&nextid, sizeof(nextid), (void *)(httpclient_ptr + (info->httpclient_nextid_pos)));
+    // Get the span context from go context. The mapping is created in the Invoke probe,
+    // the context here is derived from the Invoke context.
+    // struct span_context *current_span_context = get_parent_span_context(&go_context);
+    struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
+    bpf_map_update_elem(&streamid_to_span_contexts, &nextid, &trace_key, 0);
+
+    return 0;
+}

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

@@ -0,0 +1,719 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "arguments.h"
+#include "go_types.h"
+#include "go_net.h"
+#include "span_context.h"
+#include "go_context.h"
+#include "uprobe.h"
+// #include "trace/start_span.h"
+
+// char __license[] SEC("license") = "Dual MIT/GPL";
+
+// #define MAX_SIZE 100
+#define MAX_CONCURRENT 50
+#define MAX_HEADERS 20
+#define MAX_HEADER_STRING 50
+
+
+struct grpc_request_t {
+    BASE_SPAN_PROPERTIES
+    char method[MAX_SIZE];
+    u32 status_code;
+    net_addr_t local_addr;
+    u8 has_status;
+    u32 stream_id;
+    u64 method_size;
+};
+
+struct {
+    __uint(type, BPF_MAP_TYPE_HASH);
+    __type(key, void *);
+    __type(value, struct grpc_request_t);
+    __uint(max_entries, MAX_CONCURRENT);
+} grpc_events SEC(".maps");
+
+struct {
+    __uint(type, BPF_MAP_TYPE_HASH);
+    __type(key, u32);
+    __type(value, struct grpc_request_t);
+    __uint(max_entries, MAX_CONCURRENT);
+} streamid_to_grpc_events SEC(".maps");
+
+struct {
+    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+    __uint(key_size, sizeof(u32));
+    __uint(value_size, sizeof(struct grpc_request_t));
+    __uint(max_entries, 1);
+} grpc_storage_map SEC(".maps");
+
+struct hpack_header_field {
+    struct go_string_ot name;
+    struct go_string_ot value;
+    bool sensitive;
+};
+
+// Injected in init
+// volatile const u64 stream_method_ptr_pos;
+// u64 stream_method_ptr_pos = 80; //需要处理多版本场景
+// volatile const u64 frame_fields_pos;
+#define frame_fields_pos  8       //使用固定值8即可,不再处理多版本场景。
+// volatile const u64 frame_stream_id_pod;
+#define frame_stream_id_pod  8    //使用固定值8即可,不再处理多版本场景。
+// volatile const u64 stream_id_pos;
+#define stream_id_pos  0          //使用固定值0即可,不再处理多版本场景。
+// volatile const u64 stream_ctx_pos;
+// u64 stream_ctx_pos  32        //需要做多版本处理
+// volatile const u64 server_stream_stream_pos;
+#define server_stream_stream_pos  0   //1.69之前版本用不到,1.69之后版本用这个
+// volatile const bool is_new_frame_pos;
+// bool is_new_frame_pos;          // < 1.60 为false,>= 1.60 为true,直接在 用户态赋值即可,不再做处理。
+// volatile const u64 status_s_pos;
+#define status_s_pos  0    //使用固定值即可,不再处理多版本场景。
+// volatile const u64 status_code_pos;
+#define status_code_pos  40   //使用固定值即可,不再做多版本处理
+// volatile const u64 http2server_peer_pos;
+// u64 http2server_peer_pos;
+// volatile const u64 peer_local_addr_pos;
+// u64 peer_local_addr_pos;
+
+// volatile const bool server_addr_supported;
+
+static __always_inline long
+dummy_extract_span_context_from_headers(void *stream_id, struct span_context *parent_span_context) {
+    return 0;
+}
+
+// handleStream handles gRPC stream telemetry.
+//
+// Arguments:
+//   - ctx: the pt_regs passed to the uprobe function
+//   - stream_ptr: pointer to the transport.Stream tracking the stream
+//   - go_context: the parsed Go context.Context
+//
+// Returns 0 on success, otherwise a negative error value in case of failure.
+static __always_inline int
+handleStream(struct pt_regs *ctx, void *stream_ptr, struct go_iface *go_context) {
+    __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;
+	}
+    if (go_context == NULL) {
+        cw_bpf_debug("grpc:server:handleStream: NULL go_context");
+        return -1;
+    }
+    // cw_bpf_debug("info->stream_method_ptr_pos is %d\n", info->stream_method_ptr_pos);
+    if (stream_ptr == NULL) {
+        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) {
+        cw_bpf_debug("grpc:server:handleStream: event already tracked");
+        return 0;
+    }
+
+    // Get parent context if exists
+    u32 stream_id = 0;
+    __u32 zero = 0;
+    long rc =
+        bpf_probe_read_user(&stream_id, sizeof(stream_id), (void *)(stream_ptr + stream_id_pos));
+    if (rc != 0) {
+        cw_bpf_debug("grpc:server:handleStream: failed to read stream ID");
+        return -2;
+    }
+
+    struct grpc_request_t *grpcReq = bpf_map_lookup_elem(&streamid_to_grpc_events, &stream_id);
+    if (grpcReq == NULL) {
+        // No parent span context, generate new span context
+        u32 zero = 0;
+        grpcReq = bpf_map_lookup_elem(&grpc_storage_map, &zero);
+        if (grpcReq == NULL) {
+            cw_bpf_debug("grpc:server:handleStream: failed to get grpcReq");
+            return 0;
+        }
+    }
+
+    grpcReq->start_time = bpf_ktime_get_ns();
+    grpcReq->stream_id = stream_id;
+
+    // start_span_params_t start_span_params = {
+    //     .ctx = ctx,
+    //     .sc = &grpcReq->sc,
+    //     .psc = &grpcReq->psc,
+    //     .go_context = go_context,
+    //     // The parent span context is set by operateHeader probe
+    //     .get_parent_span_context_fn = dummy_extract_span_context_from_headers,
+    //     .get_parent_span_context_arg = NULL,
+    // };
+    // start_span(&start_span_params);
+
+    // Set attributes
+    void *method_ptr = stream_ptr + info->stream_method_ptr_pos;
+    bool parsed_method =
+        get_go_string_from_user_ptr(method_ptr, grpcReq->method, sizeof(grpcReq->method));
+    if (!parsed_method) {
+        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;
+    }
+
+    // cw_bpf_debug("grpc:server:handleStream: get the method is %s\n", grpcReq->method);
+
+    // if (server_addr_supported) {
+    //     void *http2server = get_argument(ctx, 3);
+    //     if (http2server != NULL) {
+    //         void *local_addr_ptr = 0;
+    //         void *local_addr_pos = http2server + http2server_peer_pos + peer_local_addr_pos;
+    //         bpf_probe_read_user(
+    //             &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 {
+    //         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) {
+        cw_bpf_debug("grpc:server:handleStream: failed to update event");
+        return -4;
+    }
+    start_tracking_span(go_context->data, &grpcReq->sc);
+
+
+    //处理http请求之前,确认进程信息是否存在
+    __u64 id = bpf_get_current_pid_tgid();
+    __u32 pid = id >> 32;
+    struct ebpf_proc_info *proc_info = bpf_map_lookup_elem(&proc_info_map, &pid);
+    if (!proc_info) {
+        cw_bpf_debug("[Trace End in l7][Response][HTTP]:no proc info. pid:%d \n",pid);
+        return 0;
+    }
+
+    // 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);
+    // if (cw_parent_span_context == NULL) {
+    //     return -1;
+    // }
+    // __builtin_memset(cw_parent_span_context, 0, sizeof(struct apm_span_context));
+    // generate_random_bytes(cw_parent_span_context->trace_id, TRACE_ID_SIZE);
+    // cw_save_parent_tracking_span(cw_parent_span_context);
+
+    struct l7_request_key k = {};
+    k.pid = pid;
+    k.fd = 0;
+    k.is_tls = 0;
+    k.stream_id = stream_id;
+
+    struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+    if (!e) {
+        return 0;
+    }
+
+    e->fd = k.fd;
+    e->pid = k.pid;
+    e->status = STATUS_UNKNOWN;
+    e->method = METHOD_UNKNOWN;
+    e->statement_id = 0;
+    
+    // 拷贝 grpcReq->method 到 payload 并设置 payload_size
+    // 手动计算字符串长度(在 eBPF 中不能使用 strlen)
+    u64 method_len = 0;
+    for (int i = 0; i < MAX_SIZE; i++) {
+        if (grpcReq->method[i] == '\0') {
+            method_len = i;
+            break;
+        }
+    }
+    // 如果没有找到 '\0',使用最大长度
+    if (method_len == 0) {
+        method_len = MAX_SIZE - 1;
+    }
+    
+    grpcReq->method_size = method_len;
+    e->payload_size = method_len;
+    COPY_PAYLOAD(e->payload, method_len, grpcReq->method);
+
+    // 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);
+    
+    SET_TRACE_START(e, PROTOCOL_GRPC);
+    e->trace_type = 1;
+    e->trace_id = trace_info.trace_id;
+
+
+    //不发送payload
+    bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
+
+    return 0;
+}
+
+// writeStatus writes the OTel status to any active spans.
+//
+// Arguments:
+//   - ctx: the pt_regs passed to the uprobe function
+//   - status_ptr: pointer to the status.Stream holding the status info
+//
+// 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) {
+//         cw_bpf_debug("grpc:server:writeStatus: NULL status_ptr");
+//         return -1;
+//     }
+
+//     void *key = (void *)GOROUTINE(ctx);
+
+//     struct grpc_request_t *req_ptr = bpf_map_lookup_elem(&grpc_events, &key);
+//     if (req_ptr == NULL) {
+//         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) {
+//         cw_bpf_debug("grpc:server:handleStream: failed to read Status.s");
+//         return -3;
+//     }
+
+//     // Get status code from Status.s pointer
+//     rc = bpf_probe_read_user(
+//         &req_ptr->status_code, sizeof(req_ptr->status_code), (void *)(s_ptr + status_code_pos));
+//     if (rc != 0) {
+//         cw_bpf_debug("grpc:server:handleStream: failed to read status code");
+//         return -4;
+//     }
+//     req_ptr->has_status = true;
+
+//     return 0;
+// }
+
+// This instrumentation attaches uprobe to the following function:
+// func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream, trInfo *traceInfo)
+//
+// This is only compatible with versions < 1.69.0 of the Server.
+SEC("uprobe/server_handleStream")
+int uprobe_server_handleStream(struct pt_regs *ctx) {
+    // cw_bpf_debug("enter the uprobe_server_handleStream");
+    u64 stream_pos = 4;
+    void *stream_ptr = get_argument(ctx, stream_pos);
+    // cw_bpf_debug("enter uprobe_server_handleStream\n");
+    // Get key
+    __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)
+    {
+	    cw_bpf_debug("[uprobe_HandlerFunc_ServeHTTP] no proc info");
+        return 0;
+    }
+    struct go_iface go_context = {0};
+    get_Go_context(ctx, stream_pos, proc_info->ctx_ptr_pos, false);
+
+    return handleStream(ctx, stream_ptr, &go_context);
+}
+
+// UPROBE_RETURN(server_handleStream, struct grpc_request_t, grpc_events) 
+SEC("uprobe/server_handleStream")
+int uprobe_server_handleStream_Returns(struct pt_regs *ctx) {  
+    // cw_bpf_debug("enter the uprobe_server_handleStream return");     
+    void *key = (void *)GOROUTINE(ctx);
+    __u64 id = bpf_get_current_pid_tgid();
+	__u32 zero = 0;
+    __u32 fd = 0;
+	__u32 pid, tid;
+	__u32 http_status = 200;
+
+	pid = id >> 32;
+	tid =  (__u32)id;
+    // cw_bpf_debug("enter uprobe_server_handleStream_Returns\n");
+
+    struct l7_request_key k = {};
+    k.pid = pid;
+    k.fd = fd;
+    k.is_tls = 0;
+    k.stream_id = -1;    
+    struct grpc_request_t *event = bpf_map_lookup_elem(&grpc_events, &key);
+    if (event == NULL) {
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2Return: event is NULL");
+        return -5;
+    }
+    event->end_time = bpf_ktime_get_ns();
+    // output_span_event(ctx, event, sizeof(struct grpc_request_t), &event->sc);
+    stop_tracking_span(&event->sc, &event->psc);
+    bpf_map_delete_elem(&grpc_events, &key);
+	
+    struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
+	struct apm_trace_info_t * start_trace_info = get_apm_trace_info_by_trace_key(trace_key);
+    if (!start_trace_info) {
+		return -1;
+	}
+
+    __u64 trace_id = start_trace_info->trace_id;
+	__u32 event_count = cw_get_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);
+
+    // 发送事件到用户空间 start
+    struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+    if (!e) {
+	    cw_clear_trace(pid, tid, fd);
+        return -1;
+    }
+	// parent sc
+	struct apm_span_context *cw_psc = cw_get_parent_tracking_span_by_trace_key(start_trace_info->trace_key);
+	if(cw_psc){
+		cw_copy_byte_arrays(cw_psc->trace_id, e->trace_id_from, APM_TRACE_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->assumed_app_id, e->called_id, APM_ASSUMED_APP_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->instance_id, e->instance_id_from, APM_INSTANCE_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->app_id, e->app_id_from, APM_APP_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->span_id, e->span_id_from, APM_SPAN_ID_SIZE);
+        cw_copy_byte_arrays(cw_psc->type_from, e->type_from, APM_TYPE_FROM_SIZE);
+	}
+    // struct l7_request *req = bpf_map_lookup_elem(&active_l7_requests, &k);
+    // if (!req)
+    // {
+	//     cw_clear_trace(pid, tid, fd);
+    //     return 0;
+    // }
+	// e->start_at = req->ns;
+    e->start_at = event->start_time;
+	// cw_bpf_debug("req->ns:%llu",req->ns);
+	e->end_at = bpf_ktime_get_ns();
+    e->duration = e->end_at - e->start_at;
+    e->status = http_status;
+    e->pid = k.pid;
+    e->fd = k.fd;
+    // e->connection_timestamp = get_connection_timestamp(k.pid, k.fd);
+    SET_TRACE_END(e, PROTOCOL_GRPC);
+    e->trace_type = 1;
+    e->trace_id = trace_id;
+    e->payload_size = 0;
+    e->event_count = event_count;
+    
+    e->payload_size = event->method_size;
+    COPY_PAYLOAD(e->payload, event->method_size, event->method);
+    // bpf_map_delete_elem(&active_l7_requests, &k);
+	// 清除事件计数
+	bpf_map_delete_elem(&trace_event_count_heap, &trace_id);
+	// 清除业务层trace信息
+	clear_parent_span_context_by_trace_key(start_trace_info->trace_key);
+	// 清除trace信息
+	cw_clear_trace(pid, tid, fd);
+    // cw_bpf_debug("socket accept bytes_sent cid.pid=%d, cid.fd=%d\n", cid.pid, cid.fd);
+    // struct accept_connection *accept_conn = bpf_map_lookup_elem(&active_accepts, &cid);
+    // if (accept_conn) {
+    //     cw_bpf_debug("socket accept bytes_sent after cid.pid=%d, cid.fd=%d\n", cid.pid, cid.fd);
+    //     cw_bpf_debug("rock enter the  accept_conn function cid.pid=%d, cid.fd=%d\n", cid.pid, cid.fd);
+    //     e->sport = accept_conn->sport;
+    //     e->dport = accept_conn->dport;
+    //     __builtin_memcpy(&e->saddr, &accept_conn->saddr, sizeof(e->saddr));
+    //     __builtin_memcpy(&e->daddr, &accept_conn->daddr, sizeof(e->daddr));
+    // }
+    //不发送payload
+    bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
+
+    // cw_bpf_debug("stop get apm data\n");
+    return 0;                                                                                  
+}
+
+// This instrumentation attaches uprobe to the following function:
+// func (s *Server) handleStream(t transport.ServerTransport, stream *transport.ServerStream)
+// https://github.com/grpc/grpc-go/blob/317271b232677b7869576a49855b01b9f4775d67/server.go#L1735
+//
+// This is only compatible with versions > 1.69.0 of the Server.
+SEC("uprobe/server_handleStream2")
+int uprobe_server_handleStream2(struct pt_regs *ctx) {
+
+    __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;
+	}
+    cw_bpf_debug("info->stream_ctx_pos is %d\n", info->stream_ctx_pos);
+    u64 server_stream_pos = 4;
+    // cw_bpf_debug("enter uprobe_server_handleStream2\n");
+    void *server_stream_ptr = get_argument(ctx, server_stream_pos);
+    if (server_stream_ptr == NULL) {
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to get ServerStream arg");
+        return -1;
+    }
+
+    void *stream_ptr;
+    long rc = bpf_probe_read_user(
+        &stream_ptr, sizeof(stream_ptr), (void *)(server_stream_ptr + server_stream_stream_pos));
+    if (rc != 0) {
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to read stream_ptr");
+        return -2;
+    }
+
+    struct go_iface go_context = {0};
+    rc = bpf_probe_read_user(
+        &go_context.type, sizeof(go_context.type), (void *)(stream_ptr + info->stream_ctx_pos));
+    if (rc != 0) {
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to read context type");
+        return -3;
+    }
+
+    rc = bpf_probe_read_user(&go_context.data,
+                             sizeof(go_context.data),
+                             get_go_interface_instance(stream_ptr + info->stream_ctx_pos));
+    if (rc != 0) {
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2: failed to read context data");
+        return -4;
+    }
+
+    return handleStream(ctx, stream_ptr, &go_context);
+}
+
+// This instrumentation attaches a return uprobe to the following function:
+// func (s *Server) handleStream(t transport.ServerTransport, stream *transport.ServerStream)
+// https://github.com/grpc/grpc-go/blob/317271b232677b7869576a49855b01b9f4775d67/server.go#L1735
+//
+// This is only compatible with versions > 1.69.0 of the Server.
+SEC("uprobe/server_handleStream2")
+int uprobe_server_handleStream2_Returns(struct pt_regs *ctx) {
+    u64 server_stream_pos = 4;
+    void *server_stream_ptr = get_argument(ctx, server_stream_pos);
+    void *key = NULL;
+    if (server_stream_ptr == NULL) {
+        // We might fail to get the pointer for versions of Go which use register ABI, as this function does not return anything.
+        // This is not an error in that case so we can just go to the lookup which will happen by goroutine.
+        goto lookup;
+    }
+
+    void *stream_ptr;
+    long rc = bpf_probe_read_user(
+        &stream_ptr, sizeof(stream_ptr), (void *)(server_stream_ptr + server_stream_stream_pos));
+    if (rc != 0) {
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2Return: failed to read stream_ptr");
+        return -2;
+    }
+
+lookup:
+    key = (void *)GOROUTINE(ctx);
+    struct grpc_request_t *event = bpf_map_lookup_elem(&grpc_events, &key);
+    if (event == NULL) {
+        cw_bpf_debug("grpc:server:uprobe/server_handleStream2Return: event is NULL");
+        return -5;
+    }
+    event->end_time = bpf_ktime_get_ns();
+    // output_span_event(ctx, event, sizeof(struct grpc_request_t), &event->sc);
+    stop_tracking_span(&event->sc, &event->psc);
+    bpf_map_delete_elem(&grpc_events, &key);
+
+    __u64 id = bpf_get_current_pid_tgid();
+	__u32 zero = 0;
+    __u32 fd = 0;
+	__u32 pid, tid;
+	__u32 http_status = 200;
+
+	pid = id >> 32;
+	tid =  (__u32)id;
+    // cw_bpf_debug("enter uprobe_server_handleStream_Returns\n");
+
+    struct l7_request_key k = {};
+    k.pid = pid;
+    k.fd = fd;
+    k.is_tls = 0;
+    k.stream_id = -1;    
+
+    struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
+	struct apm_trace_info_t * start_trace_info = get_apm_trace_info_by_trace_key(trace_key);
+    if (!start_trace_info) {
+		return -1;
+	}
+
+    __u64 trace_id = start_trace_info->trace_id;
+	__u32 event_count = cw_get_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);
+
+    // 发送事件到用户空间 start
+    struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
+    if (!e) {
+	    cw_clear_trace(pid, tid, fd);
+        return -1;
+    }
+	// parent sc
+	struct apm_span_context *cw_psc = cw_get_parent_tracking_span_by_trace_key(start_trace_info->trace_key);
+	if(cw_psc){
+		cw_copy_byte_arrays(cw_psc->trace_id, e->trace_id_from, APM_TRACE_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->assumed_app_id, e->called_id, APM_ASSUMED_APP_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->instance_id, e->instance_id_from, APM_INSTANCE_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->app_id, e->app_id_from, APM_APP_ID_SIZE);
+		cw_copy_byte_arrays(cw_psc->span_id, e->span_id_from, APM_SPAN_ID_SIZE);
+        cw_copy_byte_arrays(cw_psc->type_from, e->type_from, APM_TYPE_FROM_SIZE);
+	}
+    // struct l7_request *req = bpf_map_lookup_elem(&active_l7_requests, &k);
+    // if (!req)
+    // {
+	//     cw_clear_trace(pid, tid, fd);
+    //     return 0;
+    // }
+	// e->start_at = req->ns;
+    e->start_at = event->start_time;
+	// cw_bpf_debug("req->ns:%llu",req->ns);
+	e->end_at = bpf_ktime_get_ns();
+    e->duration = e->end_at - e->start_at;
+    e->status = http_status;
+    e->pid = k.pid;
+    e->fd = k.fd;
+    // e->connection_timestamp = get_connection_timestamp(k.pid, k.fd);
+    SET_TRACE_END(e, PROTOCOL_GRPC);
+    e->trace_type = 1;
+    e->trace_id = trace_id;
+    e->payload_size = 0;
+    e->event_count = event_count;
+    
+    e->payload_size = event->method_size;
+    COPY_PAYLOAD(e->payload, event->method_size, event->method);
+    // bpf_map_delete_elem(&active_l7_requests, &k);
+	// 清除事件计数
+	bpf_map_delete_elem(&trace_event_count_heap, &trace_id);
+	// 清除业务层trace信息
+	clear_parent_span_context_by_trace_key(start_trace_info->trace_key);
+	// 清除trace信息
+	cw_clear_trace(pid, tid, fd);
+    // cw_bpf_debug("socket accept bytes_sent cid.pid=%d, cid.fd=%d\n", cid.pid, cid.fd);
+    // struct accept_connection *accept_conn = bpf_map_lookup_elem(&active_accepts, &cid);
+    // if (accept_conn) {
+    //     cw_bpf_debug("socket accept bytes_sent after cid.pid=%d, cid.fd=%d\n", cid.pid, cid.fd);
+    //     cw_bpf_debug("rock enter the  accept_conn function cid.pid=%d, cid.fd=%d\n", cid.pid, cid.fd);
+    //     e->sport = accept_conn->sport;
+    //     e->dport = accept_conn->dport;
+    //     __builtin_memcpy(&e->saddr, &accept_conn->saddr, sizeof(e->saddr));
+    //     __builtin_memcpy(&e->daddr, &accept_conn->daddr, sizeof(e->daddr));
+    // }
+    //不发送payload
+    bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, e, sizeof(*e));
+
+    // cw_bpf_debug("stop get apm data\n");
+    return 0;
+}
+
+// func (d *http2Server) operateHeader(frame *http2.MetaHeadersFrame) error
+// for version 1.60 and above:
+// 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) {
+    // 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);
+	if (!info) {
+		return -1;
+	}
+    s32 find_w3c = 0;
+    void *arg4 = get_argument(ctx, 4);
+    void *arg2 = get_argument(ctx, 2);
+    void *frame_ptr = info->is_new_frame_pos ? arg4 : arg2;
+    struct go_slice header_fields = {};
+    bpf_probe_read(&header_fields, sizeof(header_fields), (void *)(frame_ptr + frame_fields_pos));
+    char key[CW_HEADER_KEY_LENGTH + 1] = "cwtrace";
+    // 确保字符串以 null 结尾
+    key[CW_HEADER_KEY_LENGTH] = '\0';
+    // 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);
+    if (cw_parent_span_context == NULL) {
+        return -1;
+    }
+    __builtin_memset(cw_parent_span_context, 0, sizeof(struct apm_span_context));
+    
+    // 优化循环:减少复杂度,提前退出
+    for (s32 i = 0; i < MAX_HEADERS && i < header_fields.len; i++) {
+        struct hpack_header_field hf = {};
+        long res = bpf_probe_read(&hf, sizeof(hf), (void *)(header_fields.cap + (i * sizeof(hf))));
+        if (res != 0) {
+            continue; // 读取失败,跳过
+        }
+        
+        // 简化条件判断
+        if (hf.name.len != CW_HEADER_KEY_LENGTH || hf.value.len != CW_HEADER_VAL_LENGTH) {
+            continue;
+        }
+        // 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;
+        }
+        // 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';
+
+        // 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;
+            // cw_bpf_debug("found traceparent header");
+            // 执行字符串到span context的转换
+            cw_string_to_span_context(hf.value.str, cw_parent_span_context);
+            // 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);
+        cw_bpf_debug("enter uprobe_http2Server_operateHeader, generate the traceid\n");
+    }
+    cw_save_parent_tracking_span(cw_parent_span_context);
+    return 0;
+}
+
+// func (ht *http2Server) WriteStatus(s *Stream, st *status.Status)
+// https://github.com/grpc/grpc-go/blob/bcf9171a20e44ed81a6eb152e3ca9e35b2c02c5d/internal/transport/http2_server.go#L1049
+//
+// 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) {
+//     // cw_bpf_debug("enter uprobe_http2Server_WriteStatus\n");
+//     void *status_ptr = get_argument(ctx, 3);
+//     return writeStatus(ctx, status_ptr);
+// }
+
+// // func (ht *http2Server) writeStatus(s *Stream, st *status.Status)
+// // https://github.com/grpc/grpc-go/blob/317271b232677b7869576a49855b01b9f4775d67/internal/transport/http2_server.go#L1045
+// //
+// // This is only compatible with versions > 1.69.0 of the Server.
+// SEC("uprobe/http2Server_WriteStatus2")
+// int uprobe_http2Server_WriteStatus2(struct pt_regs *ctx) {
+//     // 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) {
+//         cw_bpf_debug("grpc:server:uprobe/http2Server_WriteStatus2: failed to get ServerStream arg");
+//         return -1;
+//     }
+
+//     void *stream_ptr;
+//     long rc = bpf_probe_read_user(
+//         &stream_ptr, sizeof(stream_ptr), (void *)(server_stream_ptr + server_stream_stream_pos));
+//     if (rc != 0) {
+//         cw_bpf_debug("grpc:server:uprobe/http2Server_WriteStatus2: failed to read stream_ptr");
+//         return -2;
+//     }
+
+//     void *status_ptr = get_argument(ctx, 3);
+//     return writeStatus(ctx, status_ptr);
+// }

+ 101 - 237
ebpftracer/ebpf/utrace/go/net/server.probe.bpf.c

@@ -57,7 +57,7 @@ struct {
 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
 	__uint(key_size, sizeof(u32));
 	__uint(value_size, sizeof(struct map_bucket));
-	__uint(max_entries, 1);
+	__uint(max_entries, MAX_CONCURRENT);
 } golang_mapbucket_storage_map SEC(".maps");
 
 struct {
@@ -67,19 +67,19 @@ struct {
 	__uint(max_entries, MAX_CONCURRENT);
 } header_range SEC(".maps");
 
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__uint(key_size, sizeof(u32));
-	__uint(value_size, sizeof(struct span_context));
-	__uint(max_entries, 1);
-} parent_span_context_storage_map SEC(".maps");
+//struct {
+//	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+//	__uint(key_size, sizeof(u32));
+//	__uint(value_size, sizeof(struct span_context));
+//	__uint(max_entries, 1);
+//} parent_span_context_storage_map SEC(".maps");
 
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__uint(key_size, sizeof(u32));
-	__uint(value_size, sizeof(struct apm_span_context));
-	__uint(max_entries, 1);
-} cw_parent_span_context_storage_map SEC(".maps");
+//struct {
+//	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+//	__uint(key_size, sizeof(u32));
+//	__uint(value_size, sizeof(struct apm_span_context));
+//	__uint(max_entries, 1);
+//} cw_parent_span_context_storage_map SEC(".maps");
 
 struct {
 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
@@ -103,6 +103,19 @@ 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");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_LRU_HASH);
+	__uint(key_size, sizeof(u64));
+	__uint(value_size, sizeof(char [CW_SYS_HEADER_VAL_LENGTH]));
+	__uint(max_entries, MAX_CONCURRENT);
+} http_server_context_sys_headers SEC(".maps");
 
 // Injected in init
 // volatile const u64 ctx_ptr_pos;
@@ -118,228 +131,28 @@ struct {
 //}
 //}
 
-static __always_inline struct span_context *extract_context_from_req_headers(void *headers_ptr_ptr) {
-	void *headers_ptr;
-	long res;
-	res = bpf_probe_read(&headers_ptr, sizeof(headers_ptr), headers_ptr_ptr);
-	if (res < 0) {
-		return NULL;
-	}
-	u64 headers_count = 0;
-	res = bpf_probe_read(&headers_count, sizeof(headers_count), headers_ptr);
-	if (res < 0) {
-		return NULL;
-	}
-	if (headers_count == 0) {
-		return NULL;
-	}
-	unsigned char log_2_bucket_count;
-	res = bpf_probe_read(&log_2_bucket_count, sizeof(log_2_bucket_count), headers_ptr + 9);
-	if (res < 0) {
-		return NULL;
-	}
-	u64 bucket_count = 1 << log_2_bucket_count;
-	__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 0;
-    }
-	void *header_buckets;
-	res = bpf_probe_read(&header_buckets, sizeof(header_buckets), (void *) (headers_ptr + proc_info->buckets_ptr_pos));
-	if (res < 0) {
-		return NULL;
-	}
-	u32 map_id = 0;
-	struct map_bucket *map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
-	if (!map_value) {
-		return NULL;
-	}
-	for (u32 j = 0; j < MAX_BUCKETS; j++) {
-		if (j >= bucket_count) {
-			break;
-		}
-		res = bpf_probe_read(map_value, sizeof(struct map_bucket), header_buckets + (j * sizeof(struct map_bucket)));
-		if (res < 0) {
-			continue;
-		}
-
-//		for (u32 i = 0; i < 8; i++) {
-		u32 i = 0;
-			if (map_value->tophash[i] != 0 && map_value->keys[i].len == CW_HEADER_KEY_LENGTH) {
-//				char current_header_key[CW_HEADER_KEY_LENGTH];
-//				bpf_probe_read(current_header_key, sizeof(current_header_key), map_value->keys[i].str);
-			for (int ii = 0; ii < CW_HEADER_KEY_LENGTH; ii++) {
-				if (map_value->keys[i].str[ii] != CW_HEADER_KEY_VAL[i] && map_value->keys[i].str[ii] != CW_HEADER_KEY_UFIRST_VAL[ii]) {
-//					goto outer_loop;
-				}
-			}
-//				continue;
-			}
-//			if (map_value->keys[i].len != CW_HEADER_KEY_LENGTH) {
-//				continue;
-//			}
-
-//			char current_header_key[CW_HEADER_KEY_LENGTH];
-//			bpf_probe_read(current_header_key, sizeof(current_header_key), map_value->keys[i].str);
 
 
-//
-//			struct header_key * current_header_key = bpf_map_lookup_elem(&header_keys_map, &map_id);
-//			if (current_header_key) {
-//				bpf_probe_read(current_header_key->str, sizeof(current_header_key->str),  map_value->keys[i].str);
-//			}
-//
-//			for (int i = 0; i < CW_HEADER_KEY_LENGTH; i++) {
-//				if (current_header_key[i] != CW_HEADER_KEY_VAL[i] && current_header_key[i] != CW_HEADER_KEY_UFIRST_VAL[i]) {
-//					goto outer_loop;
-//				}
-//			}
-//
-//			void *traceparent_header_value_ptr = map_value->values[i].array;
-//			struct go_string_ot traceparent_header_value_go_str;
-//			res = bpf_probe_read(&traceparent_header_value_go_str, sizeof(traceparent_header_value_go_str),
-//			                     traceparent_header_value_ptr);
-//			// 00-95b5ec2b81e2374a4a27ce36ab71d349-18f65a5a3ab22213-02
-//			cw_bpf_debug("traceparent_header_value_go_str.str:%s", traceparent_header_value_go_str.str);
-//
-//			if (res < 0) {
-//				return NULL;
-//			}
-//
-//			// char traceparent_header_value[130];
-//			int key = 0;
-//			char *traceparent_header_value;
-//			traceparent_header_value = bpf_map_lookup_elem(&go_large_array_map, &key);
-//
-//			res = bpf_probe_read(&traceparent_header_value, sizeof(traceparent_header_value),
-//			                     traceparent_header_value_go_str.str);
-//
-//
-//			if (res < 0) {
-//				return NULL;
-//			}
-//			struct span_context *parent_span_context = bpf_map_lookup_elem(&parent_span_context_storage_map, &map_id);
-//			if (!parent_span_context) {
-//				cw_bpf_debug("no parent_span_context");
-//				return NULL;
-//			}
-//			struct apm_span_context *cw_parent_span_context = bpf_map_lookup_elem(&cw_parent_span_context_storage_map, &map_id);
-//			if (!cw_parent_span_context) {
-//				cw_bpf_debug("no cw_parent_span_context");
-//				return NULL;
-//			}
-//
-//			w3c_string_to_span_context(traceparent_header_value, parent_span_context);
-//
-//			cw_string_to_span_context(traceparent_header_value, cw_parent_span_context);
-//
-//			// 保存sc
-//			cw_save_parent_tracking_span(cw_parent_span_context);
-//
-//			return parent_span_context;
-//			outer_loop:
-//			continue;
-//		}
-	}
-	return NULL;
-}
+MAP_BUCKET_DEFINITION(go_string_ot,  go_slice_ot)
 
-// 获取 get_map_bucket
-static __always_inline struct map_bucket *get_map_bucket(void *headers_ptr_ptr) {
-	void *headers_ptr;
-	long res;
-	res = bpf_probe_read(&headers_ptr, sizeof(headers_ptr), headers_ptr_ptr);
-	if (res < 0) {
-		return NULL;
-	}
-	u64 headers_count = 0;
-	res = bpf_probe_read(&headers_count, sizeof(headers_count), headers_ptr);
-	if (res < 0) {
-		return NULL;
-	}
-	if (headers_count == 0) {
+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;
 	}
-	unsigned char log_2_bucket_count;
-	res = bpf_probe_read(&log_2_bucket_count, sizeof(log_2_bucket_count), headers_ptr + 9);
-	if (res < 0) {
-		return NULL;
-	}
-	u64 bucket_count = 1 << log_2_bucket_count;
-	__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 NULL;
-    }
-	void *header_buckets;
-	res = bpf_probe_read(&header_buckets, sizeof(header_buckets), (void *) (headers_ptr + proc_info->buckets_ptr_pos));
-	if (res < 0) {
-		return NULL;
-	}
-	u32 map_id = 0;
-	struct map_bucket *map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
-	if (!map_value) {
-		return NULL;
-	}
-	for (u32 j = 0; j < 8; j++) {
-		if (j >= bucket_count) {
-			break;
-		}
-		res = bpf_probe_read(map_value, sizeof(struct map_bucket), header_buckets + (j * sizeof(struct map_bucket)));
-		if (res == 0) {
-			return map_value;
-		}
-	}
-	return NULL;
-}
 
-// 获取 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++) {
-		if (map_value->tophash[i] != 0 && map_value->keys[i].len == CW_HEADER_KEY_LENGTH) {
-			char current_header_key[CW_HEADER_KEY_LENGTH];
-			bpf_probe_read(current_header_key, sizeof(current_header_key), map_value->keys[i].str);
-			if ((current_header_key[0] == CW_HEADER_KEY_VAL[0]
-			     || current_header_key[0] == CW_HEADER_KEY_UFIRST_VAL[0])
-			    && current_header_key[1] == CW_HEADER_KEY_UFIRST_VAL[1]
-			    && current_header_key[2] == CW_HEADER_KEY_UFIRST_VAL[2]
-			    && current_header_key[3] == CW_HEADER_KEY_UFIRST_VAL[3]
-			    && current_header_key[4] == CW_HEADER_KEY_UFIRST_VAL[4]
-			    && current_header_key[5] == CW_HEADER_KEY_UFIRST_VAL[5]
-			    && current_header_key[6] == CW_HEADER_KEY_UFIRST_VAL[6]) {
-				void *traceparent_header_value_ptr = map_value->values[i].array;
-				struct go_string_ot traceparent_header_value_go_str;
-				long res = bpf_probe_read(&traceparent_header_value_go_str, sizeof(traceparent_header_value_go_str),
-				                          traceparent_header_value_ptr);
-				if (res == 0) {
-					cw_bpf_debug("get_header_val %d-%d %s",off,count,traceparent_header_value_go_str.str);
-					return traceparent_header_value_go_str.str;
-				}
-			}
-		}
-	}
-	return NULL;
+	return header_val;
 }
 
-// 分段获取 header_val
-static __always_inline char *get_header_val_off(struct map_bucket *map_value) {
-	char *val = get_header_val(map_value, 0, 2);
-	if (val == NULL) {
-		val = get_header_val(map_value, 2, 4);
-	}
-	if (val == NULL) {
-		val = get_header_val(map_value, 4, 6);
-	}
-	if (val == NULL) {
-		val = get_header_val(map_value, 6, 8);
-	}
-	return val;
+
+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);
 }
 
 
@@ -381,23 +194,35 @@ int uprobe_HandlerFunc_ServeHTTP(struct pt_regs *ctx) {
 
 	struct http_server_span_t *http_server_span = &uprobe_data->span;
 	http_server_span->start_time = bpf_ktime_get_ns();
+//	return 1;
 
 	// Propagate context
 	void *req_ptr = get_argument(ctx, 4);
-	struct map_bucket * map_bucket_p = get_map_bucket((void *) (req_ptr + proc_info->headers_ptr_pos));
+//	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(key,
+	                                                                  (void *) (req_ptr + proc_info->headers_ptr_pos),
+	                                                                  proc_info->use_swiss_map);
 
-	if (map_bucket_p == NULL) {
+//	bpf_printk("111 %s",traceparent_header_value);
+
+//	char * traceparent_header_value = get_header_val_off(map_bucket_p);
+	struct apm_span_context *cw_parent_span_context = bpf_map_lookup_elem(&cw_parent_span_context_storage_map, &map_id);
+	if (!cw_parent_span_context) {
 		return 0;
 	}
-
-	char * traceparent_header_value = get_header_val_off(map_bucket_p);
+	__builtin_memset(cw_parent_span_context, 0, sizeof(struct apm_span_context));
+
+	// parentSys parentService
+	char *header_sys_val = bpf_map_lookup_elem(&http_server_context_sys_headers, &key);
+	if (header_sys_val) {
+//		bpf_printk("has sys");
+		bpf_probe_read(&cw_parent_span_context->sysvs, sizeof(cw_parent_span_context->sysvs), header_sys_val);
+//		bpf_printk("has sys %s", cw_parent_span_context->sysvs);
+	}
 
 	if (traceparent_header_value != NULL) {
 		cw_bpf_debug("traceparent_header_value != NULL");
-		struct apm_span_context *cw_parent_span_context = bpf_map_lookup_elem(&cw_parent_span_context_storage_map, &map_id);
-		if (!cw_parent_span_context) {
-			return 0;
-		}
 
 		cw_string_to_span_context(traceparent_header_value, cw_parent_span_context);
 		// found parent context in http headers
@@ -415,6 +240,7 @@ int uprobe_HandlerFunc_ServeHTTP(struct pt_regs *ctx) {
 		if (!cw_parent_span_context) {
 			return 0;
 		}
+
 		// struct apm_span_context context = {};
 		generate_random_bytes(cw_parent_span_context->trace_id, TRACE_ID_SIZE);
 		// generate_random_bytes(context.span_id, SPAN_ID_SIZE);
@@ -452,6 +278,7 @@ int uprobe_HandlerFunc_ServeHTTP(struct pt_regs *ctx) {
 // func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
 SEC("uprobe/HandlerFunc_ServeHTTP")
 int uprobe_HandlerFunc_ServeHTTP_Returns(struct pt_regs *ctx) {
+//	return 1;
 	cw_bpf_debug("[uprobe_HandlerFunc_ServeHTTP_Returns]");
 //	u64 end_time = bpf_ktime_get_ns();
 	__u64 pid_tgid = bpf_get_current_pid_tgid();
@@ -469,10 +296,13 @@ 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);
+		bpf_map_delete_elem(&http_server_context_sys_headers, &key);
 		return 0;
 	}
 	bpf_map_delete_elem(&http_server_uprobes, &key);
-
+	bpf_map_delete_elem(&http_server_context_headers, &key);
+	bpf_map_delete_elem(&http_server_context_sys_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;
@@ -504,4 +334,38 @@ 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);
+		}
+	}
+	// cwother: 08:service:03:tag
+	// 解析 cwother header (变长,需要检查是否匹配)
+	if (len >= (CW_SYS_HEADER_KEY_LENGTH + 2 + 8)) {  // 至少 "cwother: NN:name:MM " 的长度
+		long cw_sys_header_native = 0x3A726568746F7763LL; // 小端序下的 "cwother:"
+		__u64 key64 = 0;
+		bpf_probe_read_user(&key64, sizeof(key64), buf);
+		if (key64 == cw_sys_header_native) {
+			char header_val[CW_SYS_HEADER_VAL_LENGTH] = {};
+			bpf_probe_read_user(header_val, sizeof(header_val), buf + CW_SYS_HEADER_KEY_LENGTH + 2);
+			cw_bpf_debug("Swiss Map:%s key %llu", header_val,key);
+			bpf_map_update_elem(&http_server_context_sys_headers, &key, &header_val, BPF_ANY);
+		}
+	}
+	return 0;
+}

+ 3 - 4
ebpftracer/ebpf/utrace/java/include/java_common.h

@@ -57,14 +57,13 @@ struct sock_t {
 	void * payload_len_p;
 	char payload[MAX_LEN];
 	char header_stream[CW_STREAM_HEADER_LEN];
-	char host[30];
-	struct ebpf_proc_info* proc_info;
+	char host[MAX_HOSTNAME_SIZE];
 };
 
 struct {
 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct sock_t);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct sock_t));
 	__uint(max_entries, 1);
 } socket_heap SEC(".maps");
 

+ 21 - 23
ebpftracer/ebpf/utrace/java/net/client.probe.bpf.c

@@ -60,28 +60,29 @@ int uprobe_Java_java_net_SocketOutputStream_socketWrite0(struct pt_regs *ctx) {
 
 	void *len_from_rbp_ptr = (void *) PT_LEN_REGS(ctx);
 
-	void *len_ptr = 0;
+	int len_val = 0;
 	cw_bpf_debug("[java client] address: len_from_rbp_ptr<0x%lx>\n", len_from_rbp_ptr);
 
-	bpf_probe_read(&len_ptr, sizeof(len_ptr), (void *) len_from_rbp_ptr);
-	cw_bpf_debug("[java client] [len_ptr] before addr<0x%lx>, %d \n", len_from_rbp_ptr, len_ptr);
-
-	if ((long) len_ptr != data_count) {
-		cw_bpf_debug("[java client] [len_ptr] check error.");
+	bpf_probe_read(&len_val, sizeof(len_val), (void *) len_from_rbp_ptr);
+	cw_bpf_debug("[java client] [len_val] before addr<0x%lx>, %d \n", len_from_rbp_ptr, len_val);
+	if (len_val != data_count) {
+		cw_bpf_debug("len check failed: len_val=%d data_count=%d", len_val, data_count);
 		return 0;
 	}
 
 	int key = 0;
 	struct sock_t *map_data = bpf_map_lookup_elem(&socket_heap, &key);
-	if (!map_data) {
+	if (map_data == NULL) {
 		cw_bpf_debug("[java client] Failed to lookup socket_heap\n");
 		return 1;
 	}
 
+	__builtin_memset(map_data->header_stream, 0, sizeof(map_data->header_stream));
+	__builtin_memset(map_data->host, 0, sizeof(map_data->host));
 	// 获取jbytearray
 	__u32 tgid = (__u32) (bpf_get_current_pid_tgid() >> 32);
-	map_data->proc_info = bpf_map_lookup_elem(&proc_info_map, &tgid);
-	if (!map_data->proc_info) {
+	struct ebpf_proc_info *proc_info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+	if (!proc_info) {
 		return 0;
 	}
 
@@ -89,7 +90,7 @@ int uprobe_Java_java_net_SocketOutputStream_socketWrite0(struct pt_regs *ctx) {
 	void *jbytearray_ptr = NULL;
 
 	// 1003 是 javaAOT 的
-	if (map_data->proc_info->code_type == CodeTypeJavaAot) {
+	if (proc_info->code_type == CodeTypeJavaAot) {
 		jbytearray_ptr = (void *) (PT_REGS_SP(ctx)) + 0x8 + 0x10060 + 0x20;
 	} else {
 		jbytearray_ptr = (void *) PT_REGS_PARM4(ctx);
@@ -308,24 +309,21 @@ PROGUP(java_build_header)(struct pt_regs *ctx) {
 		return 1;
 	}
 
-	if (!map_data->proc_info)
-		return 1;
-
-	struct ebpf_proc_info proc_info;
-	bpf_probe_read(&proc_info, sizeof(struct ebpf_proc_info), map_data->proc_info);
+	__u32 tgid = (__u32) (bpf_get_current_pid_tgid() >> 32);
+	struct ebpf_proc_info *proc_info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+	if (!proc_info) {
+		return 0;
+	}
 
-	char host[30];
-	bpf_probe_read(&host, sizeof host, map_data->host);
-	host[29] = '\0';
 	for (u32 i = 0; i < 30; i++) {
-		if (host[i] == '\r') {
-			host[i] = '\0';
+		if (map_data->host[i] == '\r') {
+			map_data->host[i] = '\0';
 			break;
 		}
 	}
 	cw_bpf_debug("[java client] http host %s\n", map_data->host);
 
-	struct apm_span_context *cw_sc = build_sc(proc_info, host);
+	struct apm_span_context *cw_sc = build_sc(*proc_info, map_data->host);
 	if (cw_sc == NULL) {
 		return 0;
 	}
@@ -439,8 +437,8 @@ int javaaot_asmnop(struct pt_regs *ctx) {
 
 	// 获取jbytearray
 	__u32 tgid = (__u32) (bpf_get_current_pid_tgid() >> 32);
-	map_data->proc_info = bpf_map_lookup_elem(&proc_info_map, &tgid);
-	if (!map_data->proc_info) {
+	struct ebpf_proc_info *proc_info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+	if (!proc_info) {
 		return 0;
 	}
 

+ 9 - 1
ebpftracer/jvm.go

@@ -20,6 +20,8 @@ const (
 	// binPath           = "/root/code/jdk8u/build/linux-x86_64-normal-server-release/jdk/lib/amd64/libnio.so"
 	symbolsocketRead0  = "Java_sun_nio_ch_FileDispatcherImpl_read0" // display /s $rsi
 	symbolsocketWrite0 = "Java_java_net_SocketOutputStream_socketWrite0"
+	symbolgetTTL = "Java_java_net_PlainDatagramSocketImpl_getTTL"
+	symbolgetConvert0 = "Java_sun_net_sdp_SdpSupport_convert0"
 )
 
 func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType CodeType, rootfs string) ([]link.Link, error) {
@@ -183,6 +185,12 @@ func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32, rootfs string) ([]link.Li
 			FuncSymbol: inject.InstInfo{
 				SymName: uProbeData.Func,
 			},
+			FuncConvert0Symbol: inject.InstInfo{
+				SymName: symbolgetConvert0,
+			},
+			FuncGetTTLSymbol: inject.InstInfo{
+				SymName: symbolgetTTL,
+			},
 			//ProcLoadPath: procMapsLoadPath,
 		},
 		RecodeInfo: inject.LibNetInfo{FuncSymbol: inject.InstInfo{SymName: "CW_RECODE_" + originFunc}},
@@ -193,7 +201,7 @@ func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32, rootfs string) ([]link.Li
 	klog.Infof("[jvm] ReleaseLibNetInfo :[%s]", utils.ToString(jvmInjector.ReleaseLibNetInfo))
 	klog.Infof("[jvm] DebugLibNetInfo :[%s]", utils.ToString(jvmInjector.DebugLibNetInfo))
 	if err != nil {
-		klog.WithError(err).Errorf("[jvm] inject.JvmInject error.")
+		klog.WithError(err).Errorf("[jvm] inject.JvmInject error. Rootfs = [%s]", rootfs)
 		return nil, err
 	}
 

+ 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)
+						}
+					}
+				}
+			}
+		}
+	}
+}

+ 90 - 19
ebpftracer/l7/http.go

@@ -21,13 +21,43 @@ func ParseHttp(payload []byte) (string, string) {
 	return string(method), string(uri)
 }
 
-func ParseHttpHost(payload []byte) (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,24 +65,24 @@ func ParseHttpHost(payload []byte) (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
+	// 解析 Host 头部
+	hostPort := parseHttpHeader(rest, "Host:")
+	if hostPort == "" {
+		return string(method), string(uri), "", 0, rest
 	}
-	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]
-	}
-	hostPort := string(bytes.TrimSpace(hostPortBts))
+
 	hostParts := strings.Split(hostPort, ":")
 	host := hostParts[0]
-	port := uint16(80) // Default port
+
+	// 根据 TLS 标识设置默认端口
+	var port uint16
+	if isTls {
+		port = 443 // HTTPS 默认端口
+	} else {
+		port = 80 // HTTP 默认端口
+	}
+
+	// 如果 Host 头部明确指定了端口,使用指定的端口
 	if len(hostParts) > 1 {
 		port64, err := strconv.ParseUint(hostParts[1], 10, 16)
 		if err == nil {
@@ -60,5 +90,46 @@ func ParseHttpHost(payload []byte) (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
+}
+
+// ParseElasticsearch 解析 Elasticsearch HTTP 请求体
+// 只返回请求体(Query)部分
+func ParseElasticsearch(payload []byte) string {
+	_, _, _, _, rest := parseHttpHostCommon(payload, false)
+
+	// 查找请求体的开始位置(\r\n\r\n 之后)
+	bodyStart := bytes.Index(rest, []byte("\r\n\r\n"))
+	if bodyStart == -1 {
+		// 如果没有找到完整的头部结束标记,返回空字符串
+		return ""
+	}
+	bodyStart += 4
+
+	// 提取请求体(限制长度,避免过长)
+	body := rest[bodyStart:]
+	maxBodyLen := 200
+	if len(body) > maxBodyLen {
+		body = body[:maxBodyLen]
+	}
+
+	// 只返回请求体内容
+	bodyStr := strings.TrimSpace(string(body))
+	return bodyStr
 }

+ 66 - 3
ebpftracer/l7/l7.go

@@ -1,9 +1,10 @@
 package l7
 
 import (
-	"inet.af/netaddr"
 	"strconv"
 	"time"
+
+	"inet.af/netaddr"
 )
 
 type Protocol uint8
@@ -25,6 +26,10 @@ const (
 	ProtocolDubbo2    Protocol = 12
 	ProtocolDNS       Protocol = 13
 	ProtocolDM        Protocol = 14
+	ProtocolMariaDB   Protocol = 15
+	ProtocolGrpc      Protocol = 16
+	ProtocolES        Protocol = 17
+	ProtocolTiDB      Protocol = 18
 )
 
 func (p Protocol) Int() int {
@@ -63,6 +68,14 @@ func (p Protocol) String() string {
 		return "DNS"
 	case ProtocolDM:
 		return "DM"
+	case ProtocolMariaDB:
+		return "Mariadb"
+	case ProtocolTiDB:
+		return "TiDB"
+	case ProtocolGrpc:
+		return "GRPC"
+	case ProtocolES:
+		return "Elasticsearch"
 	}
 	return "UNKNOWN:" + strconv.Itoa(int(p))
 }
@@ -84,9 +97,9 @@ func (p Protocol) ServiceNameString() string {
 	case ProtocolMongo:
 		return "Mongo"
 	case ProtocolKafka:
-		return "Kafka"
+		return "KAFKA"
 	case ProtocolCassandra:
-		return "Cassandra"
+		return "CASSANDRA"
 	case ProtocolRabbitmq:
 		return "Rabbitmq"
 	case ProtocolNats:
@@ -99,6 +112,14 @@ func (p Protocol) ServiceNameString() string {
 		return "DNS"
 	case ProtocolDM:
 		return "DM"
+	case ProtocolMariaDB:
+		return "MARIA"
+	case ProtocolTiDB:
+		return "TIDB"
+	case ProtocolGrpc:
+		return "GRPC"
+	case ProtocolES:
+		return "ELASTICSEARCH"
 	}
 	return "UNKNOWN:" + strconv.Itoa(int(p))
 }
@@ -181,8 +202,42 @@ func (s Status) Error() bool {
 	return s == StatusFailed
 }
 
+// TypeFrom represents the code type from the parent span context
+type TypeFrom string
+
+// String returns the human-readable name for the type code
+func (t TypeFrom) String() string {
+	switch string(t) {
+	case "10":
+		return "SDK"
+	case "00":
+		return "GO"
+	case "01":
+		return "JAVA"
+	case "02":
+		return "NODEJS"
+	case "03":
+		return "NGINX"
+	case "04":
+		return ".NET"
+	case "05":
+		return ".NETCORE"
+	case "06":
+		return "PHP"
+	case "07":
+		return "PYTHON"
+	case "08":
+		return "C"
+	case "09":
+		return "RUBY"
+	default:
+		return string(t)
+	}
+}
+
 type RequestData struct {
 	Protocol          Protocol
+	Pid               uint32
 	Status            Status
 	Duration          time.Duration
 	Method            Method
@@ -191,6 +246,7 @@ type RequestData struct {
 	TraceId           uint64
 	TraceStart        uint32
 	TraceEnd          uint32
+	TraceType         uint32
 	EventCount        uint32
 	AssumedAppId      string
 	SpanId            string
@@ -200,11 +256,18 @@ type RequestData struct {
 	DAddr             netaddr.IPPort
 	ComponentSAddr    netaddr.IPPort
 	ComponentDAddr    netaddr.IPPort
+	DestAddrString    string
 	ParentSpanContext struct {
 		TraceIdFrom    string
 		CalledId       string
 		InstanceIdFrom string
 		AppIdFrom      string
 		SpanIdFrom     string
+		TypeFrom       TypeFrom
 	}
+	SysvcFrom string // cwother header value: NN:ParentServiceName:MM:ParentServiceSysTag
+	ErrorMsg  string
+	IsTls     bool
+	MQTopic   string // MQ topic (e.g., Kafka topic)
+	MQKey     string // MQ key (e.g., Kafka message key)
 }

+ 182 - 16
ebpftracer/l7/mongo.go

@@ -2,6 +2,8 @@ package l7
 
 import (
 	"encoding/binary"
+	"fmt"
+	"strings"
 
 	"go.mongodb.org/mongo-driver/bson"
 )
@@ -9,30 +11,194 @@ import (
 const (
 	MongoOpMSG = 2013
 
-	mongoHeaderLength      = 20
+	// MongoDB Wire Protocol OP_MSG Structure:
+	// [16 bytes: header] + [4 bytes: flag_bits] + [sections...]
+	mongoHeaderLength      = 16 // length(4) + request_id(4) + response_to(4) + op_code(4)
+	mongoFlagBitsLength    = 4
 	mongoOpCodeOffset      = 12
 	mongoSectionKindLength = 1
-	mongoSectionSizeLength = 4
-	mongoSectionKindBody   = 0
+	mongoSectionKindCmd    = 0 // Section 0: Command document (metadata)
+	mongoSectionKindDocs   = 1 // Section 1: Document sequence (actual data)
+
+	// 默认操作类型
+	invalidDataResult = "<truncated>"
 )
 
-func ParseMongo(payload []byte) (res string) {
-	res = "<truncated>"
-	if len(payload) < mongoHeaderLength+mongoSectionKindLength+mongoSectionSizeLength {
-		return
+// ParseMongo 解析 MongoDB Wire Protocol (OP_MSG) 的 payload
+// 返回格式:opType|section0 或 opType|section0@[section1]
+func ParseMongo(payload []byte) string {
+
+	minLength := mongoHeaderLength + mongoFlagBitsLength + mongoSectionKindLength + 4 // +4 for BSON length
+	if len(payload) < minLength {
+		return invalidDataResult
 	}
+
+	// 验证是否为 OP_MSG
 	opCode := binary.LittleEndian.Uint32(payload[mongoOpCodeOffset:])
 	if opCode != MongoOpMSG {
-		return
+		return invalidDataResult
+	}
+
+	// 跳过 header (16 bytes) + flag_bits (4 bytes)
+	offset := mongoHeaderLength + mongoFlagBitsLength
+
+	var section0 string    // 命令元数据
+	var section0Raw []byte // Section 0 原始 BSON 数据(用于提取操作类型)
+	var section1 string    // 文档数据
+
+	// 解析所有 sections
+	for offset < len(payload) {
+		if offset+mongoSectionKindLength >= len(payload) {
+			break
+		}
+		sectionKind := payload[offset]
+		offset += mongoSectionKindLength
+
+		if sectionKind == mongoSectionKindCmd {
+			// Section 0: 命令文档(元数据)
+			if offset+4 > len(payload) {
+				break
+			}
+			bsonLen := int(binary.LittleEndian.Uint32(payload[offset:]))
+			if bsonLen < 5 || offset+bsonLen > len(payload) {
+				break
+			}
+			section0Raw = payload[offset : offset+bsonLen]
+			section0 = bson.Raw(section0Raw).String()
+
+			// Section 0 是必需的,解析失败则提前返回
+			if section0 == "" {
+				return invalidDataResult
+			}
+
+			/*// 提取操作类型(BSON 文档的第一个字段名)
+			cmd = extractMongoOpType(section0Raw)*/
+
+			offset += bsonLen
+
+		} else if sectionKind == mongoSectionKindDocs {
+			// Section 1: 文档序列(真正的数据)
+			if offset+4 > len(payload) {
+				break
+			}
+			// Section 1 格式: [4 bytes size] + [identifier C-string] + [BSON documents]
+			// 注意:size 包含了 size 字段本身的 4 字节
+			section1Size := int(binary.LittleEndian.Uint32(payload[offset:]))
+
+			// 验证:从 offset 开始需要 section1Size 字节(包含 size 字段)
+			if section1Size < 5 || offset+section1Size > len(payload) {
+				break
+			}
+
+			// 提取数据:跳过 size 字段的 4 字节
+			section1Data := payload[offset+4 : offset+section1Size]
+
+			// 跳过 identifier(null-terminated string)
+			identifierEnd := 0
+			for i, b := range section1Data {
+				if b == 0 {
+					identifierEnd = i + 1
+					break
+				}
+				if i > 20 { // 防止无限循环,identifier一般不会超过20个字节
+					break
+				}
+			}
+
+			if identifierEnd > 0 && identifierEnd < len(section1Data) {
+				// 解析 BSON 文档数组
+				docsData := section1Data[identifierEnd:]
+				docs := parseBSONDocuments(docsData)
+				if len(docs) > 0 {
+					section1 = strings.Join(docs, ", ")
+				}
+			}
+
+			offset += section1Size
+		} else {
+			// 未知的 section kind,停止解析
+			break
+		}
 	}
-	sectionKind := payload[mongoHeaderLength]
-	if sectionKind != mongoSectionKindBody {
-		return
+
+	// 构建返回结果 格式:
+	//   - 有 section1: "section0@[section1]"  (例: {"insert":"users"}@[{"name":"Alice"}, ...])
+	//   - 无 section1: "section0"  (例: {"find":"users","filter":{...}})
+	var baseResult string
+	if section1 != "" {
+		// 同时包含命令和文档(批量操作)
+		baseResult = fmt.Sprintf("%s@[%s]", section0, section1)
+	} else {
+		// 只有命令(查询、单文档操作)
+		baseResult = section0
+	}
+
+	return baseResult
+}
+
+// extractMongoOpType 从 Section 0 的 BSON 文档中提取操作类型
+// BSON 格式:[4 bytes: length] + [1 byte: type] + [cstring: field name] + [value] + ... + [0x00]
+/*func extractMongoOpType(bsonData []byte) string {
+	if len(bsonData) < 5 {
+		return ""
+	}
+	// 跳过文档长度(前 4 字节)
+	offset := 4
+
+	// 读取第一个元素的 type(1 字节)
+	if offset >= len(bsonData) {
+		return ""
+	}
+	// elementType := bsonData[offset] // 暂不需要
+	offset += 1
+
+	// 读取第一个元素的 field name(null-terminated string)
+	fieldNameStart := offset
+	for offset < len(bsonData) && bsonData[offset] != 0 {
+		offset++
+		if offset-fieldNameStart > 50 { // 防止无限循环
+			return ""
+		}
 	}
-	sectionData := payload[mongoHeaderLength+mongoSectionKindLength:]
-	sectionLength := binary.LittleEndian.Uint32(sectionData)
-	if sectionLength < 1 || int(sectionLength) > len(sectionData) {
-		return
+
+	if offset >= len(bsonData) {
+		return ""
+	}
+	// 提取操作类型(字段名),操作类型总是 BSON 文档的第一个字段,所以这里直接提取
+	opType := string(bsonData[fieldNameStart:offset])
+
+	// 过滤掉内部字段(以 $ 开头的)(理论上不会,容错)
+	if strings.HasPrefix(opType, "$") {
+		return ""
+	}
+	return opType
+}*/
+
+// parseBSONDocuments 解析连续的 BSON 文档
+func parseBSONDocuments(data []byte) []string {
+	var docs []string
+	offset := 0
+
+	for offset < len(data) {
+		if offset+4 > len(data) {
+			break
+		}
+		// 读取 BSON 文档长度
+		bsonLen := int(binary.LittleEndian.Uint32(data[offset:]))
+		if bsonLen < 5 || offset+bsonLen > len(data) {
+			break
+		}
+		// 解析 BSON 文档
+		doc := bson.Raw(data[offset : offset+bsonLen])
+		docs = append(docs, doc.String())
+
+		offset += bsonLen
+
+		// 限制解析数量,避免过多文档, 最多解析前1个文档
+		if len(docs) > 1 {
+			docs = append(docs, "...")
+			break
+		}
 	}
-	return bson.Raw(sectionData).String()
+	return docs
 }

+ 54 - 7
ebpftracer/stack.go

@@ -12,7 +12,9 @@ import (
 	klog "github.com/sirupsen/logrus"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
+	"syscall"
 )
 
 // AttachStackUprobes
@@ -49,7 +51,7 @@ func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link
 }
 
 // JVM process stack
-func (t *Tracer) JattachJvm(pid uint32, appInfo AppInfo, whiteList, blackList string) error {
+func (t *Tracer) JattachJvm(pid uint32, appInfo AppInfo, whiteList, blackList, rootfs string) error {
 	klog.Infoln("[Jvm stack uprobe] Attach Start AttachJVMStackUprobes", pid)
 	// TODO tiny Agent 注入
 	// TODO copy agent到 jre conf/lib/plugins
@@ -68,7 +70,8 @@ func (t *Tracer) JattachJvm(pid uint32, appInfo AppInfo, whiteList, blackList st
 	} else {
 		stackRule = fmt.Sprintf("whiteStack=%s,blackStack=%s", whiteList, blackList)
 	}
-	nativeBasePath := utils.GetDefaultAgentsPath("NativeAgent")
+	//nativeBasePath := utils.GetDefaultAgentsPath("NativeAgent")
+	nativeBasePath := "/tmp/NativeAgent"
 	kvPairs := []string{
 		fmt.Sprintf("%s=%s", filepath.Join(nativeBasePath, "lib", "apmAgent.jar"), nativeBasePath),
 		fmt.Sprintf("%s=%d", "appId", appInfo.AppIdHash.IntVal),
@@ -96,7 +99,7 @@ func (t *Tracer) JattachJvm(pid uint32, appInfo AppInfo, whiteList, blackList st
 	return nil
 }
 
-func (t *Tracer) AttachJVMStackUprobes(pid uint32, appInfo AppInfo) ([]link.Link, error) {
+func (t *Tracer) AttachJVMStackUprobes(pid uint32, appInfo AppInfo, rootfs string) ([]link.Link, error) {
 
 	//path = utils.GetDefaultAgentsPath("NativeAgent", "libnativeAgent.so")
 	//tmp/NativeAgentSo2297066477572820801.tmp
@@ -105,6 +108,7 @@ func (t *Tracer) AttachJVMStackUprobes(pid uint32, appInfo AppInfo) ([]link.Link
 		klog.Error(err)
 		return nil, err
 	}
+	path = filepath.Join(rootfs, path)
 
 	setNodeEnter := "Java_com_cloudwise_agent_common_natives_TraceNative_setNodeEnter"
 	setNodeReturn := "Java_com_cloudwise_agent_common_natives_TraceNative_setNodeReturn"
@@ -139,7 +143,6 @@ func (t *Tracer) AttachJVMStackUprobes(pid uint32, appInfo AppInfo) ([]link.Link
 }
 
 func FindNativeSoFromMapped(pid uint32, prefix, suffix string) (string, error) {
-	// todo rootfs
 	mapsFile := fmt.Sprintf("/proc/%d/maps", pid)
 	tmpFile, err := os.Open(mapsFile)
 	if err != nil {
@@ -147,15 +150,33 @@ func FindNativeSoFromMapped(pid uint32, prefix, suffix string) (string, error) {
 	}
 	defer tmpFile.Close()
 
-	// 使用 bufio.Scanner 逐行读取文件内容
+	var selectedPath string
+	var maxTimestamp int64
+	fallbackPaths := make(map[string]struct{}) // 使用 map 去重
+
 	scanner := bufio.NewScanner(tmpFile)
 	for scanner.Scan() {
 		line := scanner.Text()
-		// 检查路径部分是否包含 "NativeAgentSo" 且以 ".tmp" 结尾
 		if strings.Contains(line, prefix) && strings.HasSuffix(line, suffix) {
 			parts := strings.Fields(line)
 			if len(parts) > 5 {
-				return parts[len(parts)-1], nil // 返回路径
+				path := parts[len(parts)-1]
+				baseName := filepath.Base(path)
+
+				if strings.HasPrefix(baseName, prefix) && strings.HasSuffix(baseName, suffix) {
+					middle := strings.TrimSuffix(strings.TrimPrefix(baseName, prefix), suffix)
+					segments := strings.Split(middle, ".")
+					if len(segments) >= 2 {
+						timestampStr := segments[len(segments)-1]
+						timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
+						if err == nil && timestamp > maxTimestamp {
+							maxTimestamp = timestamp
+							selectedPath = path
+						}
+					} else {
+						fallbackPaths[path] = struct{}{}
+					}
+				}
 			}
 		}
 	}
@@ -164,5 +185,31 @@ func FindNativeSoFromMapped(pid uint32, prefix, suffix string) (string, error) {
 		return "", fmt.Errorf("error reading maps file: %v", err)
 	}
 
+	if selectedPath != "" {
+		return selectedPath, nil
+	}
+
+	var latestPath string
+	var latestModTime int64
+	for path := range fallbackPaths {
+		info, err := os.Stat(path)
+		if err != nil {
+			continue
+		}
+		stat, ok := info.Sys().(*syscall.Stat_t)
+		if !ok {
+			continue
+		}
+		modTime := stat.Mtim.Sec
+		if modTime > latestModTime {
+			latestModTime = modTime
+			latestPath = path
+		}
+	}
+
+	if latestPath != "" {
+		return latestPath, nil
+	}
+
 	return "", fmt.Errorf("no matching path found")
 }

+ 596 - 142
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"
@@ -23,14 +24,27 @@ import (
 )
 
 const (
-	minSupportedGoVersion = "v1.17.0"
-	goTlsWriteSymbol      = "crypto/tls.(*Conn).Write"
-	goTlsReadSymbol       = "crypto/tls.(*Conn).Read"
-	goExecute             = "runtime.execute"
-	goNewproc1            = "runtime.newproc1"
-	goRunqget             = "runtime.runqget"
-	goServeHTTP           = "net/http.serverHandler.ServeHTTP"
-	goTransport           = "net/http.(*Transport).roundTrip"
+	minSupportedGoVersion          = "v1.15.0"
+	goTlsWriteSymbol               = "crypto/tls.(*Conn).Write"
+	goTlsReadSymbol                = "crypto/tls.(*Conn).Read"
+	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"
+	goGrpcServerHandleStream       = "google.golang.org/grpc.(*Server).handleStream"
+	goGrpcHttp2OperateHeader       = "google.golang.org/grpc/internal/transport.(*http2Server).operateHeaders"
+	goGrpcServerWritestatus        = "google.golang.org/grpc/internal/transport.(*http2Server).WriteStatus"
+	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"
+	goKafkaWriterWriteMessages     = "github.com/segmentio/kafka-go.(*Writer).WriteMessages"
+	goKafkaReaderFetchMessage      = "github.com/segmentio/kafka-go.(*Reader).FetchMessage"
 )
 
 var (
@@ -134,6 +148,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"} {
@@ -164,6 +180,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 		log(fmt.Sprintf("go_versions below %s are not supported", minSupportedGoVersion), nil)
 		return nil, err
 	}
+	appInfo.Version = version
 
 	ef, err := elf.Open(path)
 	if err != nil {
@@ -175,7 +192,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)
@@ -184,7 +201,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()
@@ -200,14 +217,172 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 		return nil, err
 	}
 
+	// 检测 gRPC 版本
+	var grpcMajorVersion, grpcMinorVersion int
+	for _, dep := range bi.Deps {
+		if strings.Contains(dep.Path, "grpc") {
+			klog.Infoln("Found gRPC dependency:", dep.Path, "version:", dep.Version)
+
+			// 解析版本号
+			version := dep.Version
+			if version != "" {
+				// 移除可能的 "v" 前缀
+				version = strings.TrimPrefix(version, "v")
+				parts := strings.Split(version, ".")
+
+				if len(parts) >= 2 {
+					major, err := strconv.Atoi(parts[0])
+					if err != nil {
+						klog.WithError(err).Warnf("Error parsing major version from %s", parts[0])
+						continue
+					}
+
+					minor, err := strconv.Atoi(parts[1])
+					if err != nil {
+						klog.WithError(err).Warnf("Error parsing minor version from %s", parts[1])
+						continue
+					}
+
+					klog.Infof("Detected gRPC version: %d.%d for PID %d", major, minor, pid)
+					grpcMajorVersion = major
+					grpcMinorVersion = minor
+					// // 根据版本选择相应的探针策略
+					// if major == 1 && minor >= 69 {
+					// 	klog.Infof("Using modern gRPC handler for version %d.%d", major, minor)
+					// } else {
+					// 	klog.Infof("Using legacy gRPC handler for version %d.%d", major, minor)
+					// }
+				}
+			}
+		}
+	}
+
 	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)
+
+	// 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)
+	}
 
-	fmt.Println(offset, ok, version)
+	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) >= 3 {
+		if len(parts) >= 2 {
 			major, err = strconv.Atoi(parts[0])
 			if err != nil {
 				log("Error converting major version:", err)
@@ -218,64 +393,236 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 				log("Error converting minor version:", err)
 				return nil, err
 			}
-			revision, err = strconv.Atoi(parts[2])
-			if err != nil {
-				log("Error converting revision version:", err)
-				return nil, err
+			if len(parts) >= 3 {
+				revision, err = strconv.Atoi(parts[2])
+				if err != nil {
+					log("Error converting revision version:", err)
+				}
 			}
+
+			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
-			// go
-			info.MethodPtrPos = uint64(0)
-			info.UrlPtrPos = uint64(16)
-			info.PathPtrPos = uint64(56)
-			info.StatusCodePos = uint64(120)
-			info.RequestHostPos = uint64(128)
-			info.ProtoPos = uint64(24)
-			info.CtxPtrPos = uint64(232)
-			info.HeadersPtrPos = uint64(56)
-			info.BucketsPtrPos = uint64(16)
 			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, gRPC and Kafka fields")
+			fields := map[*uint64]tracer.ID{
+				&info.MethodPtrPos:        tracer.NewID("std", "net/http", "Request", "Method"),
+				&info.UrlPtrPos:           tracer.NewID("std", "net/http", "Request", "URL"),
+				&info.PathPtrPos:          tracer.NewID("std", "net/url", "URL", "Path"),
+				&info.StatusCodePos:       tracer.NewID("std", "net/http", "response", "status"),
+				&info.RequestHostPos:      tracer.NewID("std", "net/http", "Request", "Host"),
+				&info.UrlHostPos:          tracer.NewID("std", "net/url", "URL", "Host"),
+				&info.ProtoPos:            tracer.NewID("std", "net/http", "Request", "Proto"),
+				&info.CtxPtrPos:           tracer.NewID("std", "net/http", "Request", "ctx"),
+				&info.HeadersPtrPos:       tracer.NewID("std", "net/http", "Request", "Header"),
+				&info.HttpClientNextidPos: tracer.NewID("google.golang.org/grpc", "google.golang.org/grpc/internal/transport", "http2Client", "nextID"),
+				&info.StreamMethodPtrPos:  tracer.NewID("google.golang.org/grpc", "google.golang.org/grpc/internal/transport", "Stream", "method"),
+				&info.StreamCtxPos:        tracer.NewID("google.golang.org/grpc", "google.golang.org/grpc/internal/transport", "Stream", "ctx"),
+				&info.IoWriterBufPtrPos:   tracer.NewID("std", "bufio", "Writer", "buf"),
+				&info.IoWriterNPos:        tracer.NewID("std", "bufio", "Writer", "n"),
+				// Kafka Message fields
+				&info.KafkaMessageKeyPos:       tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Message", "Key"),
+				&info.KafkaMessageTopicPos:     tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Message", "Topic"),
+				&info.KafkaMessageHeadersPos:   tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Message", "Headers"),
+				&info.KafkaMessageTimePos:      tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Message", "Time"),
+				&info.KafkaMessagePartitionPos: tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Message", "Partition"),
+				&info.KafkaMessageOffsetPos:    tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Message", "Offset"),
+				&info.KafkaMessageValuePos:     tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Message", "Value"),
+				// Kafka Writer fields
+				&info.KafkaWriterTopicPos: tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Writer", "Topic"),
+				// Try both Brokers and Addr fields - Addr is a string, Brokers is []string
+				&info.KafkaWriterAddrPos: tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Writer", "Addr"),
+				// Kafka Reader fields
+				// Note: Reader.config is unexported, try both "config" and "Config"
+				&info.KafkaReaderConfigPos:        tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "Reader", "config"),
+				&info.KafkaReaderConfigGroupIDPos: tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "ReaderConfig", "GroupID"),
+				&info.KafkaReaderConfigTopicsPos:  tracer.NewID("github.com/segmentio/kafka-go", "github.com/segmentio/kafka-go", "ReaderConfig", "topics"),
+				// net.TCPAddr offsets (standard library)
+				&info.TcpAddrIPOffset:   tracer.NewID("std", "net", "TCPAddr", "IP"),
+				&info.TcpAddrPortOffset: tracer.NewID("std", "net", "TCPAddr", "Port"),
+			}
+
+			successCount := 0
+			failCount := 0
+			for field, id := range fields {
+				off, ok := tracer.GetOffset(id, path)
+				if !ok {
+					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)
+
 			// 获取内存地址
-			allocDetails, err := tracer.Allocate(int(pid))
-			if err == nil && allocDetails != nil {
-				info.StartAddr = allocDetails.StartAddr
-				info.EndAddr = allocDetails.EndAddr
+			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 {
+					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
 			klog.Debugln("Major:", major)
 			klog.Debugln("Minor:", minor)
 			klog.Debugln("Revision:", revision)
 			klog.Debugln("goVersion", goVersion)
-			klog.Debugln("info.StartAddr", info.StartAddr)
-			klog.Debugln("info.EndAddr", info.EndAddr)
+			klog.WithField("pid", pid).Debugln("info.StartAddr", info.StartAddr)
+			klog.WithField("pid", pid).Debugln("info.EndAddr", info.EndAddr)
+
+			// 构建 sysvc 值:{app_name_len}:app_name:{appServiceType_len}:appServiceType:{SysTagLen}[:SysTag]
+			// appServiceType 固定为 "APPLICATION"
+			appName := appInfo.AppName
+			nodeInfo := NODE_INFO.GetNodeInfo()
+			sysTag := ""
+			if nodeInfo != nil {
+				sysTag = nodeInfo.SysTag
+			}
+			appServiceType := "APPLICATION"
+
+			appNameLen := len(appName)
+			appServiceTypeLen := len(appServiceType)
+			sysTagLen := len(sysTag)
+
+			// 限制长度,避免超出数组大小
+			if appNameLen > 32 {
+				appNameLen = 32
+				appName = appName[:32]
+			}
+			if sysTagLen > 32 {
+				sysTagLen = 32
+				sysTag = sysTag[:32]
+			}
+
+			// 格式:{app_name_len}:app_name:{appServiceType_len}:appServiceType[:{SysTagLen}:SysTag]
+			// 如果 sysTag 为空,则为 {app_name_len}:app_name:{appServiceType_len}:appServiceType
+			// 如果 sysTag 不为空,则为 {app_name_len}:app_name:{appServiceType_len}:appServiceType:{SysTagLen}:SysTag
+			// 长度不足2位时前面补0,例如:08:service:12:APPLICATION 或 08:service:12:APPLICATION:03:tag
+			var sysvcStr string
+			if sysTagLen == 0 {
+				sysvcStr = fmt.Sprintf("%02d:%s:%02d:%s", appNameLen, appName, appServiceTypeLen, appServiceType)
+			} else {
+				sysvcStr = fmt.Sprintf("%02d:%s:%02d:%s:%02d:%s", appNameLen, appName, appServiceTypeLen, appServiceType, sysTagLen, sysTag)
+			}
+
+			// 复制到字节数组,确保不超过 76 字节
+			sysvcBytes := []byte(sysvcStr)
+			if len(sysvcBytes) > 76 {
+				sysvcBytes = sysvcBytes[:76]
+			}
+			copy(info.Sysvc[:], sysvcBytes)
+			klog.Debugf("[AttachGoTlsUprobes] Sysvc constructed: %s", sysvcStr)
+
+			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:
+			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 goKafkaWriterWriteMessages:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.13: Matched kafka Writer.WriteMessages symbol (index=%d)", i)
+		case goKafkaReaderFetchMessage:
+			matchedSymbols++
+			klog.Infof("[AttachGoTlsUprobes] STEP 19.14: Matched kafka Reader.FetchMessage 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 {
@@ -291,173 +638,199 @@ 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 goServeHTTP:
-			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP"], &link.UprobeOptions{Address: address})
+
+		//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:
+			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).Errorln("failed to attach uprobe_HandlerFunc_ServeHTTP uprobe")
-				continue
+				return nil, err
 			}
-			klog.Infoln("net/http.serverHandler.ServeHTTP ok")
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			if sEnd > textSectionLen {
-				continue
+			if retLinks != nil {
+				klog.Infoln("uprobe_ClientConn_Invoke ok")
+				links = append(links, retLinks...)
 			}
-			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
+		case goGrpcClientLoopyHeaderHandler:
+			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)
 			}
-			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
-				}
+		case goGrpcHttp2ClientNewStream:
+			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)
 			}
+		case goGrpcHttp2OperateHeader:
+			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)
+			}
+		// case goGrpcServerWritestatus:
+		// 	// 根据 gRPC 版本选择相应的 WriteStatus 探针
+		// 	l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_http2Server_WriteStatus"], &link.UprobeOptions{Address: address})
+		// 	if err != nil {
+		// 		klog.WithError(err).Errorf("failed to attach uprobe_http2Server_WriteStatus uprobe")
+		// 		continue
+		// 	}
+		// 	links = append(links, l)
+		case goGrpcServerHandleStream:
+			// 根据 gRPC 版本选择相应的探针
+			probeName := t.selectGRPCServerProbe(grpcMajorVersion, grpcMinorVersion)
+			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 {
+				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, retLinks...)
+			}
+		case goServeHTTP:
+			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 {
+				return nil, err
+			}
+			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 {
+				return nil, err
+			}
+			if retLinks != nil {
+				klog.Infoln("net/http.uprobe_Transport_roundTrip ok")
+				links = append(links, retLinks...)
 			}
-			klog.Infoln("net/http.uprobe_Transport_roundTrip ok")
 
-			links = append(links, l)
-			sStart := s.Value - textSection.Addr
-			sEnd := sStart + s.Size
-			if sEnd > textSectionLen {
+		case goHeaderWriteSubset:
+			if t.DisableE2ETracing() {
 				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)
+			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")
 				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)
-			}
+			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
 			}
 			links = append(links, l)
+
 		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...)
+			}
+		case goKafkaWriterWriteMessages:
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "uprobe_WriteMessages", "uprobe_WriteMessages_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach uprobe_WriteMessages uprobe", true)
+			if err != nil {
+				return nil, err
+			}
+			if retLinks != nil {
+				klog.Infoln("uprobe_WriteMessages ok")
+				links = append(links, retLinks...)
+			}
+		case goKafkaReaderFetchMessage:
+			retLinks, err := attachUprobeWithReturns(exe, s.Name, "uprobe_FetchMessage", "uprobe_FetchMessage_Returns", t.uprobes, address, s, textSection, textSectionLen, textSectionData, ef.Machine, "failed to attach uprobe_FetchMessage uprobe", true)
+			if err != nil {
+				return nil, err
+			}
+			if retLinks != nil {
+				klog.Infoln("uprobe_FetchMessage 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
 }
@@ -528,6 +901,87 @@ func getSslLibPathAndVersion(pid uint32) (string, string) {
 	return libsslPath, "v" + version
 }
 
+// selectGRPCServerProbe 根据 gRPC 版本选择服务端探针
+func (t *Tracer) selectGRPCServerProbe(major, minor int) string {
+	// 根据 gRPC 版本选择相应的探针
+	if major == 1 && minor >= 69 {
+		// 现代版本 (>= 1.69.0) 使用新的探针
+		klog.Infof("Selecting modern gRPC server probe for version %d.%d", major, minor)
+		return "uprobe_server_handleStream2"
+	} else {
+		// 传统版本 (< 1.69.0) 使用旧的探针
+		klog.Infof("Selecting legacy gRPC server probe for version %d.%d", major, minor)
+		return "uprobe_server_handleStream"
+	}
+}
+
+// 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 {

+ 105 - 22
ebpftracer/tracer.go

@@ -7,13 +7,14 @@ import (
 	"encoding/hex"
 	"errors"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/utils"
-	"github.com/coroot/coroot-node-agent/utils/try"
 	"os"
 	"strconv"
 	"strings"
 	"time"
 
+	"github.com/coroot/coroot-node-agent/utils"
+	"github.com/coroot/coroot-node-agent/utils/try"
+
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/link"
 	"github.com/cilium/ebpf/perf"
@@ -157,12 +158,18 @@ type Tracer struct {
 func NewTracer(kernelVersion string, disableL7Tracing, disableE2ETracing, disableStackTracing bool) *Tracer {
 	if disableL7Tracing {
 		klog.Infoln("L7 tracing is disabled")
+	} else {
+		klog.Infoln("L7 tracing is enabled")
 	}
 	if disableE2ETracing {
-		klog.Infoln("L7 tracing is disabled")
+		klog.Infoln("e2e is disabled")
+	} else {
+		klog.Infoln("e2e is enabled")
 	}
 	if disableStackTracing {
 		klog.Infoln("L7 stack is disabled")
+	} else {
+		klog.Infoln("L7 stack is enabled")
 	}
 	return &Tracer{
 		kernelVersion:       kernelVersion,
@@ -172,6 +179,7 @@ func NewTracer(kernelVersion string, disableL7Tracing, disableE2ETracing, disabl
 
 		readers: map[string]*perf.Reader{},
 		uprobes: map[string]*ebpf.Program{},
+		links:   []link.Link{},
 	}
 }
 
@@ -187,18 +195,26 @@ func (t *Tracer) Run(events chan<- Event) error {
 
 func (t *Tracer) Close() {
 	for k, p := range t.uprobes {
-		err := p.Close()
-		klog.WithError(err).Infof("[close] uprobes %s", k)
+		if p != nil {
+			err := p.Close()
+			klog.WithError(err).Infof("[close] uprobes %s", k)
+		}
 	}
 	for _, l := range t.links {
-		err := l.Close()
-		klog.WithError(err).Infof("[close] links")
+		if l != nil {
+			err := l.Close()
+			klog.WithError(err).Infof("[close] links")
+		}
 	}
 	for k, r := range t.readers {
-		err := r.Close()
-		klog.WithError(err).Infof("[close] readers %s", k)
+		if r != nil {
+			err := r.Close()
+			klog.WithError(err).Infof("[close] readers %s", k)
+		}
+	}
+	if t.collection != nil {
+		t.collection.Close()
 	}
-	t.collection.Close()
 }
 
 func (t *Tracer) init(ch chan<- Event) error {
@@ -228,7 +244,8 @@ func (t *Tracer) init(ch chan<- Event) error {
 		typ := EventTypeConnectionOpen
 		if s.Listen {
 			typ = EventTypeListenOpen
-		} else if listens[uint64(s.pid)<<32|uint64(s.SAddr.Port())] || s.DAddr.Port() > s.SAddr.Port() { // inbound
+			//} else if listens[uint64(s.pid)<<32|uint64(s.SAddr.Port())] || s.DAddr.Port() > s.SAddr.Port() { // inbound
+		} else if listens[uint64(s.pid)<<32|uint64(s.SAddr.Port())] { // 存在误判
 			continue
 		}
 		ch <- Event{
@@ -385,7 +402,9 @@ func (t *Tracer) LinkEbpfProg() error {
 			}
 			if strings.HasPrefix(programSpec.SectionName, "kretprobe/") {
 				l, err = link.Kretprobe(programSpec.AttachTo, program, nil)
-				t.links = append(t.links, l)
+				if err == nil {
+					t.links = append(t.links, l)
+				}
 				continue
 			}
 			l, err = link.Kprobe(programSpec.AttachTo, program, nil)
@@ -516,15 +535,17 @@ type l7Event struct {
 	EndtAt              uint64 // ns
 	TraceStart          uint32
 	TraceEnd            uint32
+	TraceType           uint32
 	EventCount          uint32
 	Sport               uint16
 	Dport               uint16
-	SAddr               [16]byte
-	DAddr               [16]byte
+	SAddr               HashByte16
+	DAddr               HashByte16
 	ComponentSport      uint16
 	ComponentDport      uint16
-	ComponentSAddr      [16]byte
-	ComponentDAddr      [16]byte
+	IsTls               uint16
+	ComponentSAddr      HashByte16
+	ComponentDAddr      HashByte16
 	AssumedAppId        HashByte
 	SpanId              HashByte
 	TraceIdFrom         HashByte16
@@ -532,6 +553,14 @@ type l7Event struct {
 	InstanceIdFrom      HashByte
 	AppIdFrom           HashByte
 	SpanIdFrom          HashByte
+	TypeFrom            [1]byte
+	SysvcFrom           [76]byte // cwother header value: NN:ParentServiceName:MM:ParentServiceSysTag
+	RPCTarget           [64]byte
+	ErrorMsg            HashByte128
+	MQ                  struct {
+		Topic [256]byte // MQ topic (e.g., Kafka topic)
+		Key   [256]byte // MQ key (e.g., Kafka message key)
+	}
 }
 
 type SocketDataBufferddd struct {
@@ -746,6 +775,7 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy
 			payload := reader.Bytes()
 			req := &l7.RequestData{
 				Protocol:       l7.Protocol(v.Protocol),
+				Pid:            v.Pid,
 				Status:         l7.Status(v.Status),
 				Duration:       time.Duration(v.Duration),
 				Method:         l7.Method(v.Method),
@@ -753,6 +783,7 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy
 				TraceId:        v.TraceId,
 				TraceStart:     v.TraceStart,
 				TraceEnd:       v.TraceEnd,
+				TraceType:      v.TraceType,
 				EventCount:     v.EventCount,
 				AssumedAppId:   hex.EncodeToString(v.AssumedAppId[:]),
 				SpanId:         hex.EncodeToString(v.SpanId[:]),
@@ -760,21 +791,31 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy
 				EndAt:          v.EndtAt,
 				ComponentSAddr: ipPort(v.ComponentSAddr, v.ComponentSport),
 				ComponentDAddr: ipPort(v.ComponentDAddr, v.ComponentDport),
+				DestAddrString: utils.BytesToString(v.RPCTarget[:]),
+				ErrorMsg:       utils.BytesToString(v.ErrorMsg[:]),
+				IsTls:          v.IsTls > 0,
+				MQTopic:        utils.BytesToString(v.MQ.Topic[:]),
+				MQKey:          utils.BytesToString(v.MQ.Key[:]),
 			}
 			if req.Protocol == l7.ProtocolHTTP {
-				klog.Infof("runEventsReader ComponentSAddr.String %s", req.ComponentSAddr.String())
-				klog.Infof("runEventsReader ComponentDAddr.String %s", req.ComponentDAddr.String())
+				klog.Debugf("runEventsReader ComponentSAddr.String %s", req.ComponentSAddr.String())
+				klog.Debugf("runEventsReader ComponentDAddr.String %s", req.ComponentDAddr.String())
 			}
-			if v.TraceEnd == 1 {
+			if v.TraceEnd == TRACE_STATUS {
 				req.ParentSpanContext.TraceIdFrom = hex.EncodeToString(v.TraceIdFrom[:])
 				req.ParentSpanContext.CalledId = hex.EncodeToString(v.CalledId[:])
 				req.ParentSpanContext.InstanceIdFrom = hex.EncodeToString(v.InstanceIdFrom[:])
 				req.ParentSpanContext.AppIdFrom = hex.EncodeToString(v.AppIdFrom[:])
 				req.ParentSpanContext.SpanIdFrom = hex.EncodeToString(v.SpanIdFrom[:])
+				req.ParentSpanContext.TypeFrom = l7.TypeFrom(hex.EncodeToString(v.TypeFrom[:]))
+				req.SysvcFrom = utils.BytesToString(v.SysvcFrom[:])
+				klog.Debugf("cwother '%s'", req.SysvcFrom)
+				// klog.Debugf("req.ParentSpanContext.TraceIdFrom %s", req.ParentSpanContext.TraceIdFrom)
+				// klog.Debugf("req.ParentSpanContext.TypeFrom %s", req.ParentSpanContext.TypeFrom)
 				req.SAddr = ipPort(v.SAddr, v.Sport)
 				req.DAddr = ipPort(v.DAddr, v.Dport)
-				klog.Infof("runEventsReader SAddr.String %s", req.SAddr.String())
-				klog.Infof("runEventsReader DAddr.String %s", req.DAddr.String())
+				// klog.Debugf("runEventsReader SAddr.String %s", req.SAddr.String())
+				// klog.Debugf("runEventsReader DAddr.String %s", req.DAddr.String())
 			}
 			switch {
 			case v.PayloadSize == 0:
@@ -867,6 +908,7 @@ func ipPort(ip [16]byte, port uint16) netaddr.IPPort {
 }
 
 func (t *Tracer) InitKProcInfo(pid uint32, appInfo *AppInfo) error {
+	fmt.Println("InitKProcInfo111111111111111111111111111111111")
 	var err error
 	var info EbpfProcInfo
 	if appInfo.EBPFProcInfo == nil {
@@ -880,6 +922,47 @@ func (t *Tracer) InitKProcInfo(pid uint32, appInfo *AppInfo) error {
 		info.AppId = appInfo.AppIdHash.HashtVal
 	}
 
+	// 构建 sysvc 值:{app_name_len}:app_name:{appServiceType_len}:appServiceType:{SysTagLen}[:SysTag]
+	// appServiceType 固定为 "APPLICATION"
+	appName := appInfo.AppName
+	nodeInfo := NODE_INFO.GetNodeInfo()
+	sysTag := ""
+	if nodeInfo != nil {
+		sysTag = nodeInfo.SysTag
+	}
+	appServiceType := "APPLICATION"
+
+	appNameLen := len(appName)
+	appServiceTypeLen := len(appServiceType)
+	sysTagLen := len(sysTag)
+
+	// 限制长度,避免超出数组大小
+	if appNameLen > 32 {
+		appNameLen = 32
+		appName = appName[:32]
+	}
+	if sysTagLen > 32 {
+		sysTagLen = 32
+		sysTag = sysTag[:32]
+	}
+
+	// 格式:{app_name_len}:app_name:{appServiceType_len}:appServiceType[:{SysTagLen}:SysTag]
+	// 如果 sysTag 为空,则为 {app_name_len}:app_name:{appServiceType_len}:appServiceType
+	// 如果 sysTag 不为空,则为 {app_name_len}:app_name:{appServiceType_len}:appServiceType:{SysTagLen}:SysTag
+	// 长度不足2位时前面补0,例如:08:service:12:APPLICATION 或 08:service:12:APPLICATION:03:tag
+	var sysvcStr string
+	if sysTagLen == 0 {
+		sysvcStr = fmt.Sprintf("%02d:%s:%02d:%s", appNameLen, appName, appServiceTypeLen, appServiceType)
+	} else {
+		sysvcStr = fmt.Sprintf("%02d:%s:%02d:%s:%02d:%s", appNameLen, appName, appServiceTypeLen, appServiceType, sysTagLen, sysTag)
+	}
+
+	// 复制到字节数组,确保不超过 76 字节
+	sysvcBytes := []byte(sysvcStr)
+	if len(sysvcBytes) > 76 {
+		sysvcBytes = sysvcBytes[:76]
+	}
+	copy(info.Sysvc[:], sysvcBytes)
 	_, err = tracer.UpdateProcInfoToMap(t.collection, pid, info)
 	if err != nil {
 		klog.Error("failed to update program info", err)
@@ -891,7 +974,7 @@ func (t *Tracer) InitKProcInfo(pid uint32, appInfo *AppInfo) error {
 func (t *Tracer) DelKProcInfo(pid uint32) error {
 	_, err := tracer.DelProcInfoFromMap(t.collection, pid)
 	if err != nil {
-		klog.Error("failed to delete proc info", err)
+		klog.WithField("pid", pid).Error("failed to delete proc info", err)
 	}
 	return err
 }

+ 1 - 1
ebpftracer/tracer/allocate.go

@@ -44,7 +44,7 @@ func remoteAllocate(pid int, mapSize uint64) (uint64, error) {
 	}
 
 	defer func() {
-		klog.Info("Detaching from process", "pid", pid)
+		klog.Info("Detaching from process ", pid)
 		err := program.Detach()
 		if err != nil {
 			klog.Error(err, "Failed to detach ptrace", "pid", pid)

+ 225 - 0
ebpftracer/tracer/inject/inject.go

@@ -0,0 +1,225 @@
+package inject
+
+import (
+	"bufio"
+	"debug/elf"
+	"fmt"
+	klog "github.com/sirupsen/logrus"
+	"io"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+	"syscall"
+)
+
+type LibNetInfo struct {
+	LibName      string
+	LibPath      string
+	FuncSymbol   InstInfo
+	FuncConvert0Symbol   InstInfo
+	FuncGetTTLSymbol   InstInfo
+	InnerSymbol  InnerSymbolInfo
+	ProcLoadPath string
+	FileDeleted  bool
+	MapFile      string
+}
+
+type UprobeData struct {
+	Offset  int
+	Func    string
+	ELFPath string
+}
+
+type JvmInjector struct {
+	Pid               int
+	ReleaseLibNetInfo LibNetInfo
+	DebugLibNetInfo   LibNetInfo
+	RecodeInfo        LibNetInfo
+	// 原方法首个指令不为jmp | ReleaseLibNetInfo 读取无异常
+	PreCheck struct {
+		NeedInjectionCheck bool // 原指令校验 true表示可以继续执行注入
+		LoadingCheck       bool // true 表示加载成功
+		IoFdCheck          bool // fd地址校验
+		NetSendFuncCheck   bool // netsend校验
+		EbpfCanInjection   bool // 满足则注入ebpf
+	}
+	AfterCheck struct {
+		IoFdCheck        bool
+		NetSendFuncCheck bool
+	}
+	Uprobe UprobeData
+	Rootfs string
+}
+
+func FindLibBaseByPathFromProcMaps(pid int, libPath string) (uint64, string, error) {
+	mapsFile := fmt.Sprintf("/proc/%d/maps", pid)
+	file, err := os.Open(mapsFile)
+	if err != nil {
+		return 0, "", err
+	}
+	defer file.Close()
+	var start, end uint64
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if strings.Contains(line, libPath) {
+			fmt.Sscanf(line, "%x-%x", &start, &end)
+			fields := strings.Fields(line)
+			if len(fields) > 5 {
+				path := fields[5]
+				if strings.HasSuffix(path, ".so") {
+					return start, path, nil
+				}
+			}
+		}
+	}
+
+	return 1, "", fmt.Errorf("library %s not found in process.", libPath)
+}
+
+func FindLibBaseFromProcMaps(pid int, libName string) (uint64, string, string, bool, error) {
+	mapsFile := fmt.Sprintf("/proc/%d/maps", pid)
+	file, err := os.Open(mapsFile)
+	if err != nil {
+		return 0, "", "", false, err
+	}
+	defer file.Close()
+	var start, end uint64
+	var deleted bool
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if strings.Contains(line, "/"+libName) {
+			fmt.Sscanf(line, "%x-%x", &start, &end)
+			fields := strings.Fields(line)
+			if len(fields) > 5 {
+				path := fields[5]
+				if len(fields) > 6 && fields[6] == "(deleted)" {
+					deleted = true
+				}
+				if strings.HasSuffix(path, ".so") {
+					klog.Infof("[inject] found library in map %s", path)
+					return start, path, fmt.Sprintf("/proc/%d/map_files/%s", pid, fields[0]), deleted, nil
+				}
+			}
+		}
+	}
+
+	return 1, "", "", false, fmt.Errorf("[FindLibBaseFromProcMaps] library %s not found", libName)
+}
+
+func CopyFileAndMatchPermissions(srcFile, destFile, permFile string) error {
+	// 获取权限参考文件的信息
+	permInfo, err := os.Stat(permFile)
+	if err != nil {
+		return fmt.Errorf("failed to stat permission file: %w", err)
+	}
+	mode := permInfo.Mode()
+	uid, gid := -1, -1
+	if stat, ok := permInfo.Sys().(*syscall.Stat_t); ok {
+		uid = int(stat.Uid)
+		gid = int(stat.Gid)
+	}
+
+	srcInfo, err := os.Stat(srcFile)
+	if err != nil {
+		return fmt.Errorf("failed to stat source file/dir: %w", err)
+	}
+
+	if srcInfo.IsDir() {
+		// 复制整个目录
+		return filepath.WalkDir(srcFile, func(path string, d fs.DirEntry, err error) error {
+			if err != nil {
+				return err
+			}
+			relPath, err := filepath.Rel(srcFile, path)
+			if err != nil {
+				return err
+			}
+			targetPath := filepath.Join(destFile, relPath)
+
+			if d.IsDir() {
+				if err := os.MkdirAll(targetPath, mode); err != nil {
+					return fmt.Errorf("failed to create directory %s: %w", targetPath, err)
+				}
+				if uid >= 0 && gid >= 0 {
+					if err := os.Chown(targetPath, uid, gid); err != nil {
+						return fmt.Errorf("failed to set directory ownership: %w", err)
+					}
+				}
+			} else {
+				if err := copyOneFile(path, targetPath, mode, uid, gid); err != nil {
+					return fmt.Errorf("failed to copy file %s: %w", path, err)
+				}
+			}
+			return nil
+		})
+	} else {
+		// 复制单个文件
+		return copyOneFile(srcFile, destFile, mode, uid, gid)
+	}
+}
+
+func copyOneFile(src, dst string, mode os.FileMode, uid, gid int) error {
+	in, err := os.Open(src)
+	if err != nil {
+		return fmt.Errorf("failed to open source file: %w", err)
+	}
+	defer in.Close()
+
+	// 创建父目录
+	if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
+		return fmt.Errorf("failed to create parent directory: %w", err)
+	}
+
+	out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
+	if err != nil {
+		return fmt.Errorf("failed to create destination file: %w", err)
+	}
+	defer out.Close()
+
+	if _, err := io.Copy(out, in); err != nil {
+		return fmt.Errorf("failed to copy file content: %w", err)
+	}
+
+	if uid >= 0 && gid >= 0 {
+		if err := out.Chown(uid, gid); err != nil {
+			return fmt.Errorf("failed to set file ownership: %w", err)
+		}
+	}
+	return nil
+}
+func GetFunctionOffset(libPath, functionName string) (elf.Symbol, error) {
+	elfFile, err := elf.Open(libPath)
+	if err != nil {
+		return elf.Symbol{}, fmt.Errorf("failed to open ELF file: %v", err)
+	}
+	defer elfFile.Close()
+
+	symbols, err := elfFile.DynamicSymbols()
+	if err != nil {
+		return elf.Symbol{}, fmt.Errorf("failed to read dynamic symbols: %v", err)
+	}
+
+	for _, sym := range symbols {
+		if sym.Name == functionName {
+			//fmt.Println("size:", sym.Size)
+			return sym, nil
+		}
+	}
+
+	//textSection := elfFile.Section(".text")
+	//if textSection == nil {
+	//	fmt.Println("textSection is null")
+	//	//return nil
+	//}
+	//textSectionData, err := textSection.Data()
+	//if err != nil {
+	//	fmt.Println("textSectionData error is", err)
+	//	//return nil
+	//}
+	//textSectionLen := uint64(len(textSectionData) - 1)
+
+	return elf.Symbol{}, fmt.Errorf("function %s not found", functionName)
+}

+ 347 - 176
ebpftracer/tracer/inject/inject_linux_amd64.go

@@ -16,7 +16,6 @@ import (
 	"github.com/coroot/coroot-node-agent/utils"
 	klog "github.com/sirupsen/logrus"
 	"golang.org/x/arch/x86/x86asm"
-	"io"
 	"os"
 	"path/filepath"
 	"strings"
@@ -27,6 +26,8 @@ import (
 const (
 	IO_FD_FDID_SYM_OFFSET = 129
 	NET_SEND_SYM_OFFSET   = 518
+	// 备份指令长度
+	ORIGIN_CODE_LEN = 12
 )
 
 type InstInfo struct {
@@ -46,44 +47,8 @@ type InnerSymbolInfo struct {
 	NET_Send   InstInfo
 }
 
-type LibNetInfo struct {
-	LibName      string
-	LibPath      string
-	FuncSymbol   InstInfo
-	InnerSymbol  InnerSymbolInfo
-	ProcLoadPath string
-	FileDeleted  bool
-	MapFile      string
-}
-
-type UprobeData struct {
-	Offset  int
-	Func    string
-	ELFPath string
-}
-
-type JvmInjector struct {
-	Pid               int
-	ReleaseLibNetInfo LibNetInfo
-	DebugLibNetInfo   LibNetInfo
-	RecodeInfo        LibNetInfo
-	// 原方法首个指令不为jmp | ReleaseLibNetInfo 读取无异常
-	PreCheck struct {
-		NeedInjectionCheck bool // 原指令校验 true表示可以继续执行注入
-		LoadingCheck       bool // true 表示加载成功
-		IoFdCheck          bool // fd地址校验
-		NetSendFuncCheck   bool // netsend校验
-		EbpfCanInjection   bool // 满足则注入ebpf
-	}
-	AfterCheck struct {
-		IoFdCheck        bool
-		NetSendFuncCheck bool
-	}
-	Uprobe UprobeData
-	Rootfs string
-}
-
 func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
+	klog.Infof("findReleaseAddressInfoFromMem start.")
 	funcAbsAddress := j.ReleaseLibNetInfo.FuncSymbol.SymAddr
 	releaseFuncSym := InnerSymbolInfo{}
 	code, err := j.readMemory(funcAbsAddress, j.ReleaseLibNetInfo.FuncSymbol.SymSize)
@@ -97,7 +62,7 @@ func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
 
 		inst, err := x86asm.Decode(code[pc:], 64)
 		if err != nil {
-			fmt.Printf("Decode error at offset 0x%x: %v\n", pc, err)
+			klog.Errorf("Decode error at offset 0x%x: %v\n", pc, err)
 			pc++ // Skip this byte and try to decode again
 			continue
 		}
@@ -110,7 +75,7 @@ func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
 			Inst:    inst,
 			//IntelInst: x86asm.IntelSyntax(inst, 0, nil),
 		}
-		if pc == 0 && inst.Op == x86asm.JMP {
+		if pc == 10 && inst.Op == x86asm.JMP {
 			// 已经被修改过的首指令
 			j.PreCheck.EbpfCanInjection = true
 			j.Uprobe.ELFPath = j.DebugLibNetInfo.LibPath
@@ -163,8 +128,7 @@ func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
 				releaseFuncSym.IO_fd_fdID = preContext
 				releaseFuncSym.IO_fd_fdID.SymName = "<IO_fd_fdID>(Release)"
 				preInst := preContext.Inst
-				fmt.Println(preInst.Op)
-				fmt.Println((preInst.Args))
+				klog.Infof("[findReleaseAddressInfoFromMem] preInst %v\n]", preInst)
 				// 计算目标地址
 				if preInst.Op == x86asm.MOV &&
 					len(preInst.Args) == 4 &&
@@ -174,31 +138,29 @@ func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
 					if mem, ok := preInst.Args[1].(x86asm.Mem); ok && mem.Base == x86asm.RIP {
 						relOffset := mem.Disp // 直接从Mem结构体中读取偏移
 						targetAddress := preContext.SymAddr + uint64(preInst.Len) + uint64(relOffset)
-						fmt.Printf("Target address: 0x%x\n", targetAddress)
+						klog.Infof("[findReleaseAddressInfoFromMem] target address 0x%x\n", targetAddress)
 						releaseFuncSym.IO_fd_fdID.TargetAddr = targetAddress
 					} else {
 						return fmt.Errorf("The instruction does not use RIP-relative addressing.")
 					}
 				} else {
-					return fmt.Errorf("The decoded instruction is not a MOV to RDX.")
+					return fmt.Errorf("[findReleaseAddressInfoFromMem] The decoded instruction is not a MOV to RDX.")
 				}
 
-				//os.Exit(1)
 			}
 			callCount++
 			if callCount == 4 {
 				releaseFuncSym.NET_Send = currentData
-				fmt.Printf("4 call Decoded instuction at 0x%x: %v\n", funcAbsAddress+pc, inst)
-
+				klog.Infof("4 call Decoded instuction at 0x%x: %v\n", funcAbsAddress+pc, inst)
 				relOffset, ok := inst.Args[0].(x86asm.Rel)
 				if !ok {
 					return fmt.Errorf("The instruction does not use RIP-relative addressing.")
 				}
 				targetAddress := currentData.SymAddr + uint64(inst.Len) + uint64(relOffset)
 				releaseFuncSym.NET_Send.TargetAddr = targetAddress
-				fmt.Println(releaseFuncSym.NET_Send)
+				klog.Infof("[findReleaseAddressInfoFromMem] target address 0x%x\n", releaseFuncSym.NET_Send)
 				releaseFuncSym.NET_Send.SymName = "<NET_Send>(Release)"
-				fmt.Printf("Target address: 0x%x\n", targetAddress)
+				klog.Infof("[findReleaseAddressInfoFromMem] target address 0x%x\n", targetAddress)
 			}
 		}
 		preContext = InstInfo{
@@ -209,11 +171,12 @@ func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
 		pc += uint64(inst.Len)
 	}
 	j.ReleaseLibNetInfo.InnerSymbol = releaseFuncSym
-	j.ReleaseLibNetInfo.FuncSymbol.OriginCode = code[0:5]
+	j.ReleaseLibNetInfo.FuncSymbol.OriginCode = code[0:ORIGIN_CODE_LEN]
 	return nil
 }
 
 func (j *JvmInjector) findDebugAddressInfoFromMem() (uint64, error) {
+	klog.Infof("[findDebugAddressInfoFromMem] Looking for debug address info from Mem")
 	funcAbsAddress := j.DebugLibNetInfo.FuncSymbol.SymAddr
 	debugFuncSym := InnerSymbolInfo{}
 	//debugFuncSym.FuncSymbol.SymAddr = funcAbsAddress
@@ -231,7 +194,7 @@ func (j *JvmInjector) findDebugAddressInfoFromMem() (uint64, error) {
 	for pc < uint64(len(code)) {
 		inst, err := x86asm.Decode(code[pc:], 64)
 		if err != nil {
-			fmt.Printf("Decode error at offset 0x%x: %v\n", pc, err)
+			klog.Errorf("Decode error at offset 0x%x: %v\n", pc, err)
 			pc++ // Skip this byte and try to decode again
 			continue
 		}
@@ -249,7 +212,7 @@ func (j *JvmInjector) findDebugAddressInfoFromMem() (uint64, error) {
 			j.DebugLibNetInfo.FuncSymbol.OriginInst = currentData.Inst
 		}
 		if pc == IO_FD_FDID_SYM_OFFSET {
-			fmt.Printf("Instuction at 0x%x: %v\n", preContext.PC, preContext.Inst)
+			klog.Infof("Instuction at 0x%x: %v\n", preContext.PC, preContext.Inst)
 			debugFuncSym.IO_fd_fdID = currentData
 			debugFuncSym.IO_fd_fdID.SymName = "<IO_fd_fdID>(Debug)"
 			// 计算目标地址
@@ -262,7 +225,7 @@ func (j *JvmInjector) findDebugAddressInfoFromMem() (uint64, error) {
 					// 直接从Mem结构体中读取偏移
 					relOffset := mem.Disp
 					targetAddress := currentData.SymAddr + uint64(currentData.Inst.Len) + uint64(relOffset)
-					fmt.Printf("Find %s Target address: 0x%x\n", debugFuncSym.IO_fd_fdID.SymName, targetAddress)
+					klog.Infof("Find %s Target address: 0x%x\n", debugFuncSym.IO_fd_fdID.SymName, targetAddress)
 					debugFuncSym.IO_fd_fdID.TargetAddr = targetAddress
 					// 保存原始数据
 					debugFuncSym.IO_fd_fdID.OriginTargetAddr = targetAddress
@@ -272,12 +235,12 @@ func (j *JvmInjector) findDebugAddressInfoFromMem() (uint64, error) {
 					return 0, fmt.Errorf("The instruction does not use RIP-relative addressing.")
 				}
 			} else {
-				return 0, fmt.Errorf("The decoded instruction is not a MOV to RDX.")
+				return 0, fmt.Errorf("[findDebugAddressInfoFromMem] The decoded instruction is not a MOV to RDX.")
 			}
 		}
 		if pc == NET_SEND_SYM_OFFSET {
 			debugFuncSym.NET_Send = currentData
-			fmt.Printf("4 call Decoded instuction at 0x%x: %v\n", funcAbsAddress+pc, inst)
+			klog.Infof("4 call Decoded instuction at 0x%x: %v\n", funcAbsAddress+pc, inst)
 			relOffset, ok := (inst.Args[0].(x86asm.Rel))
 			if !ok {
 				return 0, fmt.Errorf("The decoded instruction is not a Rel.")
@@ -285,7 +248,7 @@ func (j *JvmInjector) findDebugAddressInfoFromMem() (uint64, error) {
 			targetAddress := currentData.SymAddr + uint64(inst.Len) + uint64(relOffset)
 			debugFuncSym.NET_Send.TargetAddr = targetAddress
 			debugFuncSym.NET_Send.SymName = "<NET_Send>(Debug)"
-			fmt.Printf("Find %s Target address: 0x%x\n", debugFuncSym.NET_Send.SymName, targetAddress)
+			klog.Infof("Find %s Target address: 0x%x\n", debugFuncSym.NET_Send.SymName, targetAddress)
 
 			// 保存原始数据
 			debugFuncSym.NET_Send.OriginTargetAddr = targetAddress
@@ -306,6 +269,7 @@ func (j *JvmInjector) findDebugAddressInfoFromMem() (uint64, error) {
 }
 
 func (j *JvmInjector) checkDebugFuncSymAfterChange() (uint64, error) {
+	klog.Infof("Checking debug function symbol after injection")
 	funcAbsAddress := j.DebugLibNetInfo.FuncSymbol.SymAddr
 	debugFuncSym := InnerSymbolInfo{}
 	code, err := j.readMemory(funcAbsAddress, j.DebugLibNetInfo.FuncSymbol.SymSize)
@@ -319,7 +283,7 @@ func (j *JvmInjector) checkDebugFuncSymAfterChange() (uint64, error) {
 	for pc < uint64(len(code)) {
 		inst, err := x86asm.Decode(code[pc:], 64)
 		if err != nil {
-			fmt.Printf("Decode error at offset 0x%x: %v\n", pc, err)
+			klog.Infof("Decode error at offset 0x%x: %v\n", pc, err)
 			pc++ // Skip this byte and try to decode again
 			continue
 		}
@@ -332,7 +296,7 @@ func (j *JvmInjector) checkDebugFuncSymAfterChange() (uint64, error) {
 			Inst:    inst,
 		}
 		if pc == NET_SEND_SYM_OFFSET {
-			fmt.Printf("Instuction at 0x%x: %v\n", preContext.PC, preContext.Inst)
+			klog.Infof("Instuction at 0x%x: %v\n", preContext.PC, preContext.Inst)
 			debugFuncSym.IO_fd_fdID = currentData
 			debugFuncSym.IO_fd_fdID.SymName = "<IO_fd_fdID>(Debug)"
 			// 计算目标地址
@@ -345,7 +309,7 @@ func (j *JvmInjector) checkDebugFuncSymAfterChange() (uint64, error) {
 					// 直接从Mem结构体中读取偏移
 					relOffset := mem.Disp
 					targetAddress := currentData.SymAddr + uint64(currentData.Inst.Len) + uint64(relOffset)
-					fmt.Printf("Find %s Target address: 0x%x\n", debugFuncSym.IO_fd_fdID.SymName, targetAddress)
+					klog.Infof("Find %s Target address: 0x%x\n", debugFuncSym.IO_fd_fdID.SymName, targetAddress)
 					debugFuncSym.IO_fd_fdID.TargetAddr = targetAddress
 					//j.PreCheck.IoFdCheck = true
 
@@ -353,19 +317,19 @@ func (j *JvmInjector) checkDebugFuncSymAfterChange() (uint64, error) {
 						j.DebugLibNetInfo.InnerSymbol.IO_fd_fdID.TargetAddr = targetAddress
 						j.DebugLibNetInfo.InnerSymbol.IO_fd_fdID.Inst = currentData.Inst
 						j.AfterCheck.IoFdCheck = true
-						fmt.Println("ok")
+						klog.Infoln("ok")
 					}
 				} else {
 					return 0, fmt.Errorf("The instruction does not use RIP-relative addressing.")
 				}
 			} else {
-				return 0, fmt.Errorf("The decoded instruction is not a MOV to RDX.")
+				return 0, fmt.Errorf("[checkDebugFuncSymAfterChange] The decoded instruction is not a MOV to RDX.")
 			}
 		}
 		if pc == NET_SEND_SYM_OFFSET {
 			debugFuncSym.NET_Send = currentData
 			//fmt.Println(currentData.IntelInst)
-			//fmt.Printf("4 call Decoded instuction at 0x%x: %v\n", funcAbsAddress+pc, inst)
+			//klog.Infof("4 call Decoded instuction at 0x%x: %v\n", funcAbsAddress+pc, inst)
 			relOffset, ok := (inst.Args[0].(x86asm.Rel))
 			if !ok {
 				return 0, fmt.Errorf("The decoded instruction is not a Rel.")
@@ -373,7 +337,7 @@ func (j *JvmInjector) checkDebugFuncSymAfterChange() (uint64, error) {
 			targetAddress := currentData.SymAddr + uint64(inst.Len) + uint64(relOffset)
 			debugFuncSym.NET_Send.TargetAddr = targetAddress
 			debugFuncSym.NET_Send.SymName = "<NET_Send>(Debug)"
-			fmt.Printf("Find %s Target address: 0x%x\n", debugFuncSym.NET_Send.SymName, targetAddress)
+			klog.Infof("Find %s Target address: 0x%x\n", debugFuncSym.NET_Send.SymName, targetAddress)
 
 			if targetAddress == j.ReleaseLibNetInfo.InnerSymbol.NET_Send.TargetAddr {
 				j.DebugLibNetInfo.InnerSymbol.NET_Send.TargetAddr = targetAddress
@@ -398,22 +362,54 @@ func (j *JvmInjector) checkReleaseFuncSymAfterChange() error {
 	if err != nil {
 		return fmt.Errorf("readMemory error in checkReleaseFuncSymAfterChange <%v>", err)
 	}
-	inst, err := x86asm.Decode(code[0:], 64)
-	if err != nil {
-		return fmt.Errorf("Decode error in checkReleaseFuncSymAfterChange <%v>", err)
+
+	// 原函数内容注释掉
+	// inst, err := x86asm.Decode(code[0:], 64)
+	// if err != nil {
+	// 	return fmt.Errorf("Decode error in checkReleaseFuncSymAfterChange <%v>", err)
+	// }
+	// if inst.Op != x86asm.JMP {
+	// 	return fmt.Errorf("The instruction does not JMP.")
+	// }
+	// relOffset, ok := inst.Args[0].(x86asm.Rel)
+	// if !ok {
+	// 	return fmt.Errorf("The instruction does not use RIP-relative addressing.")
+	// }
+	// // 验证target与Debug入口是否一致
+	// targetAddress := funcAbsAddress + uint64(inst.Len) + uint64(relOffset)
+	// if targetAddress != j.DebugLibNetInfo.FuncSymbol.SymAddr {
+	// 	return fmt.Errorf("Function entry jmp address does not match expectations.")
+	// }
+
+	// 新的验证逻辑:验证指令序列:movabs $imm64,%rax 和 jmp *%rax
+	if len(code) < 13 { // movabs(10字节) + jmp(3字节) = 13字节
+		return fmt.Errorf("Instruction sequence too short, expected at least 13 bytes, got %d", len(code))
 	}
-	if inst.Op != x86asm.JMP {
-		return fmt.Errorf("The instruction does not JMP.")
+
+	// 验证第一个指令:movabs $imm64,%rax
+	// movabs 指令格式:48 B8 + 8字节立即数 (RAX寄存器)
+	// 48 B8 = movabs rax, imm64
+	if code[0] != 0x48 || code[1] != 0xB8 {
+		return fmt.Errorf("First instruction is not movabs rax, imm64. Got: 0x%02x 0x%02x", code[0], code[1])
 	}
-	relOffset, ok := inst.Args[0].(x86asm.Rel)
-	if !ok {
-		return fmt.Errorf("The instruction does not use RIP-relative addressing.")
+
+	// 提取立即数值 (小端序)
+	imm64 := uint64(code[2]) | uint64(code[3])<<8 | uint64(code[4])<<16 | uint64(code[5])<<24 |
+		uint64(code[6])<<32 | uint64(code[7])<<40 | uint64(code[8])<<48 | uint64(code[9])<<56
+
+	// 验证立即数是否与 Debug 函数地址一致
+	expectedAddr := j.DebugLibNetInfo.FuncSymbol.SymAddr
+	if imm64 != expectedAddr {
+		return fmt.Errorf("movabs immediate value mismatch. Expected: 0x%x (Debug function addr), Got: 0x%x", expectedAddr, imm64)
 	}
-	// 验证target与Debug入口是否一致
-	targetAddress := funcAbsAddress + uint64(inst.Len) + uint64(relOffset)
-	if targetAddress != j.DebugLibNetInfo.FuncSymbol.SymAddr {
-		return fmt.Errorf("Function entry jmp address does not match expectations.")
+
+	// 验证第二个指令:jmp *%rax
+	// jmp *%rax 指令格式:FF E0
+	if code[10] != 0xFF || code[11] != 0xE0 {
+		return fmt.Errorf("Second instruction is not jmp *%%rax. Got: 0x%02x 0x%02x", code[10], code[11])
 	}
+
+	klog.Infof("[checkReleaseFuncSymAfterChange] Successfully verified instruction sequence: movabs $0x%x,%%rax; jmp *%%rax", imm64)
 	return nil
 }
 
@@ -463,7 +459,7 @@ func findLibraryBasesList(pid int, libraryName string, libPath string) ([]uint64
 	return bases, nil
 }
 
-func (j *JvmInjector) findLibBaseFromProcMaps(libName string) (uint64, string, string, bool, error) {
+func (j *JvmInjector) findLibBaseFromProcMaps(pid int, libName string) (uint64, string, string, bool, error) {
 	mapsFile := fmt.Sprintf("/proc/%d/maps", j.Pid)
 	file, err := os.Open(mapsFile)
 	if err != nil {
@@ -558,7 +554,7 @@ func (j *JvmInjector) getFunctionOffset(libPath, functionName string) (elf.Symbo
 
 func (j *JvmInjector) InitProg() error {
 	// 获取release库的基地址
-	baseAddress, releaseSoFilePathInProc, mapFilesPath, deleted, err := j.findLibBaseFromProcMaps(j.ReleaseLibNetInfo.LibName)
+	baseAddress, releaseSoFilePathInProc, mapFilesPath, deleted, err := FindLibBaseFromProcMaps(j.Pid, j.ReleaseLibNetInfo.LibName)
 	//j.ReleaseLibNetInfo.LibPath = releaseSoFilePathInProc
 	j.ReleaseLibNetInfo.FileDeleted = deleted
 	j.ReleaseLibNetInfo.MapFile = mapFilesPath
@@ -581,13 +577,13 @@ func (j *JvmInjector) InitProg() error {
 
 	// find cwlibnet.so in proc maps
 	var readDebugSoPathInMaps string
-	_, readDebugSoPathInMaps, j.DebugLibNetInfo.MapFile, j.DebugLibNetInfo.FileDeleted, _ = j.findLibBaseFromProcMaps(j.DebugLibNetInfo.LibName)
+	_, readDebugSoPathInMaps, j.DebugLibNetInfo.MapFile, j.DebugLibNetInfo.FileDeleted, _ = FindLibBaseFromProcMaps(j.Pid, j.DebugLibNetInfo.LibName)
 	j.DebugLibNetInfo.LibPath = debugSoFilePhysicalPath
 	j.DebugLibNetInfo.ProcLoadPath = filepath.Join(jvmLibBaseDir, j.DebugLibNetInfo.LibName)
 	// condition create
 	pathFromProg := utils.GetDefaultLibsPath("jvm", j.DebugLibNetInfo.LibName)
 	if noFileErr != nil && readDebugSoPathInMaps == "" && !j.DebugLibNetInfo.FileDeleted {
-		err = copyFileAndMatchPermissions(pathFromProg, debugSoFilePhysicalPath, pJvmlibnetPhysicalPath)
+		err = CopyFileAndMatchPermissions(pathFromProg, debugSoFilePhysicalPath, pJvmlibnetPhysicalPath)
 		klog.Infof("[src:%s],[target:%s],[perm:%s]", pathFromProg, debugSoFilePhysicalPath, pJvmlibnetPhysicalPath)
 		if err != nil {
 			return err
@@ -600,7 +596,7 @@ func (j *JvmInjector) InitProg() error {
 	klog.Infof("[inject] Base address of [%s]:[%x]", j.ReleaseLibNetInfo.LibName, baseAddress)
 
 	// 获取函数的偏移量
-	functionSym, err := j.getFunctionOffset(pJvmlibnetPhysicalPath, functionName)
+	functionSym, err := GetFunctionOffset(pJvmlibnetPhysicalPath, functionName)
 
 	// 计算函数的实际内存地址
 	j.ReleaseLibNetInfo.FuncSymbol.SymAddr = baseAddress + functionSym.Value
@@ -631,7 +627,7 @@ func (j *JvmInjector) findDebugFuncContextFromLibPath() error {
 	//libName := j.DebugLibNetInfo.LibPath
 
 	// 获取release库的基地址
-	baseAddress, libPath, err := j.findLibBaseByPathFromProcMaps(j.DebugLibNetInfo.ProcLoadPath)
+	baseAddress, libPath, err := FindLibBaseByPathFromProcMaps(j.Pid, j.DebugLibNetInfo.ProcLoadPath)
 	klog.Infof("[inject] debug base address of [%s] : %x", libPath, baseAddress)
 	functionName := j.DebugLibNetInfo.FuncSymbol.SymName
 	//j.DebugLibNetInfo.LibPath = libPath
@@ -641,7 +637,7 @@ func (j *JvmInjector) findDebugFuncContextFromLibPath() error {
 	}
 
 	// 获取函数的偏移量
-	functionSym, err := j.getFunctionOffset(j.DebugLibNetInfo.LibPath, functionName)
+	functionSym, err := GetFunctionOffset(j.DebugLibNetInfo.LibPath, functionName)
 	// 计算函数的实际内存地址
 	j.DebugLibNetInfo.FuncSymbol.SymAddr = baseAddress + functionSym.Value
 	j.DebugLibNetInfo.FuncSymbol.SymSize = functionSym.Size
@@ -650,31 +646,49 @@ func (j *JvmInjector) findDebugFuncContextFromLibPath() error {
 		return fmt.Errorf("Error getting function offset: %v", err)
 	}
 
+	functionConvert0Sym, err := GetFunctionOffset(j.DebugLibNetInfo.LibPath, j.DebugLibNetInfo.FuncConvert0Symbol.SymName)
+	// 计算函数的实际内存地址
+	j.DebugLibNetInfo.FuncConvert0Symbol.SymAddr = baseAddress + functionConvert0Sym.Value
+	j.DebugLibNetInfo.FuncConvert0Symbol.SymSize = functionConvert0Sym.Size
+
+	if err != nil {
+		return fmt.Errorf("Error getting function offset: %v", err)
+	}
+
+	functionGetTTLSym, err := GetFunctionOffset(j.DebugLibNetInfo.LibPath, j.DebugLibNetInfo.FuncGetTTLSymbol.SymName)
+	// 计算函数的实际内存地址
+	j.DebugLibNetInfo.FuncGetTTLSymbol.SymAddr = baseAddress + functionGetTTLSym.Value
+	j.DebugLibNetInfo.FuncGetTTLSymbol.SymSize = functionGetTTLSym.Size
+
+	if err != nil {
+		return fmt.Errorf("Error getting function offset: %v", err)
+	}
+
 	_, err = j.findDebugAddressInfoFromMem()
 	if err != nil {
 		return fmt.Errorf("Error finding first CALL instuction: %v", err)
 	}
-	fmt.Printf("First CALL instuction o1f %s at base 0x%x\n", functionName, baseAddress)
+	klog.Infof("First CALL instuction o1f %s at base 0x%x\n", functionName, baseAddress)
 	return nil
 }
 
 func printCodeData(data LibNetInfo) {
-	fmt.Printf("========FuncEnter <0x%x> \n", data.FuncSymbol.SymAddr)
-	fmt.Printf("Name %s | CurrentAddr:<0x%x>\nOrigin-TargetAddr:<0x%x> | TargetAddr:<0x%x> \nOrigin-Inst:<%s> | Inst:<%s> \n",
+	klog.Infof("========FuncEnter <0x%x> \n", data.FuncSymbol.SymAddr)
+	klog.Infof("Name %s | CurrentAddr:<0x%x>\nOrigin-TargetAddr:<0x%x> | TargetAddr:<0x%x> \nOrigin-Inst:<%s> | Inst:<%s> \n",
 		data.InnerSymbol.IO_fd_fdID.SymName,
 		data.InnerSymbol.IO_fd_fdID.SymAddr,
 		data.InnerSymbol.IO_fd_fdID.OriginTargetAddr,
 		data.InnerSymbol.IO_fd_fdID.TargetAddr,
 		x86asm.IntelSyntax(data.InnerSymbol.IO_fd_fdID.OriginInst, 0, nil),
 		x86asm.IntelSyntax(data.InnerSymbol.IO_fd_fdID.Inst, 0, nil))
-	fmt.Printf("\nName %s | CurrentAddr:<0x%x>\nOrigin-TargetAddr:<0x%x> | TargetAddr:<0x%x>\nOrigin-Inst:<%s> | Inst:<%s> \n",
+	klog.Infof("\nName %s | CurrentAddr:<0x%x>\nOrigin-TargetAddr:<0x%x> | TargetAddr:<0x%x>\nOrigin-Inst:<%s> | Inst:<%s> \n",
 		data.InnerSymbol.NET_Send.SymName,
 		data.InnerSymbol.NET_Send.SymAddr,
 		data.InnerSymbol.NET_Send.OriginTargetAddr,
 		data.InnerSymbol.NET_Send.TargetAddr,
 		x86asm.IntelSyntax(data.InnerSymbol.NET_Send.OriginInst, 0, nil),
 		x86asm.IntelSyntax(data.InnerSymbol.NET_Send.Inst, 0, nil))
-	fmt.Println("========")
+	klog.Infoln("========")
 }
 
 func (j *JvmInjector) jvmInjectLib() int {
@@ -682,7 +696,7 @@ func (j *JvmInjector) jvmInjectLib() int {
 	rootfs := C.CString(j.Rootfs)
 	defer C.free(unsafe.Pointer(dll))
 	result := C.cw_inject_library(C.int(j.Pid), C.int(1), dll, rootfs)
-	fmt.Printf("Result: %d\n", result)
+	klog.Infof("Result: %d\n", result)
 	return int(result)
 }
 
@@ -710,59 +724,214 @@ func writeData(pid int, addr uintptr, data uint64) error {
 	return nil
 }
 
-func modifyIoFdTargetAddr(pid int, insertAddr, distAddr uintptr) error {
-	newOffset := distAddr - (insertAddr + 7)
-	targetAddr := insertAddr + 3
-	// 获取目标地址处的数据
-	originalData, err := readData(pid, targetAddr)
+func readDataBytes(pid int, addr uintptr, size int) ([]byte, error) {
+	data := make([]byte, size)
+	if _, err := syscall.PtracePeekData(pid, addr, data); err != nil {
+		return nil, fmt.Errorf("ptrace PEEKDATA: %v", err)
+	}
+	return data, nil
+}
+
+func writeDataBytes(pid int, addr uintptr, data []byte) error {
+	if _, err := syscall.PtracePokeData(pid, addr, data); err != nil {
+		return fmt.Errorf("ptrace POKEDATA: %v", err)
+	}
+	return nil
+}
+
+func modifyIoFdTargetAddr(pid int, insertAddr, distAddr, getTTLFunctionAddr uintptr) error {
+	// newOffset := distAddr - (insertAddr + 7)
+	// targetAddr := insertAddr + 3
+	// // 获取目标地址处的数据
+	// originalData, err := readData(pid, targetAddr)
+	// if err != nil {
+	// 	return err
+	// }
+
+	// // 更新数据中的目标偏移
+	// updatedData := (originalData & 0xFFFFFFFF00000000) | uint64(newOffset&0xFFFFFFFF)
+	// err = writeData(pid, targetAddr, updatedData)
+	// if err != nil {
+	// 	return err
+	// }
+
+	getTTLOffset := getTTLFunctionAddr - insertAddr - 5
+
+	// 读取原始数据
+	// alignedAddr := insertAddr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
+	originalData, err := readDataBytes(pid, insertAddr, 7)
+	if err != nil {
+		return err
+	}
+
+	// offset := insertAddr % uintptr(unsafe.Sizeof(uintptr(0)))
+	offset := 0
+
+	// 写入AMD64的绝对跳转指令: mov rax, addr; jmp rax
+	var getTTLOffset32 uint32 = uint32(getTTLOffset)
+	originalData[offset] = 0xE8 // call
+	originalData[offset+1] = byte(getTTLOffset32)
+	originalData[offset+2] = byte(getTTLOffset32 >> 8)
+	originalData[offset+3] = byte(getTTLOffset32 >> 16)
+	originalData[offset+4] = byte(getTTLOffset32 >> 24)
+	originalData[offset+5] = 0x90 //nop
+	originalData[offset+6] = 0x90 //nop
+
+	err = writeDataBytes(pid, insertAddr, originalData)
 	if err != nil {
 		return err
 	}
 
-	// 更新数据中的目标偏移
-	updatedData := (originalData & 0xFFFFFFFF00000000) | uint64(newOffset&0xFFFFFFFF)
-	err = writeData(pid, targetAddr, updatedData)
+	//以上是先跳转到2GB内存的无用函数中
+	//以下来写真正的跳转函数
+
+	TTLOriginalData, err := readDataBytes(pid, getTTLFunctionAddr, 16)
+	if err != nil {
+		return err
+	}
+
+	TTLOriginalData[offset] = 0x50
+	TTLOriginalData[offset+1] = 0x48
+	TTLOriginalData[offset+2] = 0xb8
+	TTLOriginalData[offset+3] = byte(distAddr)
+	TTLOriginalData[offset+4] = byte(distAddr >> 8)
+	TTLOriginalData[offset+5] = byte(distAddr >> 16)
+	TTLOriginalData[offset+6] = byte(distAddr >> 24)
+	TTLOriginalData[offset+7] = byte(distAddr >> 32)
+	TTLOriginalData[offset+8] = byte(distAddr >> 40)
+	TTLOriginalData[offset+9] = byte(distAddr >> 48)
+	TTLOriginalData[offset+10] = byte(distAddr >> 56)
+	TTLOriginalData[offset+11] = 0x48
+	TTLOriginalData[offset+12] = 0x8b
+	TTLOriginalData[offset+13] = 0x10
+	TTLOriginalData[offset+14] = 0x58 //pop rax
+	TTLOriginalData[offset+15] = 0xc3 //ret
+
+	err = writeDataBytes(pid, getTTLFunctionAddr, TTLOriginalData)
 	if err != nil {
 		return err
 	}
+
 	return nil
 }
 
-func modifyNetSetTargetAddr(pid int, sendDebugAddr, sendReleaseAddr uintptr) error {
-	sendOffset := sendReleaseAddr - sendDebugAddr - 5
+func modifyNetSetTargetAddr(pid int, sendDebugAddr, sendReleaseAddr, convert0FunctionAddr uintptr) error {
+	// sendOffset := sendReleaseAddr - sendDebugAddr - 5
+
+	// // 读取原始数据
+	// alignedAddr := sendDebugAddr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
+	// originalData, err := readData(pid, alignedAddr)
+	// if err != nil {
+	// 	return err
+	// }
+
+	// bytes := (*[8]byte)(unsafe.Pointer(&originalData))
+	// offsetLocation := (sendDebugAddr % uintptr(unsafe.Sizeof(uintptr(0)))) + 1
+	// *(*uint32)(unsafe.Pointer(&bytes[offsetLocation])) = uint32(sendOffset)
+
+	// err = writeData(pid, alignedAddr, originalData)
+	// if err != nil {
+	// 	return err
+	// }
+
+	convert0Offset := convert0FunctionAddr - sendDebugAddr - 5
 
 	// 读取原始数据
-	alignedAddr := sendDebugAddr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
-	originalData, err := readData(pid, alignedAddr)
+	// alignedAddr := insertAddr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
+	originalData, err := readDataBytes(pid, sendDebugAddr, 5)
+	if err != nil {
+		return err
+	}
+
+	// offset := insertAddr % uintptr(unsafe.Sizeof(uintptr(0)))
+	offset := 0
+
+	// 写入AMD64的绝对跳转指令: mov rax, addr; jmp rax
+	var convert0Offset32 uint32 = uint32(convert0Offset)
+	originalData[offset] = 0xE8 // call
+	originalData[offset+1] = byte(convert0Offset32)
+	originalData[offset+2] = byte(convert0Offset32 >> 8)
+	originalData[offset+3] = byte(convert0Offset32 >> 16)
+	originalData[offset+4] = byte(convert0Offset32 >> 24)
+
+	err = writeDataBytes(pid, sendDebugAddr, originalData)
 	if err != nil {
 		return err
 	}
 
-	bytes := (*[8]byte)(unsafe.Pointer(&originalData))
-	offsetLocation := (sendDebugAddr % uintptr(unsafe.Sizeof(uintptr(0)))) + 1
-	*(*uint32)(unsafe.Pointer(&bytes[offsetLocation])) = uint32(sendOffset)
+	convert0OriginalData, err := readDataBytes(pid, convert0FunctionAddr, 13)
+	if err != nil {
+		return err
+	}
 
-	err = writeData(pid, alignedAddr, originalData)
+	convert0OriginalData[offset] = 0x48
+	convert0OriginalData[offset+1] = 0xb8
+	convert0OriginalData[offset+2] = byte(sendReleaseAddr)
+	convert0OriginalData[offset+3] = byte(sendReleaseAddr >> 8)
+	convert0OriginalData[offset+4] = byte(sendReleaseAddr >> 16)
+	convert0OriginalData[offset+5] = byte(sendReleaseAddr >> 24)
+	convert0OriginalData[offset+6] = byte(sendReleaseAddr >> 32)
+	convert0OriginalData[offset+7] = byte(sendReleaseAddr >> 40)
+	convert0OriginalData[offset+8] = byte(sendReleaseAddr >> 48)
+	convert0OriginalData[offset+9] = byte(sendReleaseAddr >> 56)
+	convert0OriginalData[offset+10] = 0xff
+	convert0OriginalData[offset+11] = 0xd0
+	convert0OriginalData[offset+12] = 0xc3
+
+	err = writeDataBytes(pid, convert0FunctionAddr, convert0OriginalData)
 	if err != nil {
 		return err
 	}
+
 	return nil
 }
 
-func modifyReleaseFuncEnter(pid int, originEnterAddr, debugEnterAddr uintptr) error {
-	offset := debugEnterAddr - (originEnterAddr + 5)
+// func modifyReleaseFuncEnter(pid int, originEnterAddr, debugEnterAddr uintptr) error {
+// 	offset := debugEnterAddr - (originEnterAddr + 5)
+
+// 	// 读取原始数据
+// 	alignedAddr := originEnterAddr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
+// 	originalData, err := readData(pid, alignedAddr)
+// 	if err != nil {
+// 		return err
+// 	}
+
+// 	bytes := (*[8]byte)(unsafe.Pointer(&originalData))
+// 	bytes[originEnterAddr%uintptr(unsafe.Sizeof(uintptr(0)))] = 0xe9
+// 	*(*uint32)(unsafe.Pointer(&bytes[(originEnterAddr%uintptr(unsafe.Sizeof(uintptr(0))))+1])) = uint32(offset)
+// 	err = writeData(pid, alignedAddr, originalData)
+// 	if err != nil {
+// 		return err
+// 	}
+// 	return nil
+// }
 
-	// 读取原始数据
+func modifyReleaseFuncEnter(pid int, originEnterAddr, debugEnterAddr uintptr) error {
+	// 读取原始数据 - 需要12字节来存储完整的跳转指令
 	alignedAddr := originEnterAddr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
-	originalData, err := readData(pid, alignedAddr)
+	originalData, err := readDataBytes(pid, alignedAddr, 12)
 	if err != nil {
 		return err
 	}
 
-	bytes := (*[8]byte)(unsafe.Pointer(&originalData))
-	bytes[originEnterAddr%uintptr(unsafe.Sizeof(uintptr(0)))] = 0xe9
-	*(*uint32)(unsafe.Pointer(&bytes[(originEnterAddr%uintptr(unsafe.Sizeof(uintptr(0))))+1])) = uint32(offset)
-	err = writeData(pid, alignedAddr, originalData)
+	offset := originEnterAddr % uintptr(unsafe.Sizeof(uintptr(0)))
+
+	// 写入AMD64的绝对跳转指令: mov rax, addr; jmp rax
+	originalData[offset] = 0x48   // REX.W prefix
+	originalData[offset+1] = 0xb8 // mov rax, imm64
+	// 按小端序写入64位地址
+	originalData[offset+2] = byte(debugEnterAddr)
+	originalData[offset+3] = byte(debugEnterAddr >> 8)
+	originalData[offset+4] = byte(debugEnterAddr >> 16)
+	originalData[offset+5] = byte(debugEnterAddr >> 24)
+	originalData[offset+6] = byte(debugEnterAddr >> 32)
+	originalData[offset+7] = byte(debugEnterAddr >> 40)
+	originalData[offset+8] = byte(debugEnterAddr >> 48)
+	originalData[offset+9] = byte(debugEnterAddr >> 56)
+	originalData[offset+10] = 0xff // jmp rax
+	originalData[offset+11] = 0xe0
+
+	err = writeDataBytes(pid, alignedAddr, originalData)
 	if err != nil {
 		return err
 	}
@@ -770,18 +939,26 @@ func modifyReleaseFuncEnter(pid int, originEnterAddr, debugEnterAddr uintptr) er
 }
 
 func restoreOriginalInstructions(pid int, addr uintptr, instructions []byte) error {
-	alignedAddr := addr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
-	originalData, err := readData(pid, alignedAddr)
+	// alignedAddr := addr & ^(uintptr(unsafe.Sizeof(uintptr(0))) - 1)
+	// originalData, err := readData(pid, alignedAddr)
+	originalData, err := readDataBytes(pid, addr, len(instructions))
 	if err != nil {
 		return err
 	}
 
-	bytes := (*[8]byte)(unsafe.Pointer(&originalData))
+	// bytes := (*[8]byte)(unsafe.Pointer(&originalData))
+	// for i := 0; i < len(instructions); i++ {
+		// bytes[addr%uintptr(unsafe.Sizeof(uintptr(0)))+uintptr(i)] = instructions[i]
+	// }
+
+	// offset := addr % uintptr(unsafe.Sizeof(uintptr(0)))
+
 	for i := 0; i < len(instructions); i++ {
-		bytes[addr%uintptr(unsafe.Sizeof(uintptr(0)))+uintptr(i)] = instructions[i]
+		originalData[i] = instructions[i]
 	}
 
-	err = writeData(pid, alignedAddr, originalData)
+	// err = writeData(pid, alignedAddr, originalData)
+	err = writeDataBytes(pid, addr, originalData)
 	if err != nil {
 		return err
 	}
@@ -846,7 +1023,7 @@ func JvmInject(jvmInjector *JvmInjector) error {
 		return err
 	}
 	printCodeData(jvmInjector.ReleaseLibNetInfo)
-	_type, _, err := jvmInjector.findLibBaseByPathFromProcMaps(jvmInjector.DebugLibNetInfo.ProcLoadPath)
+	_type, _, err := FindLibBaseByPathFromProcMaps(pid, jvmInjector.DebugLibNetInfo.ProcLoadPath)
 	if err != nil {
 		// load so
 		if _type == 1 {
@@ -861,6 +1038,7 @@ func JvmInject(jvmInjector *JvmInjector) error {
 			}
 		}
 	} else {
+		klog.Infoln("[inject] so already loaded.")
 		jvmInjector.PreCheck.LoadingCheck = true
 	}
 
@@ -876,11 +1054,19 @@ func JvmInject(jvmInjector *JvmInjector) error {
 	}
 
 	if !jvmInjector.validateAllPreCheck() {
-		klog.Infof("[inject] validate all pre check")
+		klog.Errorf("[inject] validateAllPreCheck failed: "+
+			"NeedInjectionCheck=%v, LoadingCheck=%v, IoFdCheck=%v, NetSendFuncCheck=%v",
+			jvmInjector.PreCheck.NeedInjectionCheck,
+			jvmInjector.PreCheck.LoadingCheck,
+			jvmInjector.PreCheck.IoFdCheck,
+			jvmInjector.PreCheck.NetSendFuncCheck,
+		)
 		return err
 	}
 	// 修改
 	debugFuncEnterAddr := uintptr(jvmInjector.DebugLibNetInfo.FuncSymbol.SymAddr)
+	debugFuncGetTTLEnterAddr := uintptr(jvmInjector.DebugLibNetInfo.FuncGetTTLSymbol.SymAddr)
+	debugFuncConvert0EnterAddr := uintptr(jvmInjector.DebugLibNetInfo.FuncConvert0Symbol.SymAddr)
 	debugIoFdAddr := uintptr(jvmInjector.DebugLibNetInfo.InnerSymbol.IO_fd_fdID.SymAddr)
 	debugNetSendAddr := uintptr(jvmInjector.DebugLibNetInfo.InnerSymbol.NET_Send.SymAddr)
 
@@ -888,36 +1074,40 @@ func JvmInject(jvmInjector *JvmInjector) error {
 	ioFdReleaseTargetAddr := uintptr(jvmInjector.ReleaseLibNetInfo.InnerSymbol.IO_fd_fdID.TargetAddr)
 	netSendReleaseTargetAddr := uintptr(jvmInjector.ReleaseLibNetInfo.InnerSymbol.NET_Send.TargetAddr)
 
-	fmt.Printf("<0x%x> -> <0x%x>\n", originFuncEnterAddr, debugFuncEnterAddr)
-	fmt.Printf("<0x%x> -> <0x%x>\n", debugIoFdAddr, ioFdReleaseTargetAddr)
-	fmt.Printf("<0x%x> -> <0x%x>\n", debugNetSendAddr, netSendReleaseTargetAddr)
+	klog.Infof("<0x%x> -> <0x%x>\n", originFuncEnterAddr, debugFuncEnterAddr)
+	klog.Infof("<0x%x> -> <0x%x>\n", debugIoFdAddr, ioFdReleaseTargetAddr)
+	klog.Infof("<0x%x> -> <0x%x>\n", debugNetSendAddr, netSendReleaseTargetAddr)
+	klog.Infof("conver0 -> <0x%x>\n", debugFuncConvert0EnterAddr)
+	klog.Infof("getttl -> <0x%x>\n", debugFuncGetTTLEnterAddr)
 
 	// 附加到目标进程
 	klog.Infof("attach")
 	err = syscall.PtraceAttach(pid)
 	if err != nil {
-		fmt.Printf("ptrace ATTACH: %v", err)
+		klog.Errorf("ptrace ATTACH: %v", err)
 	}
 
 	// 等待目标进程停止
 	klog.Infof("attach Wait")
 	if _, err := syscall.Wait4(pid, nil, 0, nil); err != nil {
-		fmt.Printf("wait4: %v", err)
+		klog.Errorf("wait4: %v", err)
 		return err
 	}
 	//time.Now().UnixNano()
 	// 修改目标的内存
 	klog.Infof("modifyIoFdTargetAddr")
-	err = modifyIoFdTargetAddr(pid, debugIoFdAddr, ioFdReleaseTargetAddr)
+	err = modifyIoFdTargetAddr(pid, debugIoFdAddr, ioFdReleaseTargetAddr, debugFuncGetTTLEnterAddr)
 	if err != nil {
-		fmt.Println(err)
+		klog.Error(err)
+		PtraceDetach(pid)
 		return err
 	}
 
 	klog.Infof("modifyNetSetTargetAddr")
-	err = modifyNetSetTargetAddr(pid, debugNetSendAddr, netSendReleaseTargetAddr)
+	err = modifyNetSetTargetAddr(pid, debugNetSendAddr, netSendReleaseTargetAddr, debugFuncConvert0EnterAddr)
 	if err != nil {
-		fmt.Println(err)
+		klog.Error(err)
+		PtraceDetach(pid)
 		return err
 	}
 	// 二次效验 读取并验证地址
@@ -927,74 +1117,55 @@ func JvmInject(jvmInjector *JvmInjector) error {
 	printCodeData(jvmInjector.DebugLibNetInfo)
 	// 效验目标函数内地址是否与预期一致
 	if !jvmInjector.validateAllModifyCheck() && err == nil {
-		klog.Errorf("[inject] failed validateAllModifyCheck")
+		klog.WithError(err).Errorf("[inject] failed validateAllModifyCheck")
+		PtraceDetach(pid)
 		return err
 	}
 	// 更新函数入口
 	klog.Infof("modifyReleaseFuncEnter")
+	// 计算地址差
+	diff := originFuncEnterAddr - debugFuncEnterAddr
+	if diff < 0 {
+		diff = -diff
+	}
+	// 检查是否超过 2GB
+	if diff > (1 << 31) {
+		klog.Infof("[inject] originFuncEnterAddr(0x%x) and debugFuncEnterAddr(0x%x) distance > 2GB",
+			originFuncEnterAddr, debugFuncEnterAddr)
+	}
 	err = modifyReleaseFuncEnter(pid, originFuncEnterAddr, debugFuncEnterAddr)
 	if err != nil {
-		klog.Errorf("[inject] failed modifyReleaseFuncEnter")
+		klog.WithError(err).Errorf("[inject] failed modifyReleaseFuncEnter")
+		PtraceDetach(pid)
 		return err
 	}
-	// 校验jmp地址修改正确
+	// 校验jmp地址修改正确 临时注释
 	klog.Infof("checkReleaseFuncSymAfterChange")
-	err = jvmInjector.checkReleaseFuncSymAfterChange()
-	if err != nil {
-		klog.Errorf("[inject] failed checkReleaseFuncSymAfterChange")
-		if len(jvmInjector.ReleaseLibNetInfo.FuncSymbol.OriginCode) == 5 {
+	errReleaseFuncSymAfterChange := jvmInjector.checkReleaseFuncSymAfterChange()
+	if errReleaseFuncSymAfterChange != nil {
+		klog.WithError(errReleaseFuncSymAfterChange).Errorf("[inject] failed checkReleaseFuncSymAfterChange")
+		// 回滚
+		if len(jvmInjector.ReleaseLibNetInfo.FuncSymbol.OriginCode) == ORIGIN_CODE_LEN {
 			err = restoreOriginalInstructions(pid, originFuncEnterAddr, jvmInjector.ReleaseLibNetInfo.FuncSymbol.OriginCode)
 			if err != nil {
-				fmt.Println(err)
+				klog.WithError(err).Errorf("[inject] failed restoreOriginalInstructions")
+				PtraceDetach(pid)
 				return err
 			}
 		}
+		//PtraceDetach(pid)
+		//return errReleaseFuncSymAfterChange
 	}
 
+	return PtraceDetach(pid)
+}
+
+func PtraceDetach(pid int) error {
 	// 恢复执行
 	klog.Infof("Detach")
-	if err = syscall.PtraceDetach(pid); err != nil {
+	if err := syscall.PtraceDetach(pid); err != nil {
 		klog.Errorf("ptrace DETACH: %v", err)
 		return err
 	}
 	return nil
 }
-
-func copyFileAndMatchPermissions(srcFile, destFile, permFile string) error {
-	// 获取权限文件的信息
-	permInfo, err := os.Stat(permFile)
-	if err != nil {
-		return fmt.Errorf("failed to stat permission file: %w", err)
-	}
-
-	// 打开源文件
-	src, err := os.Open(srcFile)
-	if err != nil {
-		return fmt.Errorf("failed to open source file: %w", err)
-	}
-	defer src.Close()
-
-	// 创建目标文件
-	dst, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE, permInfo.Mode())
-	if err != nil {
-		return fmt.Errorf("failed to create destination file: %w", err)
-	}
-	defer dst.Close()
-	// 复制文件内容
-	if _, err := io.Copy(dst, src); err != nil {
-		return fmt.Errorf("failed to copy file content: %w", err)
-	}
-
-	// 获取用户和组信息并设置
-	if stat, ok := permInfo.Sys().(*syscall.Stat_t); ok {
-		fmt.Println(stat.Uid)
-		fmt.Println(stat.Gid)
-		if err := dst.Chown(int(stat.Uid), int(stat.Gid)); err != nil {
-			return fmt.Errorf("failed to set file ownership: %w", err)
-		}
-	} else {
-		return fmt.Errorf("failed to retrieve ownership information")
-	}
-
-	return nil
-}

文件差异内容过多而无法显示
+ 559 - 240
ebpftracer/tracer/inject/inject_linux_arm64.go


二进制
ebpftracer/tracer/inject/lib/libhotpatch_arm64.a


+ 4 - 3
ebpftracer/tracer/offset.go

@@ -12,7 +12,6 @@ import (
 	klog "github.com/sirupsen/logrus"
 	"io"
 	"net"
-	"os"
 	"strings"
 )
 
@@ -170,12 +169,13 @@ func GetOffset(id ID, path string) (uint64, bool) {
 
 	elfF, err := elf.Open(path)
 	if err != nil {
-		os.Exit(1)
+		klog.Error(err)
+		return 0, false
 	}
 	defer elfF.Close()
 
 	data, err := elfF.DWARF()
-	fmt.Println(err)
+	//fmt.Println(err)
 	r := data.Reader()
 	if !gotoEntry(r, dwarf.TagStructType, strct) {
 		return 0, false
@@ -183,6 +183,7 @@ func GetOffset(id ID, path string) (uint64, bool) {
 
 	e, err := findEntry(r, dwarf.TagMember, id.Field)
 	if err != nil {
+		klog.Error(err)
 		return 0, false
 	}
 

+ 1 - 0
external/hotpatch

@@ -0,0 +1 @@
+Subproject commit e9a2e51f5b147e09820423ae53459266c636789e

+ 55 - 68
flags/flags.go

@@ -1,26 +1,26 @@
 package flags
 
 import (
-	"encoding/json"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/utils"
-	"github.com/coroot/coroot-node-agent/utils/modelse"
-	"github.com/jedib0t/go-pretty/v6/table"
-	"gopkg.in/alecthomas/kingpin.v2"
-	"gopkg.in/ini.v1"
 	"net/url"
 	"os"
-	"path"
 	"strings"
+
+	"github.com/common-nighthawk/go-figure"
+	"gopkg.in/alecthomas/kingpin.v2"
+	"gopkg.in/ini.v1"
 )
 
 var (
 	// apm
-	ConfigServer        = kingpin.Flag("config-server", "The URL of the endpoint to send traces to").Envar("CONFIG_SERVER").Default("http://10.0.16.250:18080").String()
-	DataServer          = kingpin.Flag("data-server", "The URL of the endpoint to send traces to").Envar("DATA_SERVER").Default("http://10.0.16.250:18080").String()
-	DumpApps            = kingpin.Flag("dump", "Dump app snap").Default("false").Bool()
+	ConfigServer        = kingpin.Flag("config-server", "The URL of the endpoint to send traces to").Envar("CONFIG_SERVER").Default("http://10.0.12.192:18080").String()
+	DataServer          = kingpin.Flag("data-server", "The URL of the endpoint to send traces to").Envar("DATA_SERVER").Default("http://10.0.12.192:18080").String()
+	DumpApps            = kingpin.Flag("dump", "Dump app snap").Short('d').Default("false").Bool()
+	DumpRules           = kingpin.Flag("dr", "Dump rule snap").Default("false").Bool()
+	PrintFormat         = kingpin.Flag("output", "Output format (table|json)").Short('o').Default("table").String()
 	Version             = kingpin.Flag("version", "show app version").Short('v').Bool()
 	LogLevel            = kingpin.Flag("log-level", "Log level").Envar("LOG_LEVEL").Default("info").String()
+	ConsoleLog          = kingpin.Flag("console-log", "Console log").Envar("CONSOLE_LOG").Default("false").Bool()
 	EbpfFilePath        = kingpin.Flag("ebpf-path", "Set ebpf file path").Envar("EBPF_FILE").Default("").String()
 	CommonIni           = kingpin.Flag("common.ini", "Set ebpf file path").Envar("COMMON_INI").Default("/opt/cloudwise/omniagent/conf/common.ini").String()
 	ServerPrefix        = kingpin.Flag("server-prefix", "server-prefix").Envar("SERVER_PREFIX").Default("").String()
@@ -32,11 +32,12 @@ var (
 	RunInContainer      = kingpin.Flag("run-in-container", "run in container").Default("false").Envar("RUN_IN_CONTAINER").Bool()
 	RunInOmniagent      = kingpin.Flag("run-in-omniagent", "run in omniagent").Default("false").Envar("RUN_IN_OMNIAGENT").Bool()
 
-	ListenAddress     = kingpin.Flag("listen", "Listen address - ip:port or :port").Default("0.0.0.0:8123").Envar("LISTEN").String()
-	CgroupRoot        = kingpin.Flag("cgroupfs-root", "The mount point of the host cgroupfs root").Default("/sys/fs/cgroup").Envar("CGROUPFS_ROOT").String()
-	DisableLogParsing = kingpin.Flag("disable-log-parsing", "Disable container log parsing").Default("false").Envar("DISABLE_LOG_PARSING").Bool()
-	DisablePinger     = kingpin.Flag("disable-pinger", "Don't ping upstreams").Default("false").Envar("DISABLE_PINGER").Bool()
-	DisableL7Tracing  = kingpin.Flag("disable-l7-tracing", "Disable L7 tracing").Default("false").Envar("DISABLE_L7_TRACING").Bool()
+	ListenAddress                = kingpin.Flag("listen", "Listen address - ip:port or :port").Default("0.0.0.0:8123").Envar("LISTEN").String()
+	CgroupRoot                   = kingpin.Flag("cgroupfs-root", "The mount point of the host cgroupfs root").Default("/sys/fs/cgroup").Envar("CGROUPFS_ROOT").String()
+	DisableLogParsing            = kingpin.Flag("disable-log-parsing", "Disable container log parsing").Default("true").Envar("DISABLE_LOG_PARSING").Bool()
+	DisablePinger                = kingpin.Flag("disable-pinger", "Don't ping upstreams").Default("false").Envar("DISABLE_PINGER").Bool()
+	DisableL7Tracing             = kingpin.Flag("disable-l7-tracing", "Disable L7 tracing").Default("false").Envar("DISABLE_L7_TRACING").Bool()
+	EnableElasticsearchDetection = kingpin.Flag("enable-es", "Enable Elasticsearch detection in HTTP requests").Default("false").Envar("ENABLE_ES").Bool()
 
 	ExternalNetworksWhitelist = kingpin.
 					Flag("track-public-network", "Allow track connections to the specified IP networks, all private networks are allowed by default (e.g., Y.Y.Y.Y/mask)").
@@ -60,12 +61,28 @@ var (
 	LogsEndpoint       = kingpin.Flag("logs-endpoint", "The URL of the endpoint to send logs to").Envar("LOGS_ENDPOINT").URL()
 	ProfilesEndpoint   = kingpin.Flag("profiles-endpoint", "The URL of the endpoint to send profiles to").Envar("PROFILES_ENDPOINT").URL()
 	InsecureSkipVerify = kingpin.Flag("insecure-skip-verify", "whether to skip verifying the certificate or not").Envar("INSECURE_SKIP_VERIFY").Default("false").Bool()
+	TracesCompression  = kingpin.Flag("traces-compression", "Compression algorithm for traces (none|gzip|zstd)").Envar("TRACES_COMPRESSION").Default("zstd").String()
 
 	ScrapeInterval = kingpin.Flag("scrape-interval", "How often to gather metrics from the agent").Default("15s").Envar("SCRAPE_INTERVAL").Duration()
 	WalDir         = kingpin.Flag("wal-dir", "Path to where the agent stores data (e.g. the metrics Write-Ahead Log)").Default("/tmp/coroot-node-agent").Envar("WAL_DIR").String()
 
 	HostDirPathPrefix = kingpin.Flag("host-dir-path-prefix", "Set the prefix of path about the mount point of the host directory").Envar("HOST_DIR_PATH_PREFIX").Default("").String()
 	FuseTryMax        = kingpin.Flag("fuse_try_max", "The maximum number of the fuse operation try").Default("3").Envar("FUSE_TRY_MAX").Int()
+	// debug
+	Test = kingpin.Flag("test", "Only test").Default("false").Envar("TEST").Bool()
+	// op 新增配置
+	SysTag = kingpin.Flag("sys-tag", "sys tag").Envar("CW_SYS").Default("").String()
+	// l7
+	MysqlDefault = kingpin.Flag("mysql-default", "Default MySQL protocol when port-based detection fails").Envar("MYSQL_DEFAULT").Default("mysql").String()
+
+	// 端口白名单配置
+	MysqlPortWhitelist   = kingpin.Flag("mysql-ports", "Comma-separated list of ports to identify as MySQL").Envar("MYSQL_PORTS").Default("").String()
+	MariadbPortWhitelist = kingpin.Flag("mariadb-ports", "Comma-separated list of ports to identify as MariaDB").Envar("MARIADB_PORTS").Default("").String()
+	TidbPortWhitelist    = kingpin.Flag("tidb-ports", "Comma-separated list of ports to identify as TiDB").Envar("TIDB_PORTS").Default("").String()
+	//APP注册到DOOP
+	RegisterAppToDoop = kingpin.Flag("register-app-to-doop", "Register the app-info to the doop").Default("false").Envar("REGISTER_APP_TO_DOOP").Bool()
+	//是否向平台发送网络数据
+	SendNetData = kingpin.Flag("send-net-data", "Send the net data to platform").Default("false").Envar("SEND_NET_DATA").Bool()
 )
 
 var AgentName = "euspace"
@@ -87,11 +104,30 @@ func init() {
 
 	kingpin.HelpFlag.Short('h').Hidden()
 	kingpin.Parse()
+	if *Test {
+		euspace := figure.NewFigure("TEST", "", true)
+		// 只在用户未显式设置时才应用 test 模式的默认值
+		if os.Getenv("DISABLE_REG_HOST") == "" {
+			*DisableRegisterHost = true
+		}
+		if os.Getenv("DISABLE_E2E_TRACING") == "" {
+			*DisableE2ETracing = false
+		}
+		*ConsoleLog = true
+		*LogLevel = "debug"
+		euspace.Print()
+	} else {
+		euspace := figure.NewColorFigure("Euspace", "slant", "blue", true)
+		euspace.Print()
+	}
 	if *Version {
 		ShowVersion()
 	}
 	if *DumpApps {
-		DumpTableFeatures()
+		DumpTableFeatures(*PrintFormat)
+	}
+	if *DumpRules {
+		DumpRuleTableFeatures(*PrintFormat)
 	}
 	if *CollectorEndpoint != nil {
 		u := *CollectorEndpoint
@@ -116,7 +152,7 @@ func init() {
 	// set ServerPrefix
 	// set ConfigServer
 	// set DataServer
-	if *CommonIni != "" {
+	if *RunInOmniagent {
 		iniData, err := ini.Load(*CommonIni)
 		if err == nil && iniData != nil {
 			*ServerPrefix = "/apm"
@@ -159,58 +195,9 @@ func init() {
 	} else {
 		*HostDirPathPrefix = ""
 	}
-}
-
-func DumpTableFeatures() {
-	dumpPath := path.Join(utils.GetDefaultRuntimePath(), "memdump")
-	fileName := path.Join(dumpPath, "app.snap")
-	content, err := os.ReadFile(fileName)
-	if err != nil {
-		fmt.Println(err.Error())
-	}
 
-	s := make(map[uint32]modelse.AppStatusInfo)
-	err = json.Unmarshal(content, &s)
-	if err != nil {
-		fmt.Println(err.Error())
-	}
-	t := table.NewWriter()
-	for pid, info := range s {
-		service := fmt.Sprintf("%s", info.Sn)
-		t.AppendRow(table.Row{
-			//info.AgentID,
-			//info.UpdateAt,
-			pid,
-			info.ProcName,
-			info.AppName,
-			info.Rule,
-			info.Language,
-			service,
-			info.AppID,
-			info.RegisterAt,
-			info.PreStatus.String(),
-			info.Status.String(),
-			info.StackStatus,
-		})
-	}
-	t.SetAutoIndex(true)
-	t.AppendHeader(table.Row{
-		//"agent id",
-		//"update at",
-		"pid",
-		"process",
-		"app name",
-		"app rule",
-		"code",
-		"service",
-		"app id",
-		"reg at",
-		"app pre status",
-		"app status",
-		"stack status",
-	})
-	fmt.Println(t.Render())
-	os.Exit(0)
+	// 初始化端口白名单
+	InitPortWhitelists()
 }
 
 func ShowVersion() {

+ 78 - 0
flags/port_whitelist.go

@@ -0,0 +1,78 @@
+package flags
+
+import (
+	"strconv"
+	"strings"
+
+	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
+)
+
+// 端口白名单数据结构
+type PortWhitelist struct {
+	Ports map[uint16]bool
+}
+
+// 全局端口白名单实例
+var (
+	MysqlPorts   *PortWhitelist
+	MariadbPorts *PortWhitelist
+	TidbPorts    *PortWhitelist
+)
+
+// 创建端口白名单
+func NewPortWhitelist(portList string) *PortWhitelist {
+	whitelist := &PortWhitelist{
+		Ports: make(map[uint16]bool),
+	}
+
+	if portList == "" {
+		return whitelist
+	}
+
+	ports := strings.Split(portList, ",")
+	for _, portStr := range ports {
+		portStr = strings.TrimSpace(portStr)
+		if port, err := strconv.ParseUint(portStr, 10, 16); err == nil {
+			whitelist.Ports[uint16(port)] = true
+		}
+	}
+
+	return whitelist
+}
+
+// 检查端口是否在白名单中
+func (pw *PortWhitelist) Contains(port uint16) bool {
+	if pw == nil {
+		return false
+	}
+	return pw.Ports[port]
+}
+
+// 获取协议类型(基于端口白名单)
+func GetProtocolByPort(port uint16) l7.Protocol {
+	if MysqlPorts != nil && MysqlPorts.Contains(port) {
+		return l7.ProtocolMysql
+	}
+	if MariadbPorts != nil && MariadbPorts.Contains(port) {
+		return l7.ProtocolMariaDB
+	}
+	if TidbPorts != nil && TidbPorts.Contains(port) {
+		return l7.ProtocolTiDB
+	}
+	// 如果端口不在白名单中,返回默认协议
+	if *MysqlDefault == "mariadb" {
+		return l7.ProtocolMariaDB
+	}
+	if *MysqlDefault == "tidb" {
+		return l7.ProtocolTiDB
+	}
+
+	return l7.ProtocolMysql
+}
+
+// 初始化端口白名单
+func InitPortWhitelists() {
+	MysqlPorts = NewPortWhitelist(*MysqlPortWhitelist)
+	MariadbPorts = NewPortWhitelist(*MariadbPortWhitelist)
+	TidbPorts = NewPortWhitelist(*TidbPortWhitelist)
+}

部分文件因为文件数量过多而无法显示