diff --git a/bpf/gotracer/go_common.h b/bpf/gotracer/go_common.h index f3e8a83cee..6c8c0621da 100644 --- a/bpf/gotracer/go_common.h +++ b/bpf/gotracer/go_common.h @@ -27,12 +27,17 @@ #include +#include + #include char __license[] SEC("license") = "Dual MIT/GPL"; enum { W3C_KEY_LENGTH = 11, W3C_VAL_LENGTH = 55 }; +static unsigned char tp_encoded[] = { + 0x4d, 0x83, 0x21, 0x6b, 0x1d, 0x85, 0xa9, 0x3f}; // hpack encoded "traceparent" + // Temporary information about a function invocation. It stores the invocation time of a function // as well as the value of registers at the invocation time. This way we can retrieve them at the // return uprobes so we can know the values of the function arguments (which are passed as registers @@ -119,13 +124,6 @@ struct { __uint(max_entries, MAX_CONCURRENT_REQUESTS); } ongoing_sql_queries SEC(".maps"); -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: goroutine id - __type(value, mongo_go_client_req_t); // the request - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} ongoing_mongo_requests SEC(".maps"); - typedef struct grpc_header_field { u8 *key_ptr; u64 key_len; diff --git a/bpf/gotracer/go_grpc.c b/bpf/gotracer/go_grpc.c index 0bd21d930e..7bb0b65e24 100644 --- a/bpf/gotracer/go_grpc.c +++ b/bpf/gotracer/go_grpc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include @@ -826,7 +825,7 @@ int obi_uprobe_grpcFramerWriteHeaders_returns(struct pt_regs *ctx) { if (original_size > 0) { u8 type_byte = 0; const u8 key_len = - TP_ENCODED_LEN | 0x80; // high tagged to signify hpack encoded value + sizeof(tp_encoded) | 0x80; // high tagged to signify hpack encoded value const u8 val_len = TP_MAX_VAL_LENGTH; // We don't hpack encode the value of the traceparent field, because that will require that @@ -842,7 +841,7 @@ int obi_uprobe_grpcFramerWriteHeaders_returns(struct pt_regs *ctx) { // Write 'traceparent' encoded as hpack bpf_probe_write_user(buf_arr + (n & 0x0ffff), tp_encoded, sizeof(tp_encoded)); ; - n += TP_ENCODED_LEN; + n += sizeof(tp_encoded); // Write the length of the hpack encoded traceparent field bpf_probe_write_user(buf_arr + (n & 0x0ffff), &val_len, sizeof(val_len)); n++; diff --git a/bpf/gotracer/go_kafka_def.h b/bpf/gotracer/go_kafka_def.h deleted file mode 100644 index 786d1219c6..0000000000 --- a/bpf/gotracer/go_kafka_def.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific la -#pragma once - -#define KAFKA_API_FETCH 0 -#define KAFKA_API_PRODUCE 1 -#define KAFKA_API_KEY_POS 5 diff --git a/bpf/gotracer/go_kafka_go.c b/bpf/gotracer/go_kafka_go.c index 5ad9d278b1..caed14854a 100644 --- a/bpf/gotracer/go_kafka_go.c +++ b/bpf/gotracer/go_kafka_go.c @@ -17,58 +17,16 @@ #include +#include #include #include -#include -#include +#include + +#include -typedef struct produce_req { - u64 msg_ptr; - u64 conn_ptr; - u64 start_monotime_ns; -} produce_req_t; - -typedef struct topic { - char name[MAX_TOPIC_NAME_LEN]; - tp_info_t tp; -} topic_t; - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // w_ptr - __type(value, tp_info_t); // traceparent - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} produce_traceparents SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // goroutine - __type(value, topic_t); // topic info - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} ongoing_produce_topics SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // msg ptr - __type(value, topic_t); // topic info - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} ongoing_produce_messages SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // goroutine - __type(value, produce_req_t); // rw ptr + start time - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} produce_requests SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // goroutine - __type(value, kafka_go_req_t); // rw ptr + start time - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} fetch_requests SEC(".maps"); +#include // Code for the produce messages path SEC("uprobe/writer_write_messages") @@ -84,7 +42,12 @@ int obi_uprobe_writer_write_messages(struct pt_regs *ctx) { go_addr_key_t p_key = {}; go_addr_key_from_id(&p_key, w_ptr); + go_addr_key_t g_key = {}; + go_addr_key_from_id(&g_key, goroutine_addr); + bpf_map_update_elem(&produce_traceparents, &p_key, &tp, BPF_ANY); + bpf_map_update_elem(&produce_traceparents_by_goroutine, &g_key, &tp, BPF_ANY); + return 0; } @@ -223,7 +186,7 @@ int obi_uprobe_protocol_roundtrip_ret(struct pt_regs *ctx) { kafka_go_req_t *trace = bpf_ringbuf_reserve(&events, sizeof(kafka_go_req_t), 0); if (trace) { trace->type = EVENT_GO_KAFKA_SEG; - trace->op = KAFKA_API_PRODUCE; + trace->op = k_kafka_api_produce; trace->start_monotime_ns = p_ptr->start_monotime_ns; trace->end_monotime_ns = bpf_ktime_get_ns(); @@ -269,7 +232,7 @@ int obi_uprobe_reader_read(struct pt_regs *ctx) { if (r_ptr) { kafka_go_req_t r = { .type = EVENT_GO_KAFKA_SEG, - .op = KAFKA_API_FETCH, + .op = k_kafka_api_fetch, .start_monotime_ns = 0, }; diff --git a/bpf/gotracer/go_mongo.c b/bpf/gotracer/go_mongo.c index 8b69bd97bc..8560d0eab4 100644 --- a/bpf/gotracer/go_mongo.c +++ b/bpf/gotracer/go_mongo.c @@ -17,11 +17,14 @@ #include +#include #include #include #include +#include + #include #define MONGO_OP_DEF(name, str) \ diff --git a/bpf/gotracer/go_net.c b/bpf/gotracer/go_net.c index 5ae2ccaa96..c91b2ac72f 100644 --- a/bpf/gotracer/go_net.c +++ b/bpf/gotracer/go_net.c @@ -20,8 +20,12 @@ #include #include +#include + #include +#include + #include SEC("uprobe/netFdRead") @@ -85,4 +89,4 @@ int obi_uprobe_netFdRead(struct pt_regs *ctx) { } return 0; -} \ No newline at end of file +} diff --git a/bpf/gotracer/go_nethttp.c b/bpf/gotracer/go_nethttp.c index f30c2dc8c0..eb6ca4829d 100644 --- a/bpf/gotracer/go_nethttp.c +++ b/bpf/gotracer/go_nethttp.c @@ -27,8 +27,10 @@ #include #include #include -#include +#include + +#include #include #include @@ -39,64 +41,11 @@ #include -static const char traceparent[] = "traceparent: "; - static __always_inline unsigned char *tp_char_buf() { int zero = 0; return bpf_map_lookup_elem(&tp_char_buf_mem, &zero); } -typedef struct http_client_data { - s64 content_length; - pid_info pid; - unsigned char path[PATH_MAX_LEN]; - unsigned char host[HOST_MAX_LEN]; - unsigned char scheme[SCHEME_MAX_LEN]; - unsigned char method[METHOD_MAX_LEN]; - u8 _pad[3]; -} http_client_data_t; - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: pointer to the request goroutine - __type(value, http_client_data_t); - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} ongoing_http_client_requests_data SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: pointer to the request goroutine - __type(value, tp_info_t); - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} http2_server_requests_tp SEC(".maps"); - -typedef struct server_http_func_invocation { - u64 start_monotime_ns; - u64 content_length; - u64 response_length; - u64 status; - u64 rpc_request_addr; // pointer to the jsonrpc Request - tp_info_t tp; - u8 method[METHOD_MAX_LEN]; - u8 path[PATH_MAX_LEN]; - u8 pattern[PATTERN_MAX_LEN]; - u8 _pad[5]; -} server_http_func_invocation_t; - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: pointer to the request goroutine - __type(value, server_http_func_invocation_t); - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} ongoing_http_server_requests SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); - __type(key, u32); - __type(value, unsigned char[HTTP_HEADER_MAX_LEN]); - __uint(max_entries, 1); -} temp_header_mem_store SEC(".maps"); - static __always_inline unsigned char *temp_header_mem() { const u32 zero = 0; return bpf_map_lookup_elem(&temp_header_mem_store, &zero); @@ -636,13 +585,6 @@ int obi_uprobe_ServeHTTPReturns(struct pt_regs *ctx) { return serve_http_returns(ctx); } -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, void *); // key: pointer to the request header map - __type(value, u64); // the goroutine of the transport request - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} header_req_map SEC(".maps"); - /* HTTP Client. We expect to see HTTP client in both HTTP server and gRPC server calls.*/ static __always_inline void roundTripStartHelper(struct pt_regs *ctx) { void *goroutine_addr = GOROUTINE_PTR(ctx); @@ -1032,15 +974,6 @@ int obi_uprobe_http2serverConn_runHandler(struct pt_regs *ctx) { return 0; } -// HTTP 2.0 client support -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, stream_key_t); // key: stream id + connection info - // the goroutine of the round trip request, which is the key for our traceparent info - __type(value, u64); - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} http2_req_map SEC(".maps"); - static __always_inline void setup_http2_client_conn(void *goroutine_addr, void *cc_ptr, u32 stream_id, @@ -1145,23 +1078,6 @@ int obi_uprobe_http2WriteHeaders_vendored(struct pt_regs *ctx) { return 0; } -#define MAX_W_PTR_N 1024 - -typedef struct framer_func_invocation { - u64 framer_ptr; - tp_info_t tp; - s64 initial_n; -} framer_func_invocation_t; - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: go routine doing framer write headers - __type( - value, - framer_func_invocation_t); // the goroutine of the round trip request, which is the key for our traceparent info - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} framer_invocation_map SEC(".maps"); - static __always_inline void on_http2FramerWriteHeaders(struct pt_regs *ctx, off_table_t *ot, u64 stream_id) { if (!g_bpf_header_propagation) { @@ -1273,9 +1189,6 @@ int obi_uprobe_net_http2FramerWriteHeaders(struct pt_regs *ctx) { return 0; } -#define HTTP2_ENCODED_HEADER_LEN \ - 66 // 1 + 1 + 8 + 1 + 55 = type byte + hpack_len_as_byte("traceparent") + strlen(hpack("traceparent")) + len_as_byte(55) + generated traceparent id - SEC("uprobe/http2FramerWriteHeaders_returns") int obi_uprobe_http2FramerWriteHeaders_returns(struct pt_regs *ctx) { if (!g_bpf_header_propagation) { @@ -1348,7 +1261,7 @@ int obi_uprobe_http2FramerWriteHeaders_returns(struct pt_regs *ctx) { if (original_size > 0) { u8 type_byte = 0; const u8 key_len = - TP_ENCODED_LEN | 0x80; // high tagged to signify hpack encoded value + sizeof(tp_encoded) | 0x80; // high tagged to signify hpack encoded value const u8 val_len = TP_MAX_VAL_LENGTH; // We don't hpack encode the value of the traceparent field, because that will require that @@ -1364,7 +1277,7 @@ int obi_uprobe_http2FramerWriteHeaders_returns(struct pt_regs *ctx) { // Write 'traceparent' encoded as hpack bpf_probe_write_user(buf_arr + (n & 0x0ffff), tp_encoded, sizeof(tp_encoded)); ; - n += TP_ENCODED_LEN; + n += sizeof(tp_encoded); // Write the length of the hpack encoded traceparent field bpf_probe_write_user(buf_arr + (n & 0x0ffff), &val_len, sizeof(val_len)); n++; diff --git a/bpf/gotracer/go_redis.c b/bpf/gotracer/go_redis.c index 71f3ded91e..4467bfdbed 100644 --- a/bpf/gotracer/go_redis.c +++ b/bpf/gotracer/go_redis.c @@ -21,21 +21,9 @@ #include -#include +#include -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: goroutine id - __type(value, redis_client_req_t); // the request - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} ongoing_redis_requests SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: goroutine id - __type(value, void *); // the *Conn - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} redis_writes SEC(".maps"); +#include static __always_inline void setup_request(void *goroutine_addr) { redis_client_req_t req = { diff --git a/bpf/gotracer/go_runtime.c b/bpf/gotracer/go_runtime.c index e78a919b85..6cd8c2234f 100644 --- a/bpf/gotracer/go_runtime.c +++ b/bpf/gotracer/go_runtime.c @@ -19,11 +19,17 @@ #include -#include - #include +#include +#include +#include +#include #include +#include +#include +#include + #include #include @@ -217,16 +223,64 @@ int obi_uprobe_runtime_casgstatus(struct pt_regs *ctx) { .addr = (u64)g, .pid = pid, }; - grpc_srv_func_invocation_t *invocation; + + // grpc + grpc_srv_func_invocation_t *grpc_server_inv; + grpc_client_func_invocation_t *grpc_client_inv; + // http + server_http_func_invocation_t *http_server_inv; + // kafka_go + tp_info_t *kafka_go_tp; + // mongo + mongo_go_client_req_t *mongo; + // redis + redis_client_req_t *redis; + // sql + sql_func_invocation_t *sql; const u32 newval = (u32)(uintptr_t)GO_PARAM3(ctx); switch (newval) { case g_running: case g_syscall: - // grpc server - invocation = bpf_map_lookup_elem(&ongoing_grpc_server_requests, &g_key); - if (invocation) { - obi_ctx__set(g_pid_tgid, &invocation->tp); + // grpc + grpc_server_inv = bpf_map_lookup_elem(&ongoing_grpc_server_requests, &g_key); + if (grpc_server_inv) { + obi_ctx__set(g_pid_tgid, &grpc_server_inv->tp); + return 0; + } + grpc_client_inv = bpf_map_lookup_elem(&ongoing_grpc_client_requests, &g_key); + if (grpc_client_inv) { + obi_ctx__set(g_pid_tgid, &grpc_client_inv->tp); + return 0; + } + // http + http_server_inv = bpf_map_lookup_elem(&ongoing_http_server_requests, &g_key); + if (http_server_inv) { + obi_ctx__set(g_pid_tgid, &http_server_inv->tp); + return 0; + } + // kafka_go + kafka_go_tp = bpf_map_lookup_elem(&produce_traceparents_by_goroutine, &g_key); + if (kafka_go_tp) { + obi_ctx__set(g_pid_tgid, kafka_go_tp); + return 0; + } + // mongo + mongo = bpf_map_lookup_elem(&ongoing_mongo_requests, &g_key); + if (mongo) { + obi_ctx__set(g_pid_tgid, &mongo->tp); + return 0; + } + // redis + redis = bpf_map_lookup_elem(&ongoing_redis_requests, &g_key); + if (redis) { + obi_ctx__set(g_pid_tgid, &redis->tp); + return 0; + } + // sql + sql = bpf_map_lookup_elem(&ongoing_sql_queries, &g_key); + if (sql) { + obi_ctx__set(g_pid_tgid, &sql->tp); return 0; } diff --git a/bpf/gotracer/go_sarama.c b/bpf/gotracer/go_sarama.c index 9dded99cbf..5a9a95f045 100644 --- a/bpf/gotracer/go_sarama.c +++ b/bpf/gotracer/go_sarama.c @@ -20,23 +20,12 @@ #include #include -#include -#include +#include -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: correlation id - __type(value, kafka_client_req_t); - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} kafka_requests SEC(".maps"); +#include -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __type(key, go_addr_key_t); // key: goroutine id - __type(value, u32); // correlation id - __uint(max_entries, MAX_CONCURRENT_REQUESTS); -} ongoing_kafka_requests SEC(".maps"); +#include SEC("uprobe/sarama_sendInternal") int obi_uprobe_sarama_sendInternal(struct pt_regs *ctx) { @@ -88,12 +77,12 @@ int obi_uprobe_sarama_broker_write(struct pt_regs *ctx) { // the api key is 2 bytes, but num APIs at the moment is max 50. // instead of reading 2 bytes and then doing ntohs, we just read // the second byte of the api key, assuming the first is 0. - const u8 api_key = small_buf[KAFKA_API_KEY_POS]; + const u8 api_key = small_buf[k_kafka_api_key_pos]; bpf_dbg_printk("api_key=%d", api_key); // We only care about fetch and produce - if (api_key == KAFKA_API_FETCH || api_key == KAFKA_API_PRODUCE) { + if (api_key == k_kafka_api_fetch || api_key == k_kafka_api_produce) { u32 correlation_id = *invocation; kafka_client_req_t req = { .type = EVENT_GO_KAFKA, diff --git a/bpf/gotracer/hpack.h b/bpf/gotracer/hpack.h deleted file mode 100644 index 5fb73d2768..0000000000 --- a/bpf/gotracer/hpack.h +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include - -uint32_t huffman_codes[256] = { - 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7, - 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec, - 0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, - 0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb, - 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, - 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, - 0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc, - 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, - 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, - 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22, - 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, - 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, - 0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78, - 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc, - 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9, - 0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, - 0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3, - 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef, - 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, - 0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, - 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef, - 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1, - 0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, - 0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed, - 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2, - 0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, - 0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, - 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4, - 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, - 0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee, -}; - -uint8_t huffman_code_len[256] = { - 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 30, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, - 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, 13, 6, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, - 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 6, 7, 6, 5, 5, 6, 7, 7, - 7, 7, 7, 15, 11, 14, 13, 28, 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, - 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, 22, 21, 20, 22, 22, 23, 23, 21, - 23, 22, 22, 24, 21, 22, 23, 23, 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, - 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, 19, 21, 26, 27, 27, 26, 27, 24, - 21, 21, 26, 26, 28, 27, 27, 27, 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, - 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, -}; - -#define EOS_CODE (0x3fffffff) -#define EOS_N_BITS (30) -#define EOS_PAD_BYTE (EOS_CODE >> (EOS_N_BITS - 8)) - -#define TP_ENCODED_LEN 8 - -static unsigned char tp_encoded[TP_ENCODED_LEN] = { - 0x4d, 0x83, 0x21, 0x6b, 0x1d, 0x85, 0xa9, 0x3f}; // hpack encoded "traceparent" - -struct hpack_ctx { - uint8_t dst[TP_MAX_VAL_LENGTH]; - int32 dst_len; - uint8_t src[TP_MAX_VAL_LENGTH]; - uint64_t m_bytes; - uint32_t m_count; - int32 len; -}; - -static int encode_iter(u32 index, struct hpack_ctx *d) { - int len = d->len; - - if (len >= (TP_MAX_VAL_LENGTH - 4)) { - d->len = -1; - return 1; - } - - if (index >= TP_MAX_VAL_LENGTH) { - return 1; - } - - uint8_t b = d->src[index]; - - d->m_count += (uint32_t)(huffman_code_len[b]); - d->m_bytes <<= huffman_code_len[b] % 64; - d->m_bytes |= huffman_codes[b]; - if (d->m_count >= 32) { - d->m_count %= 32; - uint32_t y = (uint32_t)(d->m_bytes >> d->m_count); - d->dst[len] = (uint8_t)(y >> 24); - d->dst[len + 1] = (uint8_t)(y >> 16); - d->dst[len + 2] = (uint8_t)(y >> 8); - d->dst[len + 3] = (uint8_t)(y); - d->len += 4; - } - - return 0; -} - -static __always_inline int32_t hpack_encode_tp(struct hpack_ctx *d) { - uint32_t nr_loops = TP_MAX_VAL_LENGTH; - - bpf_loop(nr_loops, encode_iter, d, 0); - - int len = d->len; - - if (len < 0) { - return -1; - } - - if (len > (TP_MAX_VAL_LENGTH - 4)) { - return -1; - } - - uint32_t over = d->m_count % 8; - - if (over > 0 && over < 8) { - uint32_t pad = (8 - over) & 0x0ff; - d->m_bytes = (d->m_bytes << pad) | (EOS_PAD_BYTE >> over); - d->m_count += pad; // 8 now divides into n exactly - } - - uint32_t rem = (d->m_count / 8); - - // n in (0, 8, 16, 24, 32) - switch (rem) { - case 0: - return d->len; - case 1: - d->dst[len] = (uint8_t)(d->m_bytes); - d->len += 1; - return d->len; - case 2: { - uint16_t y = (uint16_t)(d->m_bytes); - d->dst[len] = (uint8_t)(y >> 8); - d->dst[len + 1] = (uint8_t)(y); - - d->len += 2; - - return d->len; - } - case 3: { - uint16_t y = (uint16_t)(d->m_bytes >> 8); - d->dst[len] = (uint8_t)(y >> 8); - d->dst[len + 1] = (uint8_t)(y); - d->dst[len + 2] = (uint8_t)(d->m_bytes); - d->len += 3; - return d->len; - } - default: - break; - } - // case 4: - uint32_t y = (uint32_t)(d->m_bytes); - d->dst[len] = (uint8_t)(y >> 24); - d->dst[len + 1] = (uint8_t)(y >> 16); - d->dst[len + 2] = (uint8_t)(y >> 8); - d->dst[len + 3] = (uint8_t)(y); - - d->len += 4; - - return d->len; -} diff --git a/bpf/gotracer/maps/kafka.h b/bpf/gotracer/maps/kafka.h new file mode 100644 index 0000000000..9df0c438f1 --- /dev/null +++ b/bpf/gotracer/maps/kafka.h @@ -0,0 +1,73 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include +#include +#include + +#include + +// Kafka Go + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // goroutine + __type(value, tp_info_t); // traceparent + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} produce_traceparents_by_goroutine SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // w_ptr + __type(value, tp_info_t); // traceparent + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} produce_traceparents SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // goroutine + __type(value, topic_t); // topic info + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} ongoing_produce_topics SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // msg ptr + __type(value, topic_t); // topic info + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} ongoing_produce_messages SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // goroutine + __type(value, produce_req_t); // rw ptr + start time + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} produce_requests SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // goroutine + __type(value, kafka_go_req_t); // rw ptr + start time + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} fetch_requests SEC(".maps"); + +// Sarama + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: correlation id + __type(value, kafka_client_req_t); + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} kafka_requests SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: goroutine id + __type(value, u32); // correlation id + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} ongoing_kafka_requests SEC(".maps"); diff --git a/bpf/gotracer/maps/mongo.h b/bpf/gotracer/maps/mongo.h new file mode 100644 index 0000000000..127e66009b --- /dev/null +++ b/bpf/gotracer/maps/mongo.h @@ -0,0 +1,18 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: goroutine id + __type(value, mongo_go_client_req_t); // the request + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} ongoing_mongo_requests SEC(".maps"); diff --git a/bpf/gotracer/maps/nethttp.h b/bpf/gotracer/maps/nethttp.h new file mode 100644 index 0000000000..cbc35e041d --- /dev/null +++ b/bpf/gotracer/maps/nethttp.h @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: pointer to the request goroutine + __type(value, http_client_data_t); + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} ongoing_http_client_requests_data SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: pointer to the request goroutine + __type(value, tp_info_t); + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} http2_server_requests_tp SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: pointer to the request goroutine + __type(value, server_http_func_invocation_t); + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} ongoing_http_server_requests SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, u32); + __type(value, unsigned char[HTTP_HEADER_MAX_LEN]); + __uint(max_entries, 1); +} temp_header_mem_store SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, void *); // key: pointer to the request header map + __type(value, u64); // the goroutine of the transport request + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} header_req_map SEC(".maps"); + +// HTTP 2.0 client support +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, stream_key_t); // key: stream id + connection info + // the goroutine of the round trip request, which is the key for our traceparent info + __type(value, u64); + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} http2_req_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: go routine doing framer write headers + __type( + value, + framer_func_invocation_t); // the goroutine of the round trip request, which is the key for our traceparent info + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} framer_invocation_map SEC(".maps"); diff --git a/bpf/gotracer/maps/redis.h b/bpf/gotracer/maps/redis.h new file mode 100644 index 0000000000..c8ce1783f0 --- /dev/null +++ b/bpf/gotracer/maps/redis.h @@ -0,0 +1,25 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: goroutine id + __type(value, redis_client_req_t); // the request + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} ongoing_redis_requests SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, go_addr_key_t); // key: goroutine id + __type(value, void *); // the *Conn + __uint(max_entries, MAX_CONCURRENT_REQUESTS); +} redis_writes SEC(".maps"); diff --git a/bpf/gotracer/types/kafka.h b/bpf/gotracer/types/kafka.h new file mode 100644 index 0000000000..c55569f75b --- /dev/null +++ b/bpf/gotracer/types/kafka.h @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include +#include + +enum { + k_kafka_api_fetch = 0, + k_kafka_api_produce = 1, + k_kafka_api_key_pos = 5, +}; + +typedef struct produce_req { + u64 msg_ptr; + u64 conn_ptr; + u64 start_monotime_ns; +} produce_req_t; + +typedef struct topic { + char name[MAX_TOPIC_NAME_LEN]; + tp_info_t tp; +} topic_t; diff --git a/bpf/gotracer/types/nethttp.h b/bpf/gotracer/types/nethttp.h new file mode 100644 index 0000000000..fe00a8e1bb --- /dev/null +++ b/bpf/gotracer/types/nethttp.h @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include +#include + +#include + +#define HTTP2_ENCODED_HEADER_LEN \ + 66 // 1 + 1 + 8 + 1 + 55 = type byte + hpack_len_as_byte("traceparent") + strlen(hpack("traceparent")) + len_as_byte(55) + generated traceparent id + +#define MAX_W_PTR_N 1024 + +static const char traceparent[] = "traceparent: "; + +typedef struct http_client_data { + s64 content_length; + pid_info pid; + unsigned char path[PATH_MAX_LEN]; + unsigned char host[HOST_MAX_LEN]; + unsigned char scheme[SCHEME_MAX_LEN]; + unsigned char method[METHOD_MAX_LEN]; + u8 _pad[3]; +} http_client_data_t; + +typedef struct server_http_func_invocation { + u64 start_monotime_ns; + u64 content_length; + u64 response_length; + u64 status; + u64 rpc_request_addr; // pointer to the jsonrpc Request + tp_info_t tp; + u8 method[METHOD_MAX_LEN]; + u8 path[PATH_MAX_LEN]; + u8 pattern[PATTERN_MAX_LEN]; + u8 _pad[5]; +} server_http_func_invocation_t; + +typedef struct framer_func_invocation { + u64 framer_ptr; + tp_info_t tp; + s64 initial_n; +} framer_func_invocation_t; diff --git a/devdocs/context-propagation.md b/devdocs/context-propagation.md index 7eefdb5b88..3c2677ca40 100644 --- a/devdocs/context-propagation.md +++ b/devdocs/context-propagation.md @@ -306,5 +306,5 @@ OBI allows injecting trace context into JSON logs. The following requirements mu - Linux kernel version **6.0 or later** (overwriting user memory requires a `UBUF`-type `iov_iter`) - `CAP_SYS_ADMIN` capability and permission to use `bpf_probe_write_user` (kernel security lockdown mode should be `[none]`) - The target application writes logs in **JSON format** -- The target application does **not** use async primitives (not supported yet) - BPFFS mounted at /sys/fs/bpf (or another mountpath configurable via `config.ebpf.bpf_fs_path`) +- Async primitives: only Go runtime is currently supported