|
|
@@ -24,6 +24,15 @@ struct grpc_request_t {
|
|
|
u8 has_status;
|
|
|
};
|
|
|
|
|
|
+// Tail call 上下文结构,用于在多个 eBPF 程序间传递数据
|
|
|
+struct grpc_header_ctx_t {
|
|
|
+ void *frame_ptr;
|
|
|
+ void *header_fields_ptr;
|
|
|
+ s64 header_fields_len;
|
|
|
+ s32 current_index;
|
|
|
+ u32 frame_stream_id_pod_offset;
|
|
|
+};
|
|
|
+
|
|
|
struct {
|
|
|
__uint(type, BPF_MAP_TYPE_HASH);
|
|
|
__type(key, void *);
|
|
|
@@ -53,6 +62,14 @@ struct {
|
|
|
__uint(max_entries, 1);
|
|
|
} operate_header_storage SEC(".maps");
|
|
|
|
|
|
+// Tail call 上下文存储
|
|
|
+struct {
|
|
|
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
|
|
+ __uint(key_size, sizeof(u32));
|
|
|
+ __uint(value_size, sizeof(struct grpc_header_ctx_t));
|
|
|
+ __uint(max_entries, 1);
|
|
|
+} grpc_header_ctx_storage SEC(".maps");
|
|
|
+
|
|
|
struct hpack_header_field {
|
|
|
struct go_string_ot name;
|
|
|
struct go_string_ot value;
|
|
|
@@ -440,52 +457,162 @@ lookup:
|
|
|
// 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) {
|
|
|
void *arg4 = get_argument(ctx, 4);
|
|
|
void *arg2 = get_argument(ctx, 2);
|
|
|
void *frame_ptr = is_new_frame_pos ? arg4 : arg2;
|
|
|
+
|
|
|
+ if (frame_ptr == NULL) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 读取 header fields slice
|
|
|
struct go_slice header_fields = {};
|
|
|
- bpf_probe_read(&header_fields, sizeof(header_fields), (void *)(frame_ptr + frame_fields_pos));
|
|
|
- char key[W3C_KEY_LENGTH] = "traceparent";
|
|
|
+ long res = bpf_probe_read(&header_fields, sizeof(header_fields),
|
|
|
+ (void *)(frame_ptr + frame_fields_pos));
|
|
|
+ if (res != 0 || header_fields.len == 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 准备 tail call 上下文
|
|
|
+ u32 zero = 0;
|
|
|
+ struct grpc_header_ctx_t *ctx_storage =
|
|
|
+ bpf_map_lookup_elem(&grpc_header_ctx_storage, &zero);
|
|
|
+ if (ctx_storage == NULL) {
|
|
|
+ bpf_printk("grpc:server:operateHeader: failed to get ctx storage");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化上下文
|
|
|
+ __builtin_memset(ctx_storage, 0, sizeof(struct grpc_header_ctx_t));
|
|
|
+ ctx_storage->frame_ptr = frame_ptr;
|
|
|
+ ctx_storage->header_fields_ptr = header_fields.ptr;
|
|
|
+ ctx_storage->header_fields_len = header_fields.len;
|
|
|
+ ctx_storage->current_index = 0;
|
|
|
+ ctx_storage->frame_stream_id_pod_offset = frame_stream_id_pod;
|
|
|
+
|
|
|
+ // 调用第二阶段:处理头部
|
|
|
+ bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_GRPC_SERVER_PROCESS_HEADERS_UP_IDX);
|
|
|
+
|
|
|
+ // Tail call 失败时的回退
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+// 第二阶段:处理 HTTP/2 头部,查找 traceparent
|
|
|
+// 这个函数通过 tail call 被调用,可以有独立的指令数预算
|
|
|
+PROGUP(grpc_server_process_headers)(struct pt_regs *ctx) {
|
|
|
+ // 获取上下文
|
|
|
+ u32 zero = 0;
|
|
|
+ struct grpc_header_ctx_t *header_ctx =
|
|
|
+ bpf_map_lookup_elem(&grpc_header_ctx_storage, &zero);
|
|
|
+ if (header_ctx == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
|
|
|
- for (s32 i = 0; i < MAX_HEADERS; i++) {
|
|
|
- if (i >= header_fields.len) {
|
|
|
+ void *frame_ptr = header_ctx->frame_ptr;
|
|
|
+ void *header_fields_ptr = header_ctx->header_fields_ptr;
|
|
|
+ s64 header_fields_len = header_ctx->header_fields_len;
|
|
|
+ s32 start_idx = header_ctx->current_index;
|
|
|
+
|
|
|
+ // 每次处理最多 5 个头部,避免指令数过多
|
|
|
+ #define BATCH_SIZE 5
|
|
|
+ s32 end_idx = start_idx + BATCH_SIZE;
|
|
|
+ if (end_idx > header_fields_len) {
|
|
|
+ end_idx = header_fields_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用 #pragma unroll 减少循环复杂度
|
|
|
+ #pragma unroll
|
|
|
+ for (s32 i = start_idx; i < end_idx; i++) {
|
|
|
+ if (i >= header_fields_len) {
|
|
|
break;
|
|
|
}
|
|
|
+
|
|
|
+ // 读取头部字段
|
|
|
struct hpack_header_field hf = {};
|
|
|
- long res =
|
|
|
- bpf_probe_read(&hf, sizeof(hf), (void *)(header_fields.ptr + (i * sizeof(hf))));
|
|
|
- if (hf.name.len == W3C_KEY_LENGTH && hf.value.len == W3C_VAL_LENGTH) {
|
|
|
- char current_key[W3C_KEY_LENGTH];
|
|
|
- bpf_probe_read(current_key, sizeof(current_key), hf.name.str);
|
|
|
- if (bpf_memcmp(key, current_key, sizeof(key))) {
|
|
|
- char val[W3C_VAL_LENGTH];
|
|
|
- bpf_probe_read(val, W3C_VAL_LENGTH, hf.value.str);
|
|
|
-
|
|
|
- // Get stream id
|
|
|
- void *headers_frame = NULL;
|
|
|
- bpf_probe_read(&headers_frame, sizeof(headers_frame), frame_ptr);
|
|
|
- u32 stream_id = 0;
|
|
|
- bpf_probe_read(
|
|
|
- &stream_id, sizeof(stream_id), (void *)(headers_frame + frame_stream_id_pod));
|
|
|
-
|
|
|
- // 使用 per-cpu array map 存储大变量,避免栈空间超限
|
|
|
- // u32 zero = 0;
|
|
|
- // struct grpc_request_t *grpcReq = bpf_map_lookup_elem(&operate_header_storage, &zero);
|
|
|
- // if (grpcReq == NULL) {
|
|
|
- // bpf_printk("grpc:server:operateHeader: failed to get storage");
|
|
|
- // return -1;
|
|
|
- // }
|
|
|
-
|
|
|
- // 清零并初始化
|
|
|
- // __builtin_memset(grpcReq, 0, sizeof(struct grpc_request_t));
|
|
|
- // w3c_string_to_span_context(val, &grpcReq->psc);
|
|
|
- // bpf_map_update_elem(&streamid_to_grpc_events, &stream_id, grpcReq, 0);
|
|
|
- }
|
|
|
+ long res = bpf_probe_read(&hf, sizeof(hf),
|
|
|
+ (void *)(header_fields_ptr + (i * sizeof(hf))));
|
|
|
+ if (res != 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 快速检查:长度必须匹配
|
|
|
+ if (hf.name.len != W3C_KEY_LENGTH || hf.value.len != W3C_VAL_LENGTH) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分段比较 "traceparent" (11 字节)
|
|
|
+ // 避免使用 bpf_memcmp,使用简单的整数比较
|
|
|
+ u32 part1 = 0, part2 = 0, part3 = 0;
|
|
|
+ bpf_probe_read(&part1, 4, hf.name.str); // "trac" = 0x63617274
|
|
|
+ bpf_probe_read(&part2, 4, hf.name.str + 4); // "epar" = 0x72617065
|
|
|
+ bpf_probe_read(&part3, 3, hf.name.str + 8); // "ent" = 0x746e65
|
|
|
+
|
|
|
+ // 检查是否匹配 "traceparent"
|
|
|
+ if (part1 != 0x63617274 || part2 != 0x72617065 ||
|
|
|
+ (part3 & 0xFFFFFF) != 0x746e65) {
|
|
|
+ continue;
|
|
|
}
|
|
|
+
|
|
|
+ // 找到 traceparent 头部!
|
|
|
+ bpf_printk("grpc:server:operateHeader: found traceparent at index %d", i);
|
|
|
+
|
|
|
+ // 读取 traceparent 值
|
|
|
+ char val[W3C_VAL_LENGTH];
|
|
|
+ res = bpf_probe_read(val, W3C_VAL_LENGTH, hf.value.str);
|
|
|
+ if (res != 0) {
|
|
|
+ bpf_printk("grpc:server:operateHeader: failed to read traceparent value");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取 stream ID
|
|
|
+ void *headers_frame = NULL;
|
|
|
+ res = bpf_probe_read(&headers_frame, sizeof(headers_frame), frame_ptr);
|
|
|
+ if (res != 0) {
|
|
|
+ bpf_printk("grpc:server:operateHeader: failed to read headers_frame");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ u32 stream_id = 0;
|
|
|
+ res = bpf_probe_read(&stream_id, sizeof(stream_id),
|
|
|
+ (void *)(headers_frame + header_ctx->frame_stream_id_pod_offset));
|
|
|
+ if (res != 0) {
|
|
|
+ bpf_printk("grpc:server:operateHeader: failed to read stream_id");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ bpf_printk("grpc:server:operateHeader: stream_id=%u", stream_id);
|
|
|
+
|
|
|
+ // 使用临时存储
|
|
|
+ struct grpc_request_t *grpcReq =
|
|
|
+ bpf_map_lookup_elem(&operate_header_storage, &zero);
|
|
|
+ if (grpcReq == NULL) {
|
|
|
+ bpf_printk("grpc:server:operateHeader: failed to get storage");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清零并初始化
|
|
|
+ __builtin_memset(grpcReq, 0, sizeof(struct grpc_request_t));
|
|
|
+
|
|
|
+ // 解析 W3C trace context (可选,如果需要的话)
|
|
|
+ // w3c_string_to_span_context(val, &grpcReq->psc);
|
|
|
+
|
|
|
+ // 保存到 map,供后续 handleStream 使用
|
|
|
+ bpf_map_update_elem(&streamid_to_grpc_events, &stream_id, grpcReq, 0);
|
|
|
+
|
|
|
+ // 找到就退出
|
|
|
+ return 0;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ // 如果还有更多头部需要处理,继续 tail call
|
|
|
+ if (end_idx < header_fields_len) {
|
|
|
+ header_ctx->current_index = end_idx;
|
|
|
+ bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_GRPC_SERVER_PROCESS_HEADERS_UP_IDX);
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|