@@ -50,6 +50,14 @@ struct
50
50
__uint (max_entries , MAX_CONCURRENT );
51
51
} http_server_uprobes SEC (".maps" );
52
52
53
+ struct
54
+ {
55
+ __uint (type , BPF_MAP_TYPE_LRU_HASH );
56
+ __type (key , void * );
57
+ __type (value , struct span_context );
58
+ __uint (max_entries , MAX_CONCURRENT );
59
+ } http_server_context_headers SEC (".maps" );
60
+
53
61
struct
54
62
{
55
63
__uint (type , BPF_MAP_TYPE_PERCPU_ARRAY );
@@ -84,11 +92,13 @@ volatile const bool pattern_path_supported;
84
92
// In case pattern handlers are supported the following offsets will be used:
85
93
volatile const u64 req_pat_pos ;
86
94
volatile const u64 pat_str_pos ;
95
+ // A flag indicating whether the Go version is using swiss maps
96
+ volatile const bool swiss_maps_used ;
87
97
88
98
// Extracts the span context from the request headers by looking for the 'traceparent' header.
89
99
// Fills the parent_span_context with the extracted span context.
90
100
// Returns 0 on success, negative value on error.
91
- static __always_inline long extract_context_from_req_headers (void * headers_ptr_ptr , struct span_context * parent_span_context )
101
+ static __always_inline long extract_context_from_req_headers_go_map (void * headers_ptr_ptr , struct span_context * parent_span_context )
92
102
{
93
103
void * headers_ptr ;
94
104
long res ;
@@ -178,6 +188,23 @@ static __always_inline long extract_context_from_req_headers(void *headers_ptr_p
178
188
return -1 ;
179
189
}
180
190
191
+ static __always_inline long extract_context_from_req_headers_pre_parsed (void * key , struct span_context * parent_span_context ) {
192
+ struct span_context * parsed_header_context = bpf_map_lookup_elem (& http_server_context_headers , & key );
193
+ if (!parsed_header_context ) {
194
+ return -1 ;
195
+ }
196
+
197
+ __builtin_memcpy (parent_span_context , parsed_header_context , sizeof (struct span_context ));
198
+ return 0 ;
199
+ }
200
+
201
+ static __always_inline long extract_context_from_req_headers (void * key , struct span_context * parent_span_context ) {
202
+ if (swiss_maps_used ) {
203
+ return extract_context_from_req_headers_pre_parsed (key , parent_span_context );
204
+ }
205
+ return extract_context_from_req_headers_go_map (key , parent_span_context );
206
+ }
207
+
181
208
static __always_inline void read_go_string (void * base , int offset , char * output , int maxLen , const char * errorMsg ) {
182
209
void * ptr = (void * )(base + offset );
183
210
if (!get_go_string_from_user_ptr (ptr , output , maxLen )) {
@@ -225,8 +252,17 @@ int uprobe_serverHandler_ServeHTTP(struct pt_regs *ctx)
225
252
.psc = & http_server_span -> psc ,
226
253
.sc = & http_server_span -> sc ,
227
254
.get_parent_span_context_fn = extract_context_from_req_headers ,
228
- .get_parent_span_context_arg = (void * )(req_ptr + headers_ptr_pos ),
229
255
};
256
+
257
+ // If Go is using swiss maps, we currently rely on the uretprobe setup
258
+ // on readContinuedLineSlice to store the parsed value in a map, which
259
+ // we query with the same goroutine/context key.
260
+ if (swiss_maps_used ) {
261
+ start_span_params .get_parent_span_context_arg = key ;
262
+ } else {
263
+ start_span_params .get_parent_span_context_arg = (void * )(req_ptr + headers_ptr_pos );
264
+ }
265
+
230
266
start_span (& start_span_params );
231
267
232
268
bpf_map_update_elem (& http_server_uprobes , & key , uprobe_data , 0 );
@@ -246,6 +282,7 @@ int uprobe_serverHandler_ServeHTTP_Returns(struct pt_regs *ctx) {
246
282
struct uprobe_data_t * uprobe_data = bpf_map_lookup_elem (& http_server_uprobes , & key );
247
283
if (uprobe_data == NULL ) {
248
284
bpf_printk ("uprobe/HandlerFunc_ServeHTTP_Returns: entry_state is NULL" );
285
+ bpf_map_delete_elem (& http_server_context_headers , & key );
249
286
return 0 ;
250
287
}
251
288
@@ -280,5 +317,31 @@ int uprobe_serverHandler_ServeHTTP_Returns(struct pt_regs *ctx) {
280
317
281
318
stop_tracking_span (& http_server_span -> sc , & http_server_span -> psc );
282
319
bpf_map_delete_elem (& http_server_uprobes , & key );
320
+ bpf_map_delete_elem (& http_server_context_headers , & key );
321
+ return 0 ;
322
+ }
323
+
324
+ // This instrumentation attaches uprobe to the following function:
325
+ // func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) {
326
+ SEC ("uprobe/textproto_Reader_readContinuedLineSlice" )
327
+ int uprobe_textproto_Reader_readContinuedLineSlice_Returns (struct pt_regs * ctx ) {
328
+ struct go_iface go_context = {0 };
329
+ get_Go_context (ctx , 4 , ctx_ptr_pos , false, & go_context );
330
+ void * key = get_consistent_key (ctx , go_context .data );
331
+
332
+ u64 len = (u64 )GO_PARAM2 (ctx );
333
+ u8 * buf = (u8 * )GO_PARAM1 (ctx );
334
+
335
+ if (len >= (W3C_KEY_LENGTH + W3C_VAL_LENGTH + 2 )) {
336
+ u8 temp [W3C_KEY_LENGTH + W3C_VAL_LENGTH + 2 ];
337
+ bpf_probe_read (temp , sizeof (temp ), buf );
338
+
339
+ if (!bpf_memicmp ((const char * )temp , "traceparent: " , W3C_KEY_LENGTH + 2 )) {
340
+ struct span_context parent_span_context = {};
341
+ w3c_string_to_span_context ((char * )(temp + W3C_KEY_LENGTH + 2 ), & parent_span_context );
342
+ bpf_map_update_elem (& http_server_context_headers , & key , & parent_span_context , BPF_ANY );
343
+ }
344
+ }
345
+
283
346
return 0 ;
284
347
}
0 commit comments