Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
JamieDanielson committed Apr 18, 2023
1 parent 03b9bc9 commit 7049c04
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 1 deletion.
83 changes: 82 additions & 1 deletion pkg/instrumentors/bpf/net/http/server/bpf/probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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
Expand All @@ -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; j<bucket_count; j++) {
bpf_probe_read(map_value, sizeof(struct map_bucket), header_buckets + (j * sizeof(struct map_bucket)));
for (u64 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);
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)
Expand Down Expand Up @@ -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;
Expand Down
20 changes: 20 additions & 0 deletions pkg/instrumentors/bpf/net/http/server/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type HttpEvent struct {
Method [6]byte
Path [100]byte
SpanContext context.EbpfSpanContext
ParentSpanContext context.EbpfSpanContext
}

type httpServerInstrumentor struct {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -194,6 +213,7 @@ func (h *httpServerInstrumentor) convertEvent(e *HttpEvent) *events.Event {
semconv.HTTPMethodKey.String(method),
semconv.HTTPTargetKey.String(path),
},
ParentSpanContext: pscPtr,
}
}

Expand Down

0 comments on commit 7049c04

Please sign in to comment.