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

Improve eBPF context propagation stability #368

Merged
merged 72 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
f380f11
Add WithPID InstrumentationOption
RonFed Sep 29, 2023
2133ddd
Update changelog
RonFed Sep 29, 2023
b7fa23d
Improve validation for inst config
RonFed Sep 30, 2023
254a80b
WIP
RonFed Sep 30, 2023
f93f386
more WIP
RonFed Oct 1, 2023
4470cb6
Remove exePath checks
RonFed Oct 1, 2023
00d0226
Fix lint
RonFed Oct 1, 2023
d82ad0a
remove traces-orig.json
RonFed Oct 1, 2023
b087541
.
RonFed Oct 1, 2023
a171403
Init log if not initialized in NewInstrumentation
RonFed Oct 2, 2023
aae2823
fix os.Exit
RonFed Oct 2, 2023
7ff2deb
Merge branch 'main' into add_WithPID
RonFed Oct 2, 2023
4b072cd
test
RonFed Oct 2, 2023
8dd135b
Test Madvise
RonFed Oct 3, 2023
d34c19f
More testing
RonFed Oct 3, 2023
0a7acd2
More testing...
RonFed Oct 3, 2023
f9125e2
....
RonFed Oct 3, 2023
fd73a5c
,,,,,
RonFed Oct 3, 2023
41b4d9c
....
RonFed Oct 3, 2023
3e6b20f
fix compilation
RonFed Oct 3, 2023
8aa390b
context_ptr fix
RonFed Oct 4, 2023
b4066bf
.,,
RonFed Oct 4, 2023
aa9d84f
.
RonFed Oct 4, 2023
c00e407
fix context propagation
RonFed Oct 4, 2023
2527b4e
Add padding
RonFed Oct 4, 2023
6527023
test no slice append
RonFed Oct 4, 2023
a87add1
debugging
RonFed Oct 4, 2023
ad6d428
more testing
RonFed Oct 5, 2023
3874781
...
RonFed Oct 5, 2023
d856c1f
test append to slice
RonFed Oct 5, 2023
2d8fb1e
Add validation in inject header for http instrumentation
RonFed Oct 5, 2023
8d0d8d7
fix context
RonFed Oct 5, 2023
a156b4f
http client is root?
RonFed Oct 5, 2023
8efd894
...
RonFed Oct 5, 2023
f6b2674
...
RonFed Oct 5, 2023
3f256fb
...
RonFed Oct 5, 2023
980e9e9
........
RonFed Oct 5, 2023
b70efbb
fff
RonFed Oct 5, 2023
519b6ca
use get_go_interface_instance
RonFed Oct 6, 2023
a80aa63
include go types in uprobe.h
RonFed Oct 6, 2023
b79b641
more context propagation
RonFed Oct 6, 2023
43053de
add include
RonFed Oct 6, 2023
18a8f79
checking if http client uprobe context was created
RonFed Oct 6, 2023
41bed4c
align get_consistent_key
RonFed Oct 6, 2023
cd3ebe5
return ctx_val
RonFed Oct 6, 2023
9bcfc45
test mlock
RonFed Oct 6, 2023
9ed2a9b
more testing
RonFed Oct 6, 2023
c842141
Test reducing the mmap size
RonFed Oct 7, 2023
48d6414
Increase back the pages allocated
RonFed Oct 7, 2023
a038935
refactor context prop
RonFed Oct 8, 2023
7cd9460
Add volataile const for global structs
RonFed Oct 8, 2023
c714202
Add get_go_string_from_user_ptr helper
RonFed Oct 8, 2023
4b1bb68
fix start time at grpc server
RonFed Oct 8, 2023
5f7b9c2
mem alloc fixup
RonFed Oct 8, 2023
f497a5d
reduce allocation
RonFed Oct 8, 2023
ef34a95
Add Allocater
RonFed Oct 9, 2023
4b7bc37
remove unused definition
RonFed Oct 9, 2023
1bf1fd0
add logic to stop_tracking_span
RonFed Oct 10, 2023
296f4fd
handle error at uprobe_return
RonFed Oct 10, 2023
a89548c
Remove root arg for uprobe_return
RonFed Oct 10, 2023
233b4ea
Merge branch 'main' into ebpf_context_propagation
RonFed Oct 10, 2023
eed4cd8
remove unused import
RonFed Oct 10, 2023
51f372a
Cleanup
RonFed Oct 10, 2023
692a391
Fix linter
RonFed Oct 10, 2023
c05fb44
Merge branch 'main' into ebpf_context_propagation
RonFed Oct 10, 2023
d90508f
Merge branch 'main' into ebpf_context_propagation
RonFed Oct 11, 2023
c73064d
Add error handling to avoid multiple map entries for the same uprobe
RonFed Oct 11, 2023
ca7f5ed
Improve stop_tracking_span to handle more cases
RonFed Oct 11, 2023
9801024
increase max concurrent spans capacity for maps
RonFed Oct 11, 2023
06116f2
remove printk's
RonFed Oct 11, 2023
f477690
Merge branch 'main' into ebpf_context_propagation
RonFed Oct 14, 2023
1e996bd
Fix stop_tracking_span
RonFed Oct 14, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http

