|
|
@@ -0,0 +1,442 @@
|
|
|
+// Copyright The OpenTelemetry Authors
|
|
|
+//
|
|
|
+// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+// you may not use this file except in compliance with the License.
|
|
|
+// You may obtain a copy of the License at
|
|
|
+//
|
|
|
+// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+//
|
|
|
+// Unless required by applicable law or agreed to in writing, software
|
|
|
+// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+// See the License for the specific language governing permissions and
|
|
|
+// limitations under the License.
|
|
|
+
|
|
|
+#include "arguments.h"
|
|
|
+#include "span_context.h"
|
|
|
+#include "go_context.h"
|
|
|
+#include "go_types.h"
|
|
|
+#include "uprobe.h"
|
|
|
+
|
|
|
+//char __license[] SEC("license") = "Dual MIT/GPL";
|
|
|
+
|
|
|
+#define PATH_MAX_LEN 128
|
|
|
+#define MAX_BUCKETS 8
|
|
|
+#define METHOD_MAX_LEN 8
|
|
|
+#define MAX_CONCURRENT 50
|
|
|
+#define REMOTE_ADDR_MAX_LEN 256
|
|
|
+#define HOST_MAX_LEN 256
|
|
|
+#define PROTO_MAX_LEN 8
|
|
|
+
|
|
|
+struct http_server_span_t {
|
|
|
+ BASE_SPAN_PROPERTIES
|
|
|
+ u64 status_code;
|
|
|
+ char method[METHOD_MAX_LEN];
|
|
|
+ char path[PATH_MAX_LEN];
|
|
|
+ char remote_addr[REMOTE_ADDR_MAX_LEN];
|
|
|
+ char host[HOST_MAX_LEN];
|
|
|
+ char proto[PROTO_MAX_LEN];
|
|
|
+};
|
|
|
+
|
|
|
+struct uprobe_data_t {
|
|
|
+ struct http_server_span_t span;
|
|
|
+ // bpf2go doesn't support pointers fields
|
|
|
+ // saving the response pointer in the entry probe
|
|
|
+ // and using it in the return probe
|
|
|
+ u64 resp_ptr;
|
|
|
+};
|
|
|
+
|
|
|
+struct {
|
|
|
+ __uint(type, BPF_MAP_TYPE_HASH);
|
|
|
+ __type(key, void *);
|
|
|
+ __type(value, struct uprobe_data_t);
|
|
|
+ __uint(max_entries, MAX_CONCURRENT);
|
|
|
+} http_server_uprobes SEC(".maps");
|
|
|
+
|
|
|
+struct {
|
|
|
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
|
|
+ __uint(key_size, sizeof(u32));
|
|
|
+ __uint(value_size, sizeof(struct map_bucket));
|
|
|
+ __uint(max_entries, 1);
|
|
|
+} golang_mapbucket_storage_map SEC(".maps");
|
|
|
+
|
|
|
+struct {
|
|
|
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
|
|
+ __uint(key_size, sizeof(u32));
|
|
|
+ __uint(value_size, sizeof(struct cw_header_token));
|
|
|
+ __uint(max_entries, 1);
|
|
|
+} 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 uprobe_data_t));
|
|
|
+ __uint(max_entries, 1);
|
|
|
+} http_server_uprobe_storage_map SEC(".maps");
|
|
|
+
|
|
|
+//struct
|
|
|
+//{
|
|
|
+// __uint(type, BPF_MAP_TYPE_LRU_HASH);
|
|
|
+// __uint(key_size, sizeof(struct apm_trace_key_t));
|
|
|
+// __uint(value_size, sizeof(struct span_context));
|
|
|
+// __uint(max_entries, 1);
|
|
|
+//} apm_span_context_map SEC(".maps");
|
|
|
+
|
|
|
+struct {
|
|
|
+ __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
|
|
+} events SEC(".maps");
|
|
|
+
|
|
|
+// Injected in init
|
|
|
+volatile const u64 method_ptr_pos;
|
|
|
+volatile const u64 url_ptr_pos;
|
|
|
+volatile const u64 path_ptr_pos;
|
|
|
+volatile const u64 ctx_ptr_pos;
|
|
|
+volatile const u64 headers_ptr_pos;
|
|
|
+volatile const u64 buckets_ptr_pos;
|
|
|
+volatile const u64 req_ptr_pos;
|
|
|
+volatile const u64 status_code_pos;
|
|
|
+volatile const u64 remote_addr_pos;
|
|
|
+volatile const u64 host_pos;
|
|
|
+volatile const u64 proto_pos;
|
|
|
+//for (int i = 0; i < W3C_KEY_LENGTH; i++)
|
|
|
+//{
|
|
|
+//if (current_header_key[i] != "traceparent"[i]){
|
|
|
+//goto outer_loop;
|
|
|
+//}
|
|
|
+//if (current_header_key[i] != "Traceparent"[i]){
|
|
|
+//goto outer_loop;
|
|
|
+//}
|
|
|
+//}
|
|
|
+
|
|
|
+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;
|
|
|
+ void *header_buckets;
|
|
|
+ res = bpf_probe_read(&header_buckets, sizeof(header_buckets), (void *) (headers_ptr + 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++) {
|
|
|
+ if (map_value->tophash[i] == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (map_value->keys[i].len != W3C_KEY_LENGTH) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ char current_header_key[W3C_KEY_LENGTH];
|
|
|
+ bpf_probe_read(current_header_key, sizeof(current_header_key), map_value->keys[i].str);
|
|
|
+ for (int i = 0; i < W3C_KEY_LENGTH; i++) {
|
|
|
+ if (current_header_key[i] != "Traceparent"[i] && current_header_key[i] != "traceparent"[i]) {
|
|
|
+ goto outer_loop;
|
|
|
+ }
|
|
|
+ }
|
|
|
+// if (!bpf_memcmp(current_header_key, "traceparent", W3C_KEY_LENGTH) && !bpf_memcmp(current_header_key, "Traceparent", W3C_KEY_LENGTH))
|
|
|
+// {
|
|
|
+// bpf_printk("not find it");
|
|
|
+// continue;
|
|
|
+// }
|
|
|
+// bpf_printk("find it:%s", map_value->keys[i].str);
|
|
|
+ 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
|
|
|
+// bpf_printk("traceparent_header_value_go_str.str:%s", traceparent_header_value_go_str.str);
|
|
|
+
|
|
|
+ if (res < 0) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+// if (traceparent_header_value_go_str.len != W3C_VAL_LENGTH) {
|
|
|
+// continue;
|
|
|
+// }
|
|
|
+ char traceparent_header_value[120];
|
|
|
+ res = bpf_probe_read(&traceparent_header_value, sizeof(traceparent_header_value),
|
|
|
+ traceparent_header_value_go_str.str);
|
|
|
+ // 00-95b5ec2b81e2374a4a27ce36ab71d349-18f65a5a3ab22213-02��c�����c����@c;
|
|
|
+// bpf_printk("traceparent_header_value11:[%s]", traceparent_header_value);
|
|
|
+
|
|
|
+// bpf_printk("111111111111111111:[%d]", range.start[1]);
|
|
|
+
|
|
|
+
|
|
|
+ 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) {
|
|
|
+// bpf_printk("no parent_span_context");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+// bpf_printk("parent_span_context-TraceID1:%s", parent_span_context->TraceID);
|
|
|
+// bpf_printk("parent_span_context-SpanID1:%s", parent_span_context->SpanID);
|
|
|
+// struct cw_header_token *header_range_data = bpf_map_lookup_elem(&header_range, &map_id);
|
|
|
+// if (!header_range_data) {
|
|
|
+// return NULL;
|
|
|
+// }
|
|
|
+// u32 idx = 0;
|
|
|
+// u32 str_len = 0;
|
|
|
+// struct cw_header_token tmp = *header_range_data;
|
|
|
+// tmp.a = 4;
|
|
|
+//// struct Token t = {.start=1,.len=2};
|
|
|
+// for (int k = 0; k < MAX_TOKENS; ++k) {
|
|
|
+//// header_range_data->token[k] = t ;
|
|
|
+// header_range_data->start[k] = 1 ;
|
|
|
+//
|
|
|
+//// tmp.token[k] = t ;
|
|
|
+// }
|
|
|
+// for (int k = 0; k < MAX_TOKENS; ++k) {
|
|
|
+//// struct Token t = {.start=1, .len=2};
|
|
|
+//// header_range_data->token[k] = t;
|
|
|
+// bpf_printk("header_range_data->token---%d", header_range_data->token[k].len);
|
|
|
+//
|
|
|
+// }
|
|
|
+//
|
|
|
+//// header_range_data->token[0].len = 1;
|
|
|
+//
|
|
|
+// bpf_printk("header_range_data->token---%d", header_range_data->token[0].len);
|
|
|
+//
|
|
|
+// for (u32 k = 0; k < MAX_SIZE; ++k) {
|
|
|
+// if (traceparent_header_value[k] == '\0') {
|
|
|
+//// bpf_map_update_elem(&header_range, &map_id, &tmp, BPF_ANY);
|
|
|
+// break;
|
|
|
+// }
|
|
|
+// bpf_printk("k---%d", k);
|
|
|
+// if (traceparent_header_value[k] == ':') {
|
|
|
+// str_len = 0;
|
|
|
+// tmp.token[idx].start = k + 1;
|
|
|
+// header_range_data->start[idx] = k + 1;
|
|
|
+// bpf_printk("range.start:%d", tmp.token[idx].start);
|
|
|
+// idx++;
|
|
|
+// }
|
|
|
+//// else {
|
|
|
+//// str_len++;
|
|
|
+//// }
|
|
|
+//// tmp.token[idx].len = str_len;
|
|
|
+// bpf_printk("range.len token[%d]:[%d]", idx, str_len);
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+// for (int k = 0; k < MAX_TOKENS; ++k) {
|
|
|
+//
|
|
|
+//// struct Token tt[8] = tmp.token;
|
|
|
+//// header_range_data->token[k] = t;
|
|
|
+// bpf_printk("header_range_data->token---%d", header_range_data->token[k].len);
|
|
|
+//// bpf_printk("header_range_data->token---%d", tmp.token[k].len);
|
|
|
+// }
|
|
|
+// for (int k = 0; k < MAX_TOKENS; ++k) {
|
|
|
+//
|
|
|
+//// struct Token tt[8] = tmp.token;
|
|
|
+//// header_range_data->token[k] = t;
|
|
|
+// bpf_printk("header_range_data->token---%d", header_range_data->token[k].len);
|
|
|
+//// bpf_printk("header_range_data->token---%d", tmp.token[k].len);
|
|
|
+// }
|
|
|
+
|
|
|
+// if (idx == MAX_TOKENS) {
|
|
|
+// for (int k = 0; k < idx; ++k) {
|
|
|
+//// struct Token t = {.start=1, .len=2};
|
|
|
+//// header_range_data->token[k] = t;
|
|
|
+// bpf_printk("header_range_data->token---%d", header_range_data->token[k].len);
|
|
|
+//
|
|
|
+// }
|
|
|
+// }
|
|
|
+// for (int k = 0; k < idx ; ++k) {
|
|
|
+// struct Token t = {.start=1,.len=2};
|
|
|
+// header_range_data->token[k]= t;
|
|
|
+// }
|
|
|
+// header_range_data = &tmp;
|
|
|
+
|
|
|
+
|
|
|
+// bpf_printk("header_range_data---%d", header_range_data->a);
|
|
|
+// bpf_printk("header_range_data->token---%d", header_range_data->token[0].len);
|
|
|
+// __builtin_memcpy(&header_range_data->token, &tmp.token, sizeof(header_range_data->token));
|
|
|
+// struct cw_header_token *header_range_data2 = bpf_map_lookup_elem(&header_range, &map_id);
|
|
|
+// if (!header_range_data2) {
|
|
|
+// return NULL;
|
|
|
+// }
|
|
|
+// bpf_printk("header_range_data2---%d", header_range_data2->a);
|
|
|
+
|
|
|
+// for (int kl = 0; kl < idx; ++kl) {
|
|
|
+// u32 start = tmp.token[kl].start;
|
|
|
+// u32 len = tmp.token[kl].len;
|
|
|
+// bpf_printk("Token [%d][%d]", kl,start);
|
|
|
+// }
|
|
|
+ w3c_string_to_span_context(traceparent_header_value, parent_span_context);
|
|
|
+// bpf_printk("traceparent_header_value2:%s", traceparent_header_value);
|
|
|
+// bpf_printk("parent_span_context-TraceID: ");
|
|
|
+// for (int i = 0; i < TRACE_ID_SIZE; i++) {
|
|
|
+// bpf_printk("%02x", parent_span_context->TraceID[i]);
|
|
|
+// }
|
|
|
+// bpf_printk("parent_span_context-TraceID2:%s", parent_span_context->TraceID);
|
|
|
+// bpf_printk("parent_span_context-SpanID2:%s", parent_span_context->SpanID);
|
|
|
+ return parent_span_context;
|
|
|
+ outer_loop:
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+// This instrumentation attaches uprobe to the following function:
|
|
|
+// func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
|
|
|
+SEC("uprobe/HandlerFunc_ServeHTTP")
|
|
|
+int uprobe_HandlerFunc_ServeHTTP(struct pt_regs *ctx) {
|
|
|
+ bpf_printk("[uprobe_HandlerFunc_ServeHTTP]");
|
|
|
+ void *req_ctx_ptr = get_Go_context(ctx, 4, ctx_ptr_pos, false);
|
|
|
+ void *key = get_consistent_key(ctx, req_ctx_ptr);
|
|
|
+ void *httpReq_ptr = bpf_map_lookup_elem(&http_server_uprobes, &key);
|
|
|
+ if (httpReq_ptr != NULL) {
|
|
|
+ bpf_printk("uprobe/HandlerFunc_ServeHTTP already tracked with the current request");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ u32 map_id = 0;
|
|
|
+ struct uprobe_data_t *uprobe_data = bpf_map_lookup_elem(&http_server_uprobe_storage_map, &map_id);
|
|
|
+ if (uprobe_data == NULL) {
|
|
|
+ bpf_printk("uprobe/HandlerFunc_ServeHTTP: http_server_span is NULL");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ __builtin_memset(uprobe_data, 0, sizeof(struct uprobe_data_t));
|
|
|
+
|
|
|
+ // Save response writer
|
|
|
+ void *resp_impl = get_argument(ctx, 3);
|
|
|
+ uprobe_data->resp_ptr = (u64) resp_impl;
|
|
|
+
|
|
|
+ struct http_server_span_t *http_server_span = &uprobe_data->span;
|
|
|
+ http_server_span->start_time = bpf_ktime_get_ns();
|
|
|
+
|
|
|
+ // Propagate context
|
|
|
+ void *req_ptr = get_argument(ctx, 4);
|
|
|
+ struct span_context *parent_ctx = extract_context_from_req_headers((void *) (req_ptr + headers_ptr_pos));
|
|
|
+ if (parent_ctx != NULL) {
|
|
|
+ // found parent context in http headers
|
|
|
+ http_server_span->psc = *parent_ctx;
|
|
|
+ copy_byte_arrays(http_server_span->psc.TraceID, http_server_span->sc.TraceID, TRACE_ID_SIZE);
|
|
|
+ generate_random_bytes(http_server_span->sc.SpanID, SPAN_ID_SIZE);
|
|
|
+ } else {
|
|
|
+ http_server_span->sc = generate_span_context();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (req_ctx_ptr == NULL) {
|
|
|
+// bpf_printk("uprobe/HandlerFunc_ServeHTTP: req_ctx_ptr is NULL");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+// bpf_printk("found parent context in http headers");
|
|
|
+
|
|
|
+// bpf_printk("parent_span_context-TraceID---");
|
|
|
+
|
|
|
+
|
|
|
+// for (int i = 0; i < TRACE_ID_SIZE; i++) {
|
|
|
+// bpf_printk("%02x", uprobe_data->span.sc.TraceID[i]);
|
|
|
+// }
|
|
|
+
|
|
|
+// bpf_printk("parent_span_context-SpanID---");
|
|
|
+// for (int i = 0; i < SPAN_ID_SIZE; i++) {
|
|
|
+// bpf_printk("%02x", uprobe_data->span.sc.SpanID[i]);
|
|
|
+// }
|
|
|
+
|
|
|
+// bpf_map_update_elem(&http_server_uprobes, &key, uprobe_data, 0);
|
|
|
+// start_tracking_span(req_ctx_ptr, &http_server_span->sc);
|
|
|
+
|
|
|
+// void save_tracking_span(struct apm_span_context *sc)
|
|
|
+ struct apm_span_context apm_sc = *(struct apm_span_context *) &http_server_span->sc;
|
|
|
+ save_tracking_span(&apm_sc);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void read_go_string(void *base, int offset, char *output, int maxLen, const char *errorMsg) {
|
|
|
+ void *ptr = (void *) (base + offset);
|
|
|
+ if (!get_go_string_from_user_ptr(ptr, output, maxLen)) {
|
|
|
+ bpf_printk("Failed to get %s", errorMsg);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// This instrumentation attaches uprobe to the following function:
|
|
|
+// func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
|
|
|
+SEC("uprobe/HandlerFunc_ServeHTTP")
|
|
|
+int uprobe_HandlerFunc_ServeHTTP_Returns(struct pt_regs *ctx) {
|
|
|
+ bpf_printk("[uprobe_HandlerFunc_ServeHTTP_Returns]");
|
|
|
+ u64 end_time = bpf_ktime_get_ns();
|
|
|
+ void *req_ctx_ptr = get_Go_context(ctx, 4, ctx_ptr_pos, false);
|
|
|
+ void *key = get_consistent_key(ctx, req_ctx_ptr);
|
|
|
+
|
|
|
+ struct uprobe_data_t *uprobe_data = bpf_map_lookup_elem(&http_server_uprobes, &key);
|
|
|
+ if (uprobe_data == NULL) {
|
|
|
+ bpf_printk("uprobe/HandlerFunc_ServeHTTP_Returns: entry_state is NULL");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ bpf_map_delete_elem(&http_server_uprobes, &key);
|
|
|
+
|
|
|
+ struct http_server_span_t *http_server_span = &uprobe_data->span;
|
|
|
+ void *resp_ptr = (void *) uprobe_data->resp_ptr;
|
|
|
+ void *req_ptr = NULL;
|
|
|
+ bpf_probe_read(&req_ptr, sizeof(req_ptr), (void *) (resp_ptr + req_ptr_pos));
|
|
|
+
|
|
|
+ http_server_span->end_time = end_time;
|
|
|
+
|
|
|
+ void *url_ptr = 0;
|
|
|
+ bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *) (req_ptr + url_ptr_pos));
|
|
|
+ // Collect fields from response
|
|
|
+ read_go_string(req_ptr, method_ptr_pos, http_server_span->method, sizeof(http_server_span->method),
|
|
|
+ "method from request");
|
|
|
+ read_go_string(url_ptr, path_ptr_pos, http_server_span->path, sizeof(http_server_span->path),
|
|
|
+ "path from Request.URL");
|
|
|
+ read_go_string(req_ptr, remote_addr_pos, http_server_span->remote_addr, sizeof(http_server_span->remote_addr),
|
|
|
+ "remote addr from Request.RemoteAddr");
|
|
|
+ read_go_string(req_ptr, host_pos, http_server_span->host, sizeof(http_server_span->host), "host from Request.Host");
|
|
|
+ read_go_string(req_ptr, proto_pos, http_server_span->proto, sizeof(http_server_span->proto),
|
|
|
+ "proto from Request.Proto");
|
|
|
+
|
|
|
+ // status code
|
|
|
+ bpf_probe_read(&http_server_span->status_code, sizeof(http_server_span->status_code),
|
|
|
+ (void *) (resp_ptr + status_code_pos));
|
|
|
+
|
|
|
+ bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, http_server_span, sizeof(*http_server_span));
|
|
|
+ stop_tracking_span(&http_server_span->sc, &http_server_span->psc);
|
|
|
+
|
|
|
+ struct apm_trace_key_t trace_key = get_apm_trace_key(120 * NS_PER_SEC, true);
|
|
|
+ bpf_map_delete_elem(&apm_span_context_map, &trace_key);
|
|
|
+
|
|
|
+ bpf_printk("HTTP_END");
|
|
|
+ return 0;
|
|
|
+}
|