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

Add status code to the HTTP client probe #527

Merged
merged 2 commits into from
Nov 30, 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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fixtures/%:
if [ ! -d "opentelemetry-helm-charts" ]; then \
git clone https://github.com/open-telemetry/opentelemetry-helm-charts.git; \
fi
helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/opentelemetry-collector
helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/charts/opentelemetry-collector
kubectl wait --for=condition=Ready --timeout=60s pod/test-opentelemetry-collector-0
kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml
kubectl wait --for=condition=Complete --timeout=60s job/sample-job
Expand Down
5 changes: 5 additions & 0 deletions internal/include/arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,9 @@ static __always_inline void *get_consistent_key(struct pt_regs *ctx, void *conte
return contextContext;
}

static __always_inline bool is_register_abi()
{
return is_registers_abi;
}

#endif
153 changes: 153 additions & 0 deletions internal/pkg/inject/offset_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -2093,6 +2093,159 @@
}
]
},
{
"struct": "Response",
"fields": [
{
"field": "StatusCode",
"offsets": [
{
"offset": 16,
"versions": [
"1.12.0",
"1.12.1",
"1.12.2",
"1.12.3",
"1.12.4",
"1.12.5",
"1.12.6",
"1.12.7",
"1.12.8",
"1.12.9",
"1.12.10",
"1.12.11",
"1.12.12",
"1.12.13",
"1.12.14",
"1.12.15",
"1.12.16",
"1.12.17",
"1.13.0",
"1.13.1",
"1.13.2",
"1.13.3",
"1.13.4",
"1.13.5",
"1.13.6",
"1.13.7",
"1.13.8",
"1.13.9",
"1.13.10",
"1.13.11",
"1.13.12",
"1.13.13",
"1.13.14",
"1.13.15",
"1.14.0",
"1.14.1",
"1.14.2",
"1.14.3",
"1.14.4",
"1.14.5",
"1.14.6",
"1.14.7",
"1.14.8",
"1.14.9",
"1.14.10",
"1.14.11",
"1.14.12",
"1.14.13",
"1.14.14",
"1.14.15",
"1.15.0",
"1.15.1",
"1.15.2",
"1.15.3",
"1.15.4",
"1.15.5",
"1.15.6",
"1.15.7",
"1.15.8",
"1.15.9",
"1.15.10",
"1.15.11",
"1.15.12",
"1.15.13",
"1.15.14",
"1.15.15",
"1.16.0",
"1.16.1",
"1.16.2",
"1.16.3",
"1.16.4",
"1.16.5",
"1.16.6",
"1.16.7",
"1.16.8",
"1.16.9",
"1.16.10",
"1.16.11",
"1.16.12",
"1.16.13",
"1.16.14",
"1.16.15",
"1.17.0",
"1.17.1",
"1.17.2",
"1.17.3",
"1.17.4",
"1.17.5",
"1.17.6",
"1.17.7",
"1.17.8",
"1.17.9",
"1.17.10",
"1.17.11",
"1.17.12",
"1.17.13",
"1.18.0",
"1.18.1",
"1.18.2",
"1.18.3",
"1.18.4",
"1.18.5",
"1.18.6",
"1.18.7",
"1.18.8",
"1.18.9",
"1.18.10",
"1.19.0",
"1.19.1",
"1.19.2",
"1.19.3",
"1.19.4",
"1.19.5",
"1.19.6",
"1.19.7",
"1.19.8",
"1.19.9",
"1.19.10",
"1.19.11",
"1.19.12",
"1.19.13",
"1.20.0",
"1.20.1",
"1.20.2",
"1.20.3",
"1.20.4",
"1.20.5",
"1.20.6",
"1.20.7",
"1.20.8",
"1.20.9",
"1.20.10",
"1.20.11",
"1.21.0",
"1.21.1",
"1.21.2",
"1.21.3",
"1.21.4"
]
}
]
}
]
},
{
"struct": "response",
"fields": [
Expand Down
73 changes: 58 additions & 15 deletions internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ char __license[] SEC("license") = "Dual MIT/GPL";

struct http_request_t {
BASE_SPAN_PROPERTIES
u64 status_code;
char method[MAX_METHOD_SIZE];
char path[MAX_PATH_SIZE];
};
Expand All @@ -32,6 +33,14 @@ struct {
__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 http_request_t));
__uint(max_entries, 1);
} http_client_uprobe_storage_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
} events SEC(".maps");
Expand All @@ -43,6 +52,7 @@ volatile const u64 path_ptr_pos;
volatile const u64 headers_ptr_pos;
volatile const u64 ctx_ptr_pos;
volatile const u64 buckets_ptr_pos;
volatile const u64 status_code_pos;

static __always_inline long inject_header(void* headers_ptr, struct span_context* propagated_ctx) {
// Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go
Expand Down Expand Up @@ -146,12 +156,9 @@ static __always_inline long inject_header(void* headers_ptr, struct span_context
}

// This instrumentation attaches uprobe to the following function:
// func net/http/client.Do(req *Request)
// func net/http/client.Do(req *Request) (*Response, error)
SEC("uprobe/HttpClient_Do")
int uprobe_HttpClient_Do(struct pt_regs *ctx) {
struct http_request_t httpReq = {};
httpReq.start_time = bpf_ktime_get_ns();

u64 request_pos = 2;
void *req_ptr = get_argument(ctx, request_pos);

Expand All @@ -169,42 +176,78 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) {
return 0;
}

u32 map_id = 0;
struct http_request_t *httpReq = bpf_map_lookup_elem(&http_client_uprobe_storage_map, &map_id);
if (httpReq == NULL)
{
bpf_printk("uprobe/HttpClient_Do: httpReq is NULL");
return 0;
}

__builtin_memset(httpReq, 0, sizeof(struct http_request_t));
httpReq->start_time = bpf_ktime_get_ns();

struct span_context *parent_span_ctx = get_parent_span_context(context_ptr_val);
if (parent_span_ctx != NULL) {
bpf_probe_read(&httpReq.psc, sizeof(httpReq.psc), parent_span_ctx);
copy_byte_arrays(httpReq.psc.TraceID, httpReq.sc.TraceID, TRACE_ID_SIZE);
generate_random_bytes(httpReq.sc.SpanID, SPAN_ID_SIZE);
bpf_probe_read(&httpReq->psc, sizeof(httpReq->psc), parent_span_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();
httpReq->sc = generate_span_context();
}

if (!get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq.method, sizeof(httpReq.method))) {
if (!get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq->method, sizeof(httpReq->method))) {
bpf_printk("uprobe_HttpClient_Do: Failed to get method from request");
return 0;
}

// get path from Request.URL
void *url_ptr = 0;
bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr+url_ptr_pos));
if (!get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq.path, sizeof(httpReq.path))) {
if (!get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq->path, sizeof(httpReq->path))) {
bpf_printk("uprobe_HttpClient_Do: Failed to get path from Request.URL");
return 0;
}

// get headers from Request
void *headers_ptr = 0;
bpf_probe_read(&headers_ptr, sizeof(headers_ptr), (void *)(req_ptr+headers_ptr_pos));
long res = inject_header(headers_ptr, &httpReq.sc);
long res = inject_header(headers_ptr, &httpReq->sc);
if (res < 0) {
bpf_printk("uprobe_HttpClient_Do: Failed to inject header");
}

// Write event
bpf_map_update_elem(&http_events, &key, &httpReq, 0);
start_tracking_span(context_ptr_val, &httpReq.sc);
bpf_map_update_elem(&http_events, &key, httpReq, 0);
start_tracking_span(context_ptr_val, &httpReq->sc);
return 0;
}

// This instrumentation attaches uretprobe to the following function:
// func net/http/client.Do(req *Request)
UPROBE_RETURN(HttpClient_Do, struct http_request_t, http_events, events, 2, ctx_ptr_pos, false)
// func net/http/client.Do(req *Request) (*Response, error)
SEC("uprobe/HttpClient_Do")
int uprobe_HttpClient_Do_Returns(struct pt_regs *ctx) {
u64 end_time = bpf_ktime_get_ns();
void *req_ctx_ptr = get_Go_context(ctx, 2, ctx_ptr_pos, false);
void *key = get_consistent_key(ctx, req_ctx_ptr);

struct http_request_t *http_req_span = bpf_map_lookup_elem(&http_events, &key);
if (http_req_span == NULL) {
bpf_printk("probe_HttpClient_Do_Returns: entry_state is NULL");
return 0;
}
bpf_map_delete_elem(&http_events, &key);

if (is_register_abi()) {
// Getting the returned response
void *resp_ptr = get_argument(ctx, 1);
// Get status code from response
bpf_probe_read(&http_req_span->status_code, sizeof(http_req_span->status_code), (void *)(resp_ptr + status_code_pos));
}

http_req_span->end_time = end_time;

bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, http_req_span, sizeof(*http_req_span));
stop_tracking_span(&http_req_span->sc, &http_req_span->psc);
return 0;
}

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

Loading
Loading