Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Adding context propagation to net/http server instrumentation #92

Merged
merged 13 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http

## [Unreleased]

### Added

- Add context propagation to net/http server instrumentation. ([#92](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/92))

### Changed

- Upgrade OpenTelemetry semantic conventions to v1.18.0. ([#162](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/162))
Expand Down
13 changes: 10 additions & 3 deletions include/go_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
struct go_string
{
char *str;
s32 len;
s64 len;
};

struct go_slice
{
void *array;
s32 len;
s32 cap;
s64 len;
s64 cap;
};

struct go_slice_user_ptr
Expand All @@ -44,6 +44,13 @@ struct go_iface
void *data;
};

struct map_bucket {
char tophash[8];
struct go_string keys[8];
struct go_slice values[8];
void *overflow;
MikeGoldsmith marked this conversation as resolved.
Show resolved Hide resolved
};

static __always_inline struct go_string write_user_go_string(char *str, u32 len)
{
// Copy chars to userspace
Expand Down
4 changes: 4 additions & 0 deletions offsets-tracker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ func main() {
StructName: "net/http.Request",
Field: "RemoteAddr",
},
{
StructName: "net/http.Request",
Field: "Header",
},
{
StructName: "net/http.Request",
Field: "ctx",
Expand Down
12 changes: 12 additions & 0 deletions pkg/inject/offset_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@
}
},
"net/http.Request": {
"Header": {
"versions": {
"oldest": "1.12.0",
"newest": "1.20.4"
},
"offsets": [
{
"offset": 56,
"since": "1.12"
}
]
},
"Method": {
"versions": {
"oldest": "1.12.0",
Expand Down
129 changes: 128 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,16 @@
#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 MAX_BUCKETS 8
#define METHOD_MAX_LEN 7
#define MAX_CONCURRENT 50
#define W3C_KEY_LENGTH 11
#define W3C_VAL_LENGTH 55

struct http_request_t
{
Expand All @@ -29,6 +33,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 @@ -40,6 +45,22 @@ struct
__uint(max_entries, MAX_CONCURRENT);
} context_to_http_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");

struct
{
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
Expand All @@ -50,6 +71,102 @@ 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;

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), headers_ptr + 16);
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 (u64 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 (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;
res = bpf_probe_read(&traceparent_header_value_go_str, sizeof(traceparent_header_value_go_str), traceparent_header_value_ptr);
if (res < 0)
{
return NULL;
}
if (traceparent_header_value_go_str.len != W3C_VAL_LENGTH)
{
continue;
}
char traceparent_header_value[W3C_VAL_LENGTH];
res = bpf_probe_read(&traceparent_header_value, sizeof(traceparent_header_value), traceparent_header_value_go_str.str);
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)
{
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 @@ -85,9 +202,19 @@ int uprobe_ServerMux_ServeHTTP(struct pt_regs *ctx)

// Get goroutine pointer
void *goroutine = get_goroutine_address(ctx, ctx_ptr_pos);
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();
}

// Write event
httpReq.sc = generate_span_context();
bpf_map_update_elem(&context_to_http_events, &goroutine, &httpReq, 0);
bpf_map_update_elem(&spans_in_progress, &goroutine, &httpReq.sc, 0);
return 0;
Expand Down
22 changes: 16 additions & 6 deletions pkg/instrumentors/bpf/net/http/server/bpf_bpfel_arm64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 16 additions & 6 deletions pkg/instrumentors/bpf/net/http/server/bpf_bpfel_x86.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading