Skip to content

Commit

Permalink
Add status code to the HTTP client probe (#527)
Browse files Browse the repository at this point in the history
* Add status code the HTTP client probe

* update tests
  • Loading branch information
RonFed authored Nov 30, 2023
1 parent 7b7c9a6 commit eb597d6
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 67 deletions.
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;
}
46 changes: 25 additions & 21 deletions internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_arm64.go

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

Loading

0 comments on commit eb597d6

Please sign in to comment.