- Fix bug in the `net/http` server instrumentation which always created a new span context. ([#266]https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/266)
- Fix runtime panic if OTEL_GO_AUTO_TARGET_EXE is not set. ([#339](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/339))
- Improve eBPF context propagation stability ([#368]https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/368)

### Deprecated

Expand Down
16 changes: 11 additions & 5 deletions instrumentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ var errUndefinedTarget = fmt.Errorf("undefined target Go binary, consider settin
// NewInstrumentation returns a new [Instrumentation] configured with the
// provided opts.
func NewInstrumentation(opts ...InstrumentationOption) (*Instrumentation, error) {
if log.Logger.IsZero() {
err := log.Init()
if err != nil {
return nil, err
}
}

c := newInstConfig(opts)
if err := c.validate(); err != nil {
return nil, err
Expand Down Expand Up @@ -84,12 +91,11 @@ func NewInstrumentation(opts ...InstrumentationOption) (*Instrumentation, error)
return nil, err
}

if log.Logger.IsZero() {
err := log.Init()
if err != nil {
return nil, err
}
allocDetails, err := process.Allocate(pid)
if err != nil {
return nil, err
}
td.AllocationDetails = allocDetails

log.Logger.V(0).Info(
"target process analysis completed",
Expand Down
6 changes: 6 additions & 0 deletions internal/include/alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ static __always_inline void *write_target_data(void *data, s32 size)

void *target = (void *)start;
size = bound_number(size, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
u64 page_offset = (u64)target & 0xFFF;
u64 dist_to_next_page = 4096 - page_offset;
if (dist_to_next_page < size)
{
target += dist_to_next_page;
}
long success = bpf_probe_write_user(target, data, size);
if (success == 0)
{
Expand Down
6 changes: 2 additions & 4 deletions internal/include/arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void *get_argument_by_stack(struct pt_regs *ctx, int index)
return ptr;
}

void *get_argument(struct pt_regs *ctx, int index)
void __always_inline *get_argument(struct pt_regs *ctx, int index)
{
if (is_registers_abi)
{
Expand All @@ -80,9 +80,7 @@ static __always_inline void *get_consistent_key(struct pt_regs *ctx, void *conte
return (void *)GOROUTINE(ctx);
}

void *ctx_ptr = 0;
bpf_probe_read(&ctx_ptr, sizeof(ctx_ptr), contextContext);
return ctx_ptr;
return contextContext;
}

#endif
79 changes: 74 additions & 5 deletions internal/include/go_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
#define _GO_CONTEXT_H_

#include "bpf_helpers.h"
#include "go_types.h"

#define MAX_DISTANCE 10
#define MAX_CONCURRENT_SPANS 1000

struct
{
Expand All @@ -41,6 +43,11 @@ static __always_inline void *get_parent_go_context(void *ctx, void *map) {
void *data = ctx;
for (int i = 0; i < MAX_DISTANCE; i++)
{
if (data == NULL)
{
break;
}

void *found_in_map = bpf_map_lookup_elem(map, &data);
if (found_in_map != NULL)
{
Expand Down Expand Up @@ -72,20 +79,82 @@ static __always_inline struct span_context *get_parent_span_context(void *ctx) {
return parent_sc;
}

static __always_inline void start_tracking_span(void *ctx, struct span_context *sc) {
bpf_map_update_elem(&tracked_spans, &ctx, sc, BPF_ANY);
bpf_map_update_elem(&tracked_spans_by_sc, sc, &ctx, BPF_ANY);
static __always_inline void start_tracking_span(void *contextContext, struct span_context *sc) {
long err = 0;
err = bpf_map_update_elem(&tracked_spans, &contextContext, sc, BPF_ANY);
if (err != 0)
{
bpf_printk("Failed to update tracked_spans map: %ld", err);
return;
}

err = bpf_map_update_elem(&tracked_spans_by_sc, sc, &contextContext, BPF_ANY);
if (err != 0)
{
bpf_printk("Failed to update tracked_spans_by_sc map: %ld", err);
return;
}
}

static __always_inline void stop_tracking_span(struct span_context *sc) {
static __always_inline void stop_tracking_span(struct span_context *sc, struct span_context *psc) {
if (sc == NULL) {
bpf_printk("stop_tracking_span: sc is null");
return;
}

void *ctx = bpf_map_lookup_elem(&tracked_spans_by_sc, sc);
if (ctx == NULL)
{
bpf_printk("stop_tracking_span: cant find span context");
return;
}

bpf_map_delete_elem(&tracked_spans, &ctx);
void *parent_ctx = ((psc == NULL) ? NULL : bpf_map_lookup_elem(&tracked_spans_by_sc, psc));
if (parent_ctx == NULL)
{
// No parent span, delete the context
bpf_map_delete_elem(&tracked_spans, ctx);
} else
{
void *ctx_val = 0;
bpf_probe_read_user(&ctx_val, sizeof(ctx_val), ctx);
void *parent_ctx_val = 0;
bpf_probe_read_user(&parent_ctx_val, sizeof(parent_ctx_val), parent_ctx);

if (ctx_val != parent_ctx_val)
{
// Parent with different context, delete the context
bpf_map_delete_elem(&tracked_spans, ctx);
} else {
// Parent with the same context, update the entry to point to the parent span
bpf_map_update_elem(&tracked_spans, ctx, psc, BPF_ANY);
}
}

bpf_map_delete_elem(&tracked_spans_by_sc, sc);
}

// Extract the go context.Context data pointer from the function arguments
// context_pos:
// The argument position of the context.Context data pointer
// In case the context.Context is passed as an argument,
// this is the argument index of the pointer (starting from 1).
// In case the context.Context is a member of a struct,
// this is the argument index of the struct pointer (starting from 1).
// context_offset:
// In case the context.Context is a member of a struct,
// this is the offset of the context.Context member in the struct.
// passed_as_arg:
// Indicates whether context.Context is passed as an argument or is a member of a struct
static __always_inline void *get_Go_context(void *ctx, int context_pos, const volatile u64 context_offset, bool passed_as_arg) {
void *arg = get_argument(ctx, context_pos);
if (passed_as_arg) {
return arg;
}
void *ctx_addr = get_go_interface_instance((void*)(arg + context_offset));
void *ctx_val = 0;
bpf_probe_read_user(&ctx_val, sizeof(ctx_val), ctx_addr);
return ctx_val;
}

#endif
60 changes: 58 additions & 2 deletions internal/include/go_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,25 @@ struct map_bucket {
void *overflow;
};

// In Go, interfaces are represented as a pair of pointers: a pointer to the
// interface data, and a pointer to the interface table.
// See: runtime.iface in https://golang.org/src/runtime/runtime2.go
static __always_inline void* get_go_interface_instance(void *iface)
{
return (void*)(iface + 8);
}

static __always_inline struct go_string write_user_go_string(char *str, u32 len)
{
// Copy chars to userspace
struct go_string new_string = {.str = NULL, .len = 0};
char *addr = write_target_data((void *)str, len);
if (addr == NULL) {
bpf_printk("write_user_go_string: failed to copy string to userspace");
return new_string;
}

// Build string struct in kernel space
struct go_string new_string = {};
new_string.str = addr;
new_string.len = len;

Expand All @@ -82,7 +94,7 @@ static __always_inline void append_item_to_slice(struct go_slice *slice, void *n
}
else
{
// No room on current array - copy to new one of size item_size * (len + 1)
//No room on current array - copy to new one of size item_size * (len + 1)
if (slice->len > MAX_DATA_SIZE || slice->len < 1)
{
return;
Expand Down Expand Up @@ -111,19 +123,63 @@ static __always_inline void append_item_to_slice(struct go_slice *slice, void *n
}

void *new_array = write_target_data(map_buff, new_array_size);
if (new_array == NULL)
{
bpf_printk("append_item_to_slice: failed to copy new array to userspace");
return;
}

// Update array
slice->array = new_array;
long success = bpf_probe_write_user(slice_user_ptr->array, &slice->array, sizeof(slice->array));
if (success != 0)
{
bpf_printk("append_item_to_slice: failed to update array pointer in userspace");
return;
}

// Update cap
slice->cap++;
success = bpf_probe_write_user(slice_user_ptr->cap, &slice->cap, sizeof(slice->cap));
if (success != 0)
{
bpf_printk("append_item_to_slice: failed to update cap in userspace");
return;
}
}

// Update len
slice->len++;
long success = bpf_probe_write_user(slice_user_ptr->len, &slice->len, sizeof(slice->len));
if (success != 0)
{
bpf_printk("append_item_to_slice: failed to update len in userspace");
return;
}
}

static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char *dst, u64 max_len)
{
if (user_str_ptr == NULL)
{
return false;
}

struct go_string user_str = {0};
long success = 0;
success = bpf_probe_read(&user_str, sizeof(struct go_string), user_str_ptr);
if (success != 0 || user_str.len < 1)
{
return false;
}

u64 size_to_read = user_str.len > max_len ? max_len : user_str.len;
success = bpf_probe_read(dst, size_to_read, user_str.str);
if (success != 0)
{
return false;
}

return true;
}
#endif
1 change: 0 additions & 1 deletion internal/include/span_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "utils.h"

#define SPAN_CONTEXT_STRING_SIZE 55
#define MAX_CONCURRENT_SPANS 100

struct span_context
{
Expand Down
32 changes: 18 additions & 14 deletions internal/include/uprobe.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "common.h"
#include "span_context.h"
#include "go_context.h"
#include "go_types.h"

#define BASE_SPAN_PROPERTIES \
u64 start_time; \
Expand All @@ -32,19 +33,22 @@
// 4. Submit the constructed event to the agent code using perf buffer events_map
// 5. Delete the span from the uprobe_context_map
// 6. Delete the span from the global active spans map
#define UPROBE_RETURN(name, event_type, ctx_struct_pos, ctx_struct_offset, uprobe_context_map, events_map) \
SEC("uprobe/##name##") \
int uprobe_##name##_Returns(struct pt_regs *ctx) { \
void *req_ptr = get_argument(ctx, ctx_struct_pos); \
void *key = get_consistent_key(ctx, (void *)(req_ptr + ctx_struct_offset)); \
void *req_ptr_map = bpf_map_lookup_elem(&uprobe_context_map, &key); \
event_type tmpReq = {}; \
bpf_probe_read(&tmpReq, sizeof(tmpReq), req_ptr_map); \
tmpReq.end_time = bpf_ktime_get_ns(); \
bpf_perf_event_output(ctx, &events_map, BPF_F_CURRENT_CPU, &tmpReq, sizeof(tmpReq)); \
bpf_map_delete_elem(&uprobe_context_map, &key); \
stop_tracking_span(&tmpReq.sc); \
return 0; \
}
#define UPROBE_RETURN(name, event_type, uprobe_context_map, events_map, context_pos, context_offset, passed_as_arg) \
SEC("uprobe/##name##") \
int uprobe_##name##_Returns(struct pt_regs *ctx) { \
void *ctx_address = get_Go_context(ctx, context_pos, context_offset, passed_as_arg); \
void *key = get_consistent_key(ctx, ctx_address); \
void *req_ptr_map = bpf_map_lookup_elem(&uprobe_context_map, &key); \
if (req_ptr_map == NULL) { \
return 0; \
} \
event_type tmpReq = {0}; \
bpf_probe_read(&tmpReq, sizeof(tmpReq), req_ptr_map); \
tmpReq.end_time = bpf_ktime_get_ns(); \
bpf_perf_event_output(ctx, &events_map, BPF_F_CURRENT_CPU, &tmpReq, sizeof(tmpReq)); \
bpf_map_delete_elem(&uprobe_context_map, &key); \
stop_tracking_span(&tmpReq.sc, &tmpReq.psc); \
return 0; \
}

#endif
13 changes: 13 additions & 0 deletions internal/include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,16 @@ static __always_inline void bpf_memset(unsigned char *dst, u32 size, unsigned ch
dst[i] = value;
}
}

static __always_inline bool bpf_is_zero(unsigned char *buff, u32 size)
{
for (int i = 0; i < size; i++)
{
if (buff[i] != 0)
{
return false;
}
}

return true;
}
8 changes: 3 additions & 5 deletions internal/pkg/instrumentors/bpf/database/sql/bpf/probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ int uprobe_queryDC(struct pt_regs *ctx) {
}

// Get parent if exists
void *context_ptr = get_argument(ctx, context_ptr_pos);
void *context_ptr_val = 0;
bpf_probe_read(&context_ptr_val, sizeof(context_ptr_val), context_ptr);
void *context_ptr_val = get_Go_context(ctx, 3, 0, true);
struct span_context *span_ctx = get_parent_span_context(context_ptr_val);
if (span_ctx != NULL) {
// Set the parent context
Expand All @@ -77,7 +75,7 @@ int uprobe_queryDC(struct pt_regs *ctx) {
}

// Get key
void *key = get_consistent_key(ctx, context_ptr);
void *key = get_consistent_key(ctx, context_ptr_val);

bpf_map_update_elem(&sql_events, &key, &sql_request, 0);
start_tracking_span(context_ptr_val, &sql_request.sc);
Expand All @@ -86,4 +84,4 @@ int uprobe_queryDC(struct pt_regs *ctx) {

// This instrumentation attaches uprobe to the following function:
// func (db *DB) queryDC(ctx, txctx context.Context, dc *driverConn, releaseConn func(error), query string, args []any)
UPROBE_RETURN(queryDC, struct sql_request_t, 3, 0, sql_events, events)
UPROBE_RETURN(queryDC, struct sql_request_t, sql_events, events, 3, 0, true)
Loading
Loading