|
|
@@ -19,6 +19,9 @@ struct http_request_t {
|
|
|
u64 status_code;
|
|
|
char method[MAX_METHOD_SIZE];
|
|
|
char path[MAX_PATH_SIZE];
|
|
|
+
|
|
|
+ struct apm_span_context apm_sc;
|
|
|
+ struct apm_span_context apm_psc;
|
|
|
};
|
|
|
|
|
|
struct {
|
|
|
@@ -158,12 +161,114 @@ static __always_inline long inject_header(void* headers_ptr, struct span_context
|
|
|
bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
|
|
|
return 0;
|
|
|
}
|
|
|
+// 00:00:1015481350055581:5450531005555981:5610250100539899:304775019cd3218a304775019cd3218a:1001025098564810:140acc88cde8773f
|
|
|
+static __always_inline long cw_inject_header(void* headers_ptr, struct apm_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
|
|
|
+ u64 curr_keyvalue_count = 0;
|
|
|
+ long res = bpf_probe_read_user(&curr_keyvalue_count, sizeof(curr_keyvalue_count), headers_ptr);
|
|
|
+
|
|
|
+ if (res < 0) {
|
|
|
+ bpf_printk("Couldn't read map key-value count from user");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (curr_keyvalue_count >= 8) {
|
|
|
+ bpf_printk("Map size is bigger than 8, skipping context propagation");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get pointer to temp bucket struct we store in a map (avoiding large local variable on the stack)
|
|
|
+ // Performing read-modify-write on the bucket
|
|
|
+ u32 map_id = 0;
|
|
|
+ struct map_bucket *bucket_map_value = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id);
|
|
|
+ if (!bucket_map_value) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ void *buckets_ptr_ptr = (void*) (headers_ptr + buckets_ptr_pos);
|
|
|
+ void *bucket_ptr = 0; // The actual pointer to the buckets
|
|
|
+
|
|
|
+ if (curr_keyvalue_count == 0) {
|
|
|
+ // No key-value pairs in the Go map, need to "allocate" memory for the user
|
|
|
+ bucket_ptr = write_target_data(bucket_map_value, sizeof(struct map_bucket));
|
|
|
+ if (bucket_ptr == NULL) {
|
|
|
+ bpf_printk("inject_header: Failed to write bucket to user");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ // Update the buckets pointer in the hmap struct to point to newly allocated bucket
|
|
|
+ res = bpf_probe_write_user(buckets_ptr_ptr, &bucket_ptr, sizeof(bucket_ptr));
|
|
|
+ if (res < 0) {
|
|
|
+ bpf_printk("Failed to update the map bucket pointer for the user");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // There is at least 1 key-value pair, hence the bucket pointer from the user is valid
|
|
|
+ // Read the bucket pointer
|
|
|
+ res = bpf_probe_read_user(&bucket_ptr, sizeof(bucket_ptr), buckets_ptr_ptr);
|
|
|
+ // Read the user's bucket to the eBPF map entry
|
|
|
+ bpf_probe_read_user(bucket_map_value, sizeof(struct map_bucket), bucket_ptr);
|
|
|
+ }
|
|
|
+
|
|
|
+ u8 bucket_index = curr_keyvalue_count & 0x7;
|
|
|
+
|
|
|
+ char traceparent_tophash = 0xee;
|
|
|
+ bucket_map_value->tophash[bucket_index] = traceparent_tophash;
|
|
|
+
|
|
|
+ // Prepare the key string for the user
|
|
|
+ char key[W3C_KEY_LENGTH] = "traceparent";
|
|
|
+ void *ptr = write_target_data(key, W3C_KEY_LENGTH);
|
|
|
+ if (ptr == NULL) {
|
|
|
+ bpf_printk("inject_header: Failed to write key to user");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ bucket_map_value->keys[bucket_index] = (struct go_string_ot) {.len = W3C_KEY_LENGTH, .str = ptr};
|
|
|
+
|
|
|
+ // Prepare the value string slice
|
|
|
+ // First the value string which constains the span context
|
|
|
+ char val[CW_HEADER_LENGTH];
|
|
|
+ span_context_to_cw_string(propagated_ctx, val);
|
|
|
+ ptr = write_target_data(val, sizeof(val));
|
|
|
+ if(ptr == NULL) {
|
|
|
+ bpf_printk("inject_header: Failed to write value to user");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The go string pointing to the above val
|
|
|
+ struct go_string_ot header_value = {.len = CW_HEADER_LENGTH, .str = ptr};
|
|
|
+ ptr = write_target_data((void*)&header_value, sizeof(header_value));
|
|
|
+ if(ptr == NULL) {
|
|
|
+ bpf_printk("inject_header: Failed to write go_string to user");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Last, go_slice pointing to the above go_string
|
|
|
+ bucket_map_value->values[bucket_index] = (struct go_slice_ot) {.array = ptr, .cap = 1, .len = 1};
|
|
|
+
|
|
|
+ // Update the map header count field
|
|
|
+ curr_keyvalue_count += 1;
|
|
|
+ res = bpf_probe_write_user(headers_ptr, &curr_keyvalue_count, sizeof(curr_keyvalue_count));
|
|
|
+ if (res < 0) {
|
|
|
+ bpf_printk("Failed to update key-value count in map header");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update the bucket
|
|
|
+ res = bpf_probe_write_user(bucket_ptr, bucket_map_value, sizeof(struct map_bucket));
|
|
|
+ if (res < 0) {
|
|
|
+ bpf_printk("Failed to update bucket content");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ bpf_memset((unsigned char *)bucket_map_value, sizeof(struct map_bucket), 0);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
// This instrumentation attaches uprobe to the following function:
|
|
|
// func net/http/transport.roundTrip(req *Request) (*Response, error)
|
|
|
SEC("uprobe/Transport_roundTrip")
|
|
|
int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
|
|
|
- bpf_printk("[HTTP Client] start");
|
|
|
+ bpf_printk("[Uprobe HTTP Client] start");
|
|
|
+
|
|
|
u64 request_pos = 2;
|
|
|
void *req_ptr = get_argument(ctx, request_pos);
|
|
|
|
|
|
@@ -196,17 +301,35 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
|
|
|
if (parent_span_ctx != NULL) {
|
|
|
bpf_printk("[Client] 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 {
|
|
|
- bpf_printk("[Client] parent_span_ctx == NULL");
|
|
|
- struct apm_span_context *apm_sc = get_tracking_span();
|
|
|
- if (!apm_sc) {
|
|
|
- httpReq->sc = generate_span_context();
|
|
|
- } else {
|
|
|
- httpReq->sc = *(struct span_context *) apm_sc;
|
|
|
- }
|
|
|
+// bpf_printk("[Client] parent_span_ctx == NULL");
|
|
|
+// struct apm_span_context *apm_sc = get_tracking_span();
|
|
|
+// if (apm_sc) {
|
|
|
+// httpReq->sc = *(struct span_context *) apm_sc;
|
|
|
+// } else {
|
|
|
+// httpReq->sc = generate_span_context();
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+ struct apm_span_context *cw_sc = cw_get_parent_tracking_span();
|
|
|
+ if(cw_sc){
|
|
|
+// httpReq->apm_sc = *cw_sc;
|
|
|
+ bpf_probe_read(&httpReq->apm_psc, sizeof(httpReq->apm_psc), cw_sc);
|
|
|
+ // copy trace_id
|
|
|
+// copy_byte_arrays(httpReq->apm_psc.instance_id, httpReq->apm_sc.instance_id, APM_INSTANCE_ID_SIZE);
|
|
|
+ copy_byte_arrays(httpReq->apm_psc.trace_id, httpReq->apm_sc.trace_id, APM_TRACE_ID_SIZE);
|
|
|
+ // new spanid
|
|
|
+ generate_random_bytes(httpReq->apm_sc.span_id, APM_SPAN_ID_SIZE);
|
|
|
+
|
|
|
+
|
|
|
+ // copy TraceID
|
|
|
+// copy_byte_arrays(httpReq->apm_psc.trace_id, httpReq->sc.TraceID, TRACE_ID_SIZE);
|
|
|
+// copy_byte_arrays(httpReq->psc.TraceID, httpReq->sc.TraceID, TRACE_ID_SIZE);
|
|
|
+// generate_random_bytes(httpReq->sc.SpanID, SPAN_ID_SIZE);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (!get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq->method, sizeof(httpReq->method))) {
|
|
|
@@ -226,8 +349,20 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
|
|
|
if (!get_go_string_from_user_ptr((void *)(req_ptr+request_host_pos), httpReq->host, sizeof(httpReq->host))) {
|
|
|
bpf_printk("uprobe_Transport_roundTrip: Failed to get host from Request");
|
|
|
}
|
|
|
-
|
|
|
- // get proto from Request
|
|
|
+ // TODO set request_host_pos
|
|
|
+ bpf_printk("[Client] httpReq->host:%llu",request_host_pos);
|
|
|
+// for (int i = 0; i < MAX_HOSTNAME_SIZE; ++i) {
|
|
|
+// if (httpReq->host[i] == '\0') {
|
|
|
+// break;
|
|
|
+// }
|
|
|
+//// httpReq->apm_sc.assumed_app_id[i] = httpReq->host[i];
|
|
|
+// bpf_printk("[Client] httpReq->host:%c",httpReq->host[i]);
|
|
|
+// }
|
|
|
+// unsigned char hash[APM_ASSUMED_APP_ID_STRING_SIZE];
|
|
|
+
|
|
|
+ set_assumed_app_id_arrays(httpReq->host, httpReq->apm_sc.assumed_app_id, APM_ASSUMED_APP_ID_STRING_SIZE);
|
|
|
+
|
|
|
+ // get proto from Request
|
|
|
if (!get_go_string_from_user_ptr((void *)(req_ptr+request_proto_pos), httpReq->proto, sizeof(httpReq->proto))) {
|
|
|
bpf_printk("uprobe_Transport_roundTrip: Failed to get proto from Request");
|
|
|
}
|
|
|
@@ -235,14 +370,15 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
|
|
|
// 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);
|
|
|
+ long res = cw_inject_header(headers_ptr, &httpReq->apm_sc);
|
|
|
if (res < 0) {
|
|
|
bpf_printk("uprobe_Transport_roundTrip: 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;
|
|
|
}
|
|
|
|
|
|
@@ -250,7 +386,7 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
|
|
|
// func net/http/transport.roundTrip(req *Request) (*Response, error)
|
|
|
SEC("uprobe/Transport_roundTrip")
|
|
|
int uprobe_Transport_roundTrip_Returns(struct pt_regs *ctx) {
|
|
|
- bpf_printk("[HTTP Client] Return start");
|
|
|
+ bpf_printk("[Uprobe HTTP Client] Return start");
|
|
|
|
|
|
u64 end_time = bpf_ktime_get_ns();
|
|
|
void *req_ctx_ptr = get_Go_context(ctx, 2, ctx_ptr_pos, false);
|