Explorar el Código

Feature #TASK_QT-31635【国网辽宁电力】go端到端重构

Carl hace 6 meses
padre
commit
b8a1247bb8

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

@@ -208,6 +208,9 @@ struct ebpf_proc_info {
 	__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;
 } __attribute__((packed));
 
 enum {

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

@@ -76,7 +76,7 @@ int is_mysql_response(char *buf, __u64 buf_size, __u8 request_type, __u32 *state
 			error_message[error_message_len] = '\0'; // 确保字符串结束
 		}
 		// 输出错误信息到内核日志
-		bpf_printk("MySQL Error %d: %s", error_code, error_message);
+		cw_bpf_debug("MySQL Error %d: %s", error_code, error_message);
 		*status = STATUS_FAILED;
 		return 1;
 	}

+ 112 - 346
ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c

@@ -48,6 +48,27 @@ 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 {
+	__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_PERF_EVENT_ARRAY);
 //} events SEC(".maps");
@@ -63,345 +84,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) {
-	bpf_printk("11111111111111");
-    // Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go
-    u64 curr_keyvalue_count = 0;
-    long res = bpf_probe_read_user(&curr_keyvalue_count, sizeof(curr_keyvalue_count), headers_ptr);
-
-    if (res < 0) {
-        cw_bpf_debug("Couldn't read map key-value count from user");
-        return -1;
-    }
-
-    if (curr_keyvalue_count >= 8) {
-        cw_bpf_debug("Map size is bigger than 8, skipping context propagation");
-        return -1;
-    }
-
-    // Get pointer to temp bucket struct we store in a map (avoiding large local variable on the stack)
-    // Performing read-modify-write on the bucket
-    u32 map_id = 1;
-    struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
-    if (!bucket_map_value) {
-        return -1;
-    }
-	__builtin_memset(bucket_map_value, 0, sizeof(struct map_bucket));
-
-    __u64 pid_tgid = bpf_get_current_pid_tgid();
-	__u32 tgid = pid_tgid >> 32;
-	struct ebpf_proc_info *proc_info =
-			bpf_map_lookup_elem(&proc_info_map, &tgid);
-    if(!proc_info)
-    {
-        return -1;
-    }
-
-    void *buckets_ptr_ptr = (void*) (headers_ptr + proc_info->buckets_ptr_pos);
-    void *bucket_ptr = 0; // The actual pointer to the buckets
-
-    if (curr_keyvalue_count == 0) {
-	    bpf_printk("curr_keyvalue_count == 0");
-
-        // No key-value pairs in the Go map, need to "allocate" memory for the user
-        bucket_ptr = write_target_data(bucket_map_value, sizeof(struct map_bucket));
-        if (bucket_ptr == NULL) {
-            cw_bpf_debug("inject_header: Failed to write bucket to user");
-            return -1;
-        }
-        // Update the buckets pointer in the hmap struct to point to newly allocated bucket
-        res = bpf_probe_write_user(buckets_ptr_ptr, &bucket_ptr, sizeof(bucket_ptr));
-        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;
-	bpf_printk("bucket_index:%d",bucket_index);
-
-    char traceparent_tophash = 0xee;
-    bucket_map_value->tophash[bucket_index] = traceparent_tophash;
-
-    // Prepare the key string for the user
-    char key[W3C_KEY_LENGTH] = "traceparent";
-    void *ptr = write_target_data(key, W3C_KEY_LENGTH);
-    if (ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write key to user");
-        return -1;
-    }
-    bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = W3C_KEY_LENGTH, .str = ptr};
-
-    // Prepare the value string slice
-    // First the value string which constains the span context
-    char val[W3C_VAL_LENGTH];
-    span_context_to_w3c_string(propagated_ctx, val);
-    ptr = write_target_data(val, sizeof(val));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write value to user");
-        return -1;
-    }
-
-    // The go string pointing to the above val
-    struct go_string_ot header_value = {.len = W3C_VAL_LENGTH, .str = ptr};
-    ptr = write_target_data((void*)&header_value, sizeof(header_value));
-    if(ptr == NULL) {
-        cw_bpf_debug("inject_header: Failed to write go_string to user");
-        return -1;
-    }
-
-    // Last, go_slice pointing to the above go_string
-    bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = ptr, .cap = 1, .len = 1};
-
-    // Update the map header count field
-    curr_keyvalue_count += 1;
-    res = bpf_probe_write_user(headers_ptr, &curr_keyvalue_count, sizeof(curr_keyvalue_count));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update key-value count in map header");
-        return -1;
-    }
-
-    // Update the bucket
-    res = bpf_probe_write_user(bucket_ptr, bucket_map_value, sizeof(struct map_bucket));
-    if (res < 0) {
-        cw_bpf_debug("Failed to update bucket content");
-        return -1;
-    }
-
-    bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
-    return 0;
-}
-// 00:00:1015481350055581:5450531005555981:5610250100539899:304775019cd3218a304775019cd3218a:1001025098564810:140acc88cde8773f
-static __always_inline long cw_inject_header(void* headers_ptr, struct apm_span_context* propagated_ctx) {
-    // Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go
-    u64 curr_keyvalue_count = 0;
-    long res = bpf_probe_read_user(&curr_keyvalue_count, sizeof(curr_keyvalue_count), headers_ptr);
-
-    if (res < 0) {
-        cw_bpf_debug("Couldn't read map key-value count from user");
-        return -1;
-    }
-
-    if (curr_keyvalue_count >= 8) {
-        cw_bpf_debug("Map size is bigger than 8, skipping context propagation");
-        return -1;
-    }
-
-    // Get pointer to temp bucket struct we store in a map (avoiding large local variable on the stack)
-    // Performing read-modify-write on the bucket
-    u32 map_id = 0;
-    struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
-    if (!bucket_map_value) {
-        return -1;
-    }
-
-    __u64 pid_tgid = bpf_get_current_pid_tgid();
-	__u32 tgid = pid_tgid >> 32;
-	struct ebpf_proc_info *proc_info =
-			bpf_map_lookup_elem(&proc_info_map, &tgid);
-    if(!proc_info)
-    {
-        return -1;
-    }
-
-    void *buckets_ptr_ptr = (void*) (headers_ptr + proc_info->buckets_ptr_pos);
-    void *bucket_ptr = 0; // The actual pointer to the buckets
-
-    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) {
@@ -570,7 +253,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)
     {
@@ -682,6 +367,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) {
@@ -690,16 +379,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);
 
@@ -838,7 +528,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) {
@@ -862,4 +553,79 @@ 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;
+				}
+			}
+		}
+	}
+	done:
+	bpf_map_delete_elem(&http_headers, &headers_ptr);
+	return 0;
 }

+ 44 - 29
ebpftracer/tls.go

@@ -23,20 +23,21 @@ import (
 )
 
 const (
-	minSupportedGoVersion = "v1.15.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"
-	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"
+	minSupportedGoVersion          = "v1.15.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"
+	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"
+	goGrpcHttp2ClientNewStream     = "google.golang.org/grpc/internal/transport.(*http2Client).NewStream"
 )
 
 var (
@@ -211,27 +212,27 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 	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
@@ -290,17 +291,19 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 			// go
 			info.BucketsPtrPos = bucketsOff
 			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.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.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.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"),
 			}
 
 			for field, id := range fields {
@@ -348,7 +351,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 		}
 		switch s.Name {
 		case goTlsWriteSymbol, goTlsReadSymbol:
-		case goExecute, goNewproc1, goRunqget, goServeHTTP, goTransport, goGrpcHttp2OperateHeader,goGrpcServerHandleStream,goGrpcServerWritestatus,goGrpcClientConnInvoke,goGrpcClientLoopyHeaderHandler,goGrpcHttp2ClientNewStream:
+		case goExecute, goNewproc1, goRunqget, goServeHTTP, goTransport, goHeaderWriteSubset, goGrpcHttp2OperateHeader, goGrpcServerHandleStream, goGrpcServerWritestatus, goGrpcClientConnInvoke, goGrpcClientLoopyHeaderHandler, goGrpcHttp2ClientNewStream:
 		default:
 			continue
 		}
@@ -590,6 +593,18 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint1
 				links = append(links, l)
 			}
 
+		case goHeaderWriteSubset:
+			if t.DisableE2ETracing() {
+				continue
+			}
+			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_writeSubset"], &link.UprobeOptions{Address: address})
+			if err != nil {
+				klog.WithError(err).Errorln("failed to attach uprobe_writeSubset uprobe")
+				continue
+			}
+			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})

+ 4 - 0
utils/modelse/bpf_struct.go

@@ -209,6 +209,10 @@ type EbpfProcInfo struct {
 	StreamMethodPtrPos  uint64
 	StreamCtxPos        uint64
 	IsNewFramePos       uint8
+
+	// io writer
+	IoWriterBufPtrPos uint64
+	IoWriterNPos      uint64
 }
 
 type allowPortBitmap struct {