From 989bb43b8de9924cd2087b5a6e4399036a7e61c2 Mon Sep 17 00:00:00 2001 From: Ron Federman <73110295+RonFed@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:19:53 +0200 Subject: [PATCH] otelglobal: add support for v1.32.0 of otel-go (#1302) * otelglobal: add support for v1.32.0 of otel-go * Add changelog entry * add comment --------- Co-authored-by: Tyler Yahn --- CHANGELOG.md | 1 + .../otel/traceglobal/bpf/probe.bpf.c | 99 +++++++++++++++++-- .../otel/traceglobal/probe.go | 23 ++++- internal/test/e2e/otelglobal/go.mod | 6 +- internal/test/e2e/otelglobal/go.sum | 12 +-- 5 files changed, 121 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18ac243b8..3dbdc12b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http - Support Go standard libraries for 1.22.9 and 1.23.3. ([#1250](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1250)) - Support `google.golang.org/grpc` `1.68.0`. ([#1251](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1251)) - Support `golang.org/x/net` `0.31.0`. ([#1254](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1254)) +- Support `go.opentelemetry.io/otel@v1.32.0`. ([#1302](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1302)) ### Fixed diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf/probe.bpf.c index 349d24b56..1cdc2d859 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf/probe.bpf.c @@ -55,17 +55,26 @@ typedef struct go_tracer_id_partial { struct go_string version; } go_tracer_id_partial_t; -typedef struct go_tracer_id_full { +typedef struct go_tracer_with_schema { struct go_string name; struct go_string version; struct go_string schema_url; -} go_tracer_id_full_t; +} go_tracer_with_schema_t; + +typedef struct go_tracer_with_scope_attributes { + struct go_string name; + struct go_string version; + struct go_string schema_url; + go_iface_t scope_attributes; +} go_tracer_with_scope_attributes_t; + typedef void* go_tracer_ptr; // tracerProvider contains a map of tracers MAP_BUCKET_DEFINITION(go_tracer_id_partial_t, go_tracer_ptr) -MAP_BUCKET_DEFINITION(go_tracer_id_full_t, go_tracer_ptr) +MAP_BUCKET_DEFINITION(go_tracer_with_schema_t, go_tracer_ptr) +MAP_BUCKET_DEFINITION(go_tracer_with_scope_attributes_t, go_tracer_ptr) struct { __uint(type, BPF_MAP_TYPE_HASH); @@ -100,7 +109,7 @@ struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(key_size, sizeof(u32)); - __uint(value_size, sizeof(MAP_BUCKET_TYPE(go_tracer_id_full_t, go_tracer_ptr))); + __uint(value_size, sizeof(MAP_BUCKET_TYPE(go_tracer_with_scope_attributes_t, go_tracer_ptr))); __uint(max_entries, 1); } golang_mapbucket_storage_map SEC(".maps"); @@ -130,6 +139,7 @@ volatile const u64 tracer_provider_tracers_pos; volatile const u64 buckets_ptr_pos; volatile const bool tracer_id_contains_schemaURL; +volatile const bool tracer_id_contains_scope_attributes; // read_span_name reads the span name from the provided span_name_ptr and stores the result in // span_name.buf. @@ -202,7 +212,72 @@ static __always_inline long fill_partial_tracer_id_from_tracers_map(void *tracer return 0; } -static __always_inline long fill_full_tracer_id_from_tracers_map(void *tracers_map, go_tracer_ptr tracer, tracer_id_t *tracer_id) { +static __always_inline long fill_tracer_id_with_schema_from_tracers_map(void *tracers_map, go_tracer_ptr tracer, tracer_id_t *tracer_id) { + u64 tracers_count = 0; + long res = 0; + res = bpf_probe_read(&tracers_count, sizeof(tracers_count), tracers_map); + if (res < 0) + { + return -1; + } + if (tracers_count == 0) + { + return -1; + } + unsigned char log_2_bucket_count; + res = bpf_probe_read(&log_2_bucket_count, sizeof(log_2_bucket_count), tracers_map + 9); + if (res < 0) + { + return -1; + } + u64 bucket_count = 1 << log_2_bucket_count; + void *buckets_array; + res = bpf_probe_read(&buckets_array, sizeof(buckets_array), (void*)(tracers_map + buckets_ptr_pos)); + if (res < 0) + { + return -1; + } + u32 map_id = 0; + MAP_BUCKET_TYPE(go_tracer_with_schema_t, go_tracer_ptr) *map_bucket = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id); + if (!map_bucket) + { + return -1; + } + + for (u64 j = 0; j < MAX_BUCKETS; j++) + { + if (j >= bucket_count) + { + break; + } + res = bpf_probe_read(map_bucket, sizeof(MAP_BUCKET_TYPE(go_tracer_with_schema_t, go_tracer_ptr)), buckets_array + (j * sizeof(MAP_BUCKET_TYPE(go_tracer_with_schema_t, go_tracer_ptr)))); + if (res < 0) + { + continue; + } + for (u64 i = 0; i < 8; i++) + { + if (map_bucket->tophash[i] == 0) + { + continue; + } + if (map_bucket->values[i] == NULL) + { + continue; + } + if (map_bucket->values[i] != tracer) + { + continue; + } + get_go_string_from_user_ptr(&map_bucket->keys[i].version, tracer_id->version, MAX_TRACER_VERSION_LEN); + get_go_string_from_user_ptr(&map_bucket->keys[i].schema_url, tracer_id->schema_url, MAX_TRACER_SCHEMA_URL_LEN); + return 0; + } + } + return 0; +} + +static __always_inline long fill_tracer_id_with_scope_attributes_from_tracers_map(void *tracers_map, go_tracer_ptr tracer, tracer_id_t *tracer_id) { u64 tracers_count = 0; long res = 0; res = bpf_probe_read(&tracers_count, sizeof(tracers_count), tracers_map); @@ -228,7 +303,7 @@ static __always_inline long fill_full_tracer_id_from_tracers_map(void *tracers_m return -1; } u32 map_id = 0; - MAP_BUCKET_TYPE(go_tracer_id_full_t, go_tracer_ptr) *map_bucket = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id); + MAP_BUCKET_TYPE(go_tracer_with_scope_attributes_t, go_tracer_ptr) *map_bucket = bpf_map_lookup_elem(&golang_mapbucket_storage_map, &map_id); if (!map_bucket) { return -1; @@ -240,7 +315,7 @@ static __always_inline long fill_full_tracer_id_from_tracers_map(void *tracers_m { break; } - res = bpf_probe_read(map_bucket, sizeof(MAP_BUCKET_TYPE(go_tracer_id_full_t, go_tracer_ptr)), buckets_array + (j * sizeof(MAP_BUCKET_TYPE(go_tracer_id_full_t, go_tracer_ptr)))); + res = bpf_probe_read(map_bucket, sizeof(MAP_BUCKET_TYPE(go_tracer_with_scope_attributes_t, go_tracer_ptr)), buckets_array + (j * sizeof(MAP_BUCKET_TYPE(go_tracer_with_schema_t, go_tracer_ptr)))); if (res < 0) { continue; @@ -293,7 +368,15 @@ static __always_inline long fill_tracer_id(tracer_id_t *tracer_id, go_tracer_ptr } if (tracer_id_contains_schemaURL) { - res = fill_full_tracer_id_from_tracers_map(tracers_map, tracer, tracer_id); + // version of otel-go is 1.28.0 or higher + if (tracer_id_contains_scope_attributes) { + // version of otel-go is 1.32.0 or higher + // we don't collect the scope attributes, but we need to take their presence into account, + // when parsing the map bucket + res = fill_tracer_id_with_scope_attributes_from_tracers_map(tracers_map, tracer, tracer_id); + } else { + res = fill_tracer_id_with_schema_from_tracers_map(tracers_map, tracer, tracer_id); + } } else { res = fill_partial_tracer_id_from_tracers_map(tracers_map, tracer, tracer_id); } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go index fae0785d6..b8d8ef1f5 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go @@ -105,6 +105,7 @@ func New(logger *slog.Logger) probe.Probe { Val: structfield.NewID("std", "runtime", "hmap", "buckets"), }, tracerIDContainsSchemaURL{}, + tracerIDContainsScopeAttributes{}, }, Uprobes: []probe.Uprobe{ { @@ -138,13 +139,13 @@ func New(logger *slog.Logger) probe.Probe { } } -// framePosConst is a Probe Const defining whether the tracer key contains schemaURL. +// tracerIDContainsSchemaURL is a Probe Const defining whether the tracer key contains schemaURL. type tracerIDContainsSchemaURL struct{} // Prior to v1.28 the tracer key did not contain schemaURL. However, in that version a // change was made to include it. // https://github.com/open-telemetry/opentelemetry-go/pull/5426/files -var paramChangeVer = version.Must(version.NewVersion("1.28.0")) +var schemaAddedToTracerKeyVer = version.Must(version.NewVersion("1.28.0")) func (c tracerIDContainsSchemaURL) InjectOption(td *process.TargetDetails) (inject.Option, error) { ver, ok := td.Libraries["go.opentelemetry.io/otel"] @@ -152,7 +153,23 @@ func (c tracerIDContainsSchemaURL) InjectOption(td *process.TargetDetails) (inje return nil, fmt.Errorf("unknown module version: %s", pkg) } - return inject.WithKeyValue("tracer_id_contains_schemaURL", ver.GreaterThanOrEqual(paramChangeVer)), nil + return inject.WithKeyValue("tracer_id_contains_schemaURL", ver.GreaterThanOrEqual(schemaAddedToTracerKeyVer)), nil +} + +// In v1.32.0 the tracer key was updated to include the scope attributes. +// https://github.com/open-telemetry/opentelemetry-go/pull/5924/files +var scopeAttributesAddedToTracerKeyVer = version.Must(version.NewVersion("1.32.0")) + +// tracerIDContainsScopeAttributes is a Probe Const defining whether the tracer key contains scope attributes. +type tracerIDContainsScopeAttributes struct{} + +func (c tracerIDContainsScopeAttributes) InjectOption(td *process.TargetDetails) (inject.Option, error) { + ver, ok := td.Libraries["go.opentelemetry.io/otel"] + if !ok { + return nil, fmt.Errorf("unknown module version: %s", pkg) + } + + return inject.WithKeyValue("tracer_id_contains_scope_attributes", ver.GreaterThanOrEqual(scopeAttributesAddedToTracerKeyVer)), nil } type attributeKeyVal struct { diff --git a/internal/test/e2e/otelglobal/go.mod b/internal/test/e2e/otelglobal/go.mod index 744cfd94c..1e7e3a7f3 100644 --- a/internal/test/e2e/otelglobal/go.mod +++ b/internal/test/e2e/otelglobal/go.mod @@ -3,12 +3,12 @@ module go.opentelemetry.io/auto/internal/test/e2e/otelglobal go 1.22.0 require ( - go.opentelemetry.io/otel v1.31.0 - go.opentelemetry.io/otel/trace v1.31.0 + go.opentelemetry.io/otel v1.32.0 + go.opentelemetry.io/otel/trace v1.32.0 ) require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect ) diff --git a/internal/test/e2e/otelglobal/go.sum b/internal/test/e2e/otelglobal/go.sum index 4dbc307de..c1f92a636 100644 --- a/internal/test/e2e/otelglobal/go.sum +++ b/internal/test/e2e/otelglobal/go.sum @@ -11,11 +11,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= -go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=