From 7049c0465e1b5e6bdecee5540b22bd477755e104 Mon Sep 17 00:00:00 2001 From: JamieDanielson Date: Tue, 18 Apr 2023 15:08:06 -0400 Subject: [PATCH] ported from keyval-dev/opentelemetry-go-instrumentation#49 --- .../bpf/net/http/server/bpf/probe.bpf.c | 83 ++++++++++++++++++- .../bpf/net/http/server/probe.go | 20 +++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/pkg/instrumentors/bpf/net/http/server/bpf/probe.bpf.c b/pkg/instrumentors/bpf/net/http/server/bpf/probe.bpf.c index b49802cfe..22de9661d 100644 --- a/pkg/instrumentors/bpf/net/http/server/bpf/probe.bpf.c +++ b/pkg/instrumentors/bpf/net/http/server/bpf/probe.bpf.c @@ -15,12 +15,15 @@ #include "arguments.h" #include "span_context.h" #include "go_context.h" +#include "go_types.h" char __license[] SEC("license") = "Dual MIT/GPL"; #define PATH_MAX_LEN 100 #define METHOD_MAX_LEN 6 // Longer method: DELETE #define MAX_CONCURRENT 50 +#define W3C_KEY_LENGTH 11 +#define W3C_VAL_LENGTH 55 struct http_request_t { @@ -29,6 +32,7 @@ struct http_request_t char method[METHOD_MAX_LEN]; char path[PATH_MAX_LEN]; struct span_context sc; + struct span_context psc; }; // map key: pointer to the goroutine that handles the request @@ -45,10 +49,80 @@ struct __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); } events 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 span_context)); + __uint(max_entries, 1); +} parent_span_context_storage_map 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 headers_ptr_pos; + +static __always_inline struct span_context* extract_context_from_req_headers(void* headers_ptr_ptr) { + void* headers_ptr; + bpf_probe_read(&headers_ptr, sizeof(headers_ptr), headers_ptr_ptr); + u64 headers_count = 0; + bpf_probe_read(&headers_count, sizeof(headers_count), headers_ptr); + if (headers_count == 0) { + return NULL; + } + unsigned char log_2_bucket_count; + bpf_probe_read(&log_2_bucket_count, sizeof(log_2_bucket_count), headers_ptr + 9); + u64 bucket_count = 1 << log_2_bucket_count; + void* header_buckets; + bpf_probe_read(&header_buckets, sizeof(header_buckets), headers_ptr + 16); + u32 map_id = 0; + struct map_bucket* map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id); + if (!map_value) { + return NULL; + } + // Currently iterating only over the first bucket + if (bucket_count >= 2) { + bucket_count = 1; + } + for (u64 j=0; jtophash[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); + if (!bpf_memcmp(current_header_key, "traceparent", W3C_KEY_LENGTH) && !bpf_memcmp(current_header_key, "Traceparent", W3C_KEY_LENGTH)) { + continue; + } + void* traceparent_header_value_ptr = map_value->values[i].array; + struct go_string traceparent_header_value_go_str; + bpf_probe_read(&traceparent_header_value_go_str, sizeof(traceparent_header_value_go_str), traceparent_header_value_ptr); + if (traceparent_header_value_go_str.len != W3C_VAL_LENGTH) { + continue; + } + char traceparent_header_value[W3C_VAL_LENGTH]; + bpf_probe_read(&traceparent_header_value, sizeof(traceparent_header_value), traceparent_header_value_go_str.str); + struct span_context* parent_span_context = bpf_map_lookup_elem(&parent_span_context_storage_map, &map_id); + if (!parent_span_context) { + return NULL; + } + w3c_string_to_span_context(traceparent_header_value, parent_span_context); + return parent_span_context; + } + } + return NULL; +} // This instrumentation attaches uprobe to the following function: // func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) @@ -86,7 +160,14 @@ int uprobe_ServerMux_ServeHTTP(struct pt_regs *ctx) void *goroutine = get_goroutine_address(ctx); // Write event - httpReq.sc = generate_span_context(); + struct span_context* parent_ctx = extract_context_from_req_headers(req_ptr+headers_ptr_pos); + if(parent_ctx != NULL) { + httpReq.psc = *parent_ctx; + copy_byte_arrays(httpReq.psc.TraceID, httpReq.sc.TraceID, TRACE_ID_SIZE); + generate_random_bytes(httpReq.sc.SpanID, SPAN_ID_SIZE); + } else { + httpReq.sc = generate_span_context(); + } bpf_map_update_elem(&context_to_http_events, &goroutine, &httpReq, 0); long res = bpf_map_update_elem(&spans_in_progress, &goroutine, &httpReq.sc, 0); return 0; diff --git a/pkg/instrumentors/bpf/net/http/server/probe.go b/pkg/instrumentors/bpf/net/http/server/probe.go index d6f6cfa45..bcc36379e 100644 --- a/pkg/instrumentors/bpf/net/http/server/probe.go +++ b/pkg/instrumentors/bpf/net/http/server/probe.go @@ -43,6 +43,7 @@ type HttpEvent struct { Method [6]byte Path [100]byte SpanContext context.EbpfSpanContext + ParentSpanContext context.EbpfSpanContext } type httpServerInstrumentor struct { @@ -81,6 +82,11 @@ func (h *httpServerInstrumentor) Load(ctx *context.InstrumentorContext) error { StructName: "net/url.URL", Field: "Path", }, + { + VarName: "headers_ptr_pos", + StructName: "net/http.Request", + Field: "Header", + }, }, false) if err != nil { @@ -183,6 +189,19 @@ func (h *httpServerInstrumentor) convertEvent(e *HttpEvent) *events.Event { TraceFlags: trace.FlagsSampled, }) + var pscPtr *trace.SpanContext + if e.ParentSpanContext.TraceID.IsValid() { + psc := trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: e.ParentSpanContext.TraceID, + SpanID: e.ParentSpanContext.SpanID, + TraceFlags: trace.FlagsSampled, + Remote: true, + }) + pscPtr = &psc + } else { + pscPtr = nil + } + return &events.Event{ Library: h.LibraryName(), Name: path, @@ -194,6 +213,7 @@ func (h *httpServerInstrumentor) convertEvent(e *HttpEvent) *events.Event { semconv.HTTPMethodKey.String(method), semconv.HTTPTargetKey.String(path), }, + ParentSpanContext: pscPtr, } }