From 959be3cfb3c59c7740a8ce401a08019521cfcf5f Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 10 Nov 2023 15:07:14 +0200 Subject: [PATCH 01/33] Initial instrumentation of otel sdk trace functions --- examples/go.mod | 11 +- examples/go.sum | 18 +- examples/rolldice/main.go | 106 ++++++++- .../otel/sdk/trace/bpf/probe.bpf.c | 109 +++++++++ .../otel/sdk/trace/bpf_bpfel_arm64.go | 151 ++++++++++++ .../otel/sdk/trace/bpf_bpfel_x86.go | 151 ++++++++++++ .../otel/sdk/trace/probe.go | 224 ++++++++++++++++++ .../otel/sdk/trace/probe_test.go | 67 ++++++ internal/pkg/instrumentation/manager.go | 2 + 9 files changed, 836 insertions(+), 3 deletions(-) create mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c create mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go create mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go create mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go create mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe_test.go diff --git a/examples/go.mod b/examples/go.mod index 18c23650d..2eb697d21 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -4,7 +4,16 @@ go 1.20 require ( github.com/mattn/go-sqlite3 v1.14.18 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/sdk v1.19.0 go.uber.org/zap v1.26.0 ) -require go.uber.org/multierr v1.10.0 // indirect +require ( + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/sys v0.12.0 // indirect +) diff --git a/examples/go.sum b/examples/go.sum index be6fb9d2d..ffc7ec3dc 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -1,11 +1,27 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/examples/rolldice/main.go b/examples/rolldice/main.go index f9342c05b..52cd334af 100644 --- a/examples/rolldice/main.go +++ b/examples/rolldice/main.go @@ -15,12 +15,23 @@ package main import ( + "context" + "errors" "fmt" "math/rand" "net/http" + "os" + "os/signal" "time" "go.uber.org/zap" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" ) // Server is Http server that exposes multiple endpoints. @@ -28,6 +39,75 @@ type Server struct { rand *rand.Rand } +var ( + tracer = otel.Tracer("rolldice") +) + +// setupOTelSDK bootstraps the OpenTelemetry pipeline. +// If it does not return an error, make sure to call shutdown for proper cleanup. +func setupOTelSDK(ctx context.Context, serviceName, serviceVersion string) (shutdown func(context.Context) error, err error) { + var shutdownFuncs []func(context.Context) error + + // shutdown calls cleanup functions registered via shutdownFuncs. + // The errors from the calls are joined. + // Each registered cleanup will be invoked once. + shutdown = func(ctx context.Context) error { + var err error + for _, fn := range shutdownFuncs { + err = errors.Join(err, fn(ctx)) + } + shutdownFuncs = nil + return err + } + + // handleErr calls shutdown for cleanup and makes sure that all errors are returned. + handleErr := func(inErr error) { + err = errors.Join(inErr, shutdown(ctx)) + } + + // Set up resource. + res, err := newResource(serviceName, serviceVersion) + if err != nil { + handleErr(err) + return + } + + // Set up propagator. + prop := newPropagator() + otel.SetTextMapPropagator(prop) + + // Set up trace provider. + tracerProvider, err := newTraceProvider(res) + if err != nil { + handleErr(err) + return + } + shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown) + otel.SetTracerProvider(tracerProvider) + + return +} + +func newResource(serviceName, serviceVersion string) (*resource.Resource, error) { + return resource.NewWithAttributes(semconv.SchemaURL, + semconv.ServiceName(serviceName), + semconv.ServiceVersion(serviceVersion)), nil +} + +func newPropagator() propagation.TextMapPropagator { + return propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ) +} + +func newTraceProvider(res *resource.Resource) (*trace.TracerProvider, error) { + traceProvider := trace.NewTracerProvider( + trace.WithResource(res), + ) + return traceProvider, nil +} + // NewServer creates a server struct after initialing rand. func NewServer() *Server { rd := rand.New(rand.NewSource(time.Now().Unix())) @@ -36,8 +116,14 @@ func NewServer() *Server { } } -func (s *Server) rolldice(w http.ResponseWriter, _ *http.Request) { +func (s *Server) rolldice(w http.ResponseWriter, r *http.Request) { + _, span := tracer.Start(r.Context(), "roll") + defer span.End() n := s.rand.Intn(6) + 1 + + rollValueAttr := attribute.Int("roll.value", n) + span.SetAttributes(rollValueAttr) + logger.Info("rolldice called", zap.Int("dice", n)) fmt.Fprintf(w, "%v", n) } @@ -57,6 +143,24 @@ func main() { fmt.Printf("error creating zap logger, error:%v", err) return } + + // Handle SIGINT (CTRL+C) gracefully. + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + defer stop() + + // Set up OpenTelemetry. + serviceName := "dice" + serviceVersion := "0.1.0" + otelShutdown, err := setupOTelSDK(ctx, serviceName, serviceVersion) + if err != nil { + fmt.Printf("error setting up otel sdk, error:%v", err) + return + } + // Handle shutdown properly so nothing leaks. + defer func() { + err = errors.Join(err, otelShutdown(context.Background())) + }() + port := fmt.Sprintf(":%d", 8080) logger.Info("starting http server", zap.String("port", port)) diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c new file mode 100644 index 000000000..ac3d619f1 --- /dev/null +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c @@ -0,0 +1,109 @@ +// Copyright The OpenTelemetry Authors +// +// 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 language governing permissions and +// limitations under the License. + +#include "arguments.h" +#include "span_context.h" +#include "go_context.h" +#include "go_types.h" +#include "uprobe.h" + +char __license[] SEC("license") = "Dual MIT/GPL"; + +#define MAX_CONCURRENT 50 + +struct otel_span_t { + BASE_SPAN_PROPERTIES +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, void*); + __type(value, struct otel_span_t); + __uint(max_entries, MAX_CONCURRENT); +} active_spans SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); +} events SEC(".maps"); + +// This instrumentation attaches uprobe to the following function: +// func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) +// SEC("uprobe/Start") +// int uprobe_Start(struct pt_regs *ctx) { +// struct otel_span_t otel_span = {0}; +// otel_span.start_time = bpf_ktime_get_ns(); + +// // Get parent if exists +// 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 +// bpf_probe_read(&otel_span.psc, sizeof(otel_span.psc), span_ctx); +// copy_byte_arrays(otel_span.psc.TraceID, otel_span.sc.TraceID, TRACE_ID_SIZE); +// generate_random_bytes(otel_span.sc.SpanID, SPAN_ID_SIZE); +// } else { +// otel_span.sc = generate_span_context(); +// } + +// // Get key +// void *key = get_consistent_key(ctx, context_ptr_val); + +// bpf_map_update_elem(&active_spans, &key, &otel_span, 0); +// start_tracking_span(context_ptr_val, &otel_span.sc); +// return 0; +// } + +// This instrumentation attaches uprobe to the following function: +// func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) +SEC("uprobe/Start") +int uprobe_Start_Returns(struct pt_regs *ctx) { + struct otel_span_t otel_span = {0}; + otel_span.start_time = bpf_ktime_get_ns(); + + // Get the returned context and Span (concrete type of the interfaces) + void *context_ptr_val = get_argument(ctx, 2); + void *span_ptr_val = get_argument(ctx, 4); + + struct span_context *span_ctx = get_parent_span_context(context_ptr_val); + if (span_ctx != NULL) { + // Set the parent context + bpf_probe_read(&otel_span.psc, sizeof(otel_span.psc), span_ctx); + copy_byte_arrays(otel_span.psc.TraceID, otel_span.sc.TraceID, TRACE_ID_SIZE); + generate_random_bytes(otel_span.sc.SpanID, SPAN_ID_SIZE); + } else { + otel_span.sc = generate_span_context(); + } + + bpf_map_update_elem(&active_spans, &span_ptr_val, &otel_span, 0); + start_tracking_span(context_ptr_val, &otel_span.sc); + return 0; +} + + +// This instrumentation attaches uprobe to the following function: +// unc (s *recordingSpan) End(options ...trace.SpanEndOption) +SEC("uprobe/End") +int uprobe_End(struct pt_regs *ctx) { + void *recording_span_ptr = get_argument(ctx, 1); + struct otel_span_t *span = bpf_map_lookup_elem(&active_spans, &recording_span_ptr); + if (span == NULL) { + return 0; + } + span->end_time = bpf_ktime_get_ns(); + bpf_map_delete_elem(&active_spans, &recording_span_ptr); + stop_tracking_span(&span->sc, &span->psc); + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, span, sizeof(*span)); + return 0; +} \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go new file mode 100644 index 000000000..555f52e33 --- /dev/null +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go @@ -0,0 +1,151 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build arm64 + +package sdk + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +type bpfOtelSpanT struct { + StartTime uint64 + EndTime uint64 + Sc bpfSpanContext + Psc bpfSpanContext +} + +type bpfSliceArrayBuff struct{ Buff [1024]uint8 } + +type bpfSpanContext struct { + TraceID [16]uint8 + SpanID [8]uint8 +} + +// loadBpf returns the embedded CollectionSpec for bpf. +func loadBpf() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_BpfBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load bpf: %w", err) + } + + return spec, err +} + +// loadBpfObjects loads bpf and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *bpfObjects +// *bpfPrograms +// *bpfMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadBpf() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// bpfSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfSpecs struct { + bpfProgramSpecs + bpfMapSpecs +} + +// bpfSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfProgramSpecs struct { + UprobeEnd *ebpf.ProgramSpec `ebpf:"uprobe_End"` + UprobeStartReturns *ebpf.ProgramSpec `ebpf:"uprobe_Start_Returns"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` +} + +// bpfObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfObjects struct { + bpfPrograms + bpfMaps +} + +func (o *bpfObjects) Close() error { + return _BpfClose( + &o.bpfPrograms, + &o.bpfMaps, + ) +} + +// bpfMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfMaps struct { + ActiveSpans *ebpf.Map `ebpf:"active_spans"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.ActiveSpans, + m.AllocMap, + m.Events, + m.SliceArrayBuffMap, + m.TrackedSpans, + m.TrackedSpansBySc, + ) +} + +// bpfPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfPrograms struct { + UprobeEnd *ebpf.Program `ebpf:"uprobe_End"` + UprobeStartReturns *ebpf.Program `ebpf:"uprobe_Start_Returns"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.UprobeEnd, + p.UprobeStartReturns, + ) +} + +func _BpfClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed bpf_bpfel_arm64.o +var _BpfBytes []byte diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go new file mode 100644 index 000000000..13d142727 --- /dev/null +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go @@ -0,0 +1,151 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build 386 || amd64 + +package sdk + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +type bpfOtelSpanT struct { + StartTime uint64 + EndTime uint64 + Sc bpfSpanContext + Psc bpfSpanContext +} + +type bpfSliceArrayBuff struct{ Buff [1024]uint8 } + +type bpfSpanContext struct { + TraceID [16]uint8 + SpanID [8]uint8 +} + +// loadBpf returns the embedded CollectionSpec for bpf. +func loadBpf() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_BpfBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load bpf: %w", err) + } + + return spec, err +} + +// loadBpfObjects loads bpf and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *bpfObjects +// *bpfPrograms +// *bpfMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadBpf() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// bpfSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfSpecs struct { + bpfProgramSpecs + bpfMapSpecs +} + +// bpfSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfProgramSpecs struct { + UprobeEnd *ebpf.ProgramSpec `ebpf:"uprobe_End"` + UprobeStartReturns *ebpf.ProgramSpec `ebpf:"uprobe_Start_Returns"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` +} + +// bpfObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfObjects struct { + bpfPrograms + bpfMaps +} + +func (o *bpfObjects) Close() error { + return _BpfClose( + &o.bpfPrograms, + &o.bpfMaps, + ) +} + +// bpfMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfMaps struct { + ActiveSpans *ebpf.Map `ebpf:"active_spans"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.ActiveSpans, + m.AllocMap, + m.Events, + m.SliceArrayBuffMap, + m.TrackedSpans, + m.TrackedSpansBySc, + ) +} + +// bpfPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfPrograms struct { + UprobeEnd *ebpf.Program `ebpf:"uprobe_End"` + UprobeStartReturns *ebpf.Program `ebpf:"uprobe_Start_Returns"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.UprobeEnd, + p.UprobeStartReturns, + ) +} + +func _BpfClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed bpf_bpfel_x86.o +var _BpfBytes []byte diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go new file mode 100644 index 000000000..c0962ff50 --- /dev/null +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go @@ -0,0 +1,224 @@ +// Copyright The OpenTelemetry Authors +// +// 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 language governing permissions and +// limitations under the License. + +package sdk + +import ( + "bytes" + "encoding/binary" + "errors" + "os" + // "strconv" + + "go.opentelemetry.io/auto/internal/pkg/inject" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/perf" + "github.com/go-logr/logr" + // "golang.org/x/sys/unix" + + // "go.opentelemetry.io/otel/attribute" + // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/trace" + + "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/utils" + "go.opentelemetry.io/auto/internal/pkg/process" +) + +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target amd64,arm64 -cc clang -cflags $CFLAGS bpf ./bpf/probe.bpf.c + +const instrumentedPkg = "go.opentelemetry.io/otel/sdk/trace" + +// Event represents a manual span created by the user +type Event struct { + context.BaseSpanProperties +} + +// Probe is the go.opentelemetry.io/otel/sdk/trace instrumentation probe. +type Probe struct { + logger logr.Logger + bpfObjects *bpfObjects + uprobes []link.Link + returnProbs []link.Link + eventsReader *perf.Reader +} + +// New returns a new [Probe]. +func New(logger logr.Logger) *Probe { + return &Probe{logger: logger.WithName("Probe/otel")} +} + +// LibraryName returns the /otel/sdk/trace package name. +func (h *Probe) LibraryName() string { + return instrumentedPkg +} + +// FuncNames returns the function names from "go.opentelemetry.io/otel/sdk/trace" that are instrumented. +func (h *Probe) FuncNames() []string { + return []string{ + "go.opentelemetry.io/otel/sdk/trace.(*tracer).Start", + "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End", + } +} + +// Load loads all instrumentation offsets. +func (h *Probe) Load(exec *link.Executable, target *process.TargetDetails) error { + spec, err := loadBpf() + if err != nil { + return err + } + if target.AllocationDetails == nil { + // This Probe requires allocation. + return errors.New("no allocation details") + } + err = inject.Constants( + spec, + inject.WithRegistersABI(target.IsRegistersABI()), + inject.WithAllocationDetails(*target.AllocationDetails), + ) + if err != nil { + return err + } + + h.bpfObjects = &bpfObjects{} + + err = utils.LoadEBPFObjects(spec, h.bpfObjects, &ebpf.CollectionOptions{ + Maps: ebpf.MapOptions{ + PinPath: bpffs.PathForTargetApplication(target), + }, + }) + + if err != nil { + return err + } + + retOffsets, err := target.GetFunctionReturns(h.FuncNames()[0]) + if err != nil { + return err + } + + for _, ret := range retOffsets { + retProbe, err := exec.Uprobe("", h.bpfObjects.UprobeStartReturns, &link.UprobeOptions{ + Address: ret, + }) + if err != nil { + return err + } + h.returnProbs = append(h.returnProbs, retProbe) + } + + offset, err := target.GetFunctionOffset(h.FuncNames()[1]) + if err != nil { + return err + } + + up, err := exec.Uprobe("", h.bpfObjects.UprobeEnd, &link.UprobeOptions{ + Address: offset, + }) + if err != nil { + return err + } + + h.uprobes = append(h.uprobes, up) + + rd, err := perf.NewReader(h.bpfObjects.Events, os.Getpagesize()) + if err != nil { + return err + } + h.eventsReader = rd + + return nil +} + +// Run runs the events processing loop. +func (h *Probe) Run(eventsChan chan<- *probe.Event) { + var event Event + for { + record, err := h.eventsReader.Read() + if err != nil { + if errors.Is(err, perf.ErrClosed) { + return + } + h.logger.Error(err, "error reading from perf reader") + continue + } + + if record.LostSamples != 0 { + h.logger.V(0).Info("perf event ring buffer full", "dropped", record.LostSamples) + continue + } + + if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { + h.logger.Error(err, "error parsing perf event") + continue + } + + eventsChan <- h.convertEvent(&event) + } +} + +func (h *Probe) convertEvent(e *Event) *probe.Event { + sc := trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: e.SpanContext.TraceID, + SpanID: e.SpanContext.SpanID, + TraceFlags: trace.FlagsSampled, + }) + + var pscPtr *trace.SpanContext + if e.ParentSpanContext.TraceID.IsValid() { + psc := trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: e.ParentSpanContext.TraceID, + SpanID: e.ParentSpanContext.SpanID, + TraceFlags: trace.FlagsSampled, + Remote: true, + }) + pscPtr = &psc + } else { + pscPtr = nil + } + + return &probe.Event{ + Library: h.LibraryName(), + Name: "manual", + Kind: trace.SpanKindClient, + StartTime: int64(e.StartTime), + EndTime: int64(e.EndTime), + SpanContext: &sc, + ParentSpanContext: pscPtr, + } +} + +// Close stops the Probe. +func (h *Probe) Close() { + h.logger.Info("closing go.opentelemetry.io/otel/sdk/trace probe") + if h.eventsReader != nil { + h.eventsReader.Close() + } + + for _, r := range h.uprobes { + r.Close() + } + + for _, r := range h.returnProbs { + r.Close() + } + + if h.bpfObjects != nil { + h.bpfObjects.Close() + } +} diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe_test.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe_test.go new file mode 100644 index 000000000..e1ed4fe1a --- /dev/null +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe_test.go @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// +// 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 language governing permissions and +// limitations under the License. + +package sdk + +import ( + "testing" + // "time" + + // "github.com/go-logr/logr/testr" + // "github.com/stretchr/testify/assert" + + // "go.opentelemetry.io/otel/attribute" + // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + // "go.opentelemetry.io/otel/trace" + + // "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" + // "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" +) + +func TestProbeConvertEvent(t *testing.T) { + // start := time.Now() + // end := start.Add(1 * time.Second) + + // traceID := trace.TraceID{1} + // spanID := trace.SpanID{1} + + // i := New(testr.New(t)) + // got := i.convertEvent(&Event{ + // BaseSpanProperties: context.BaseSpanProperties{ + // StartTime: uint64(start.UnixNano()), + // EndTime: uint64(end.UnixNano()), + // SpanContext: context.EBPFSpanContext{TraceID: traceID, SpanID: spanID}, + // }, + // // "SELECT * FROM foo" + // Query: [256]byte{0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x20, 0x2a, 0x20, 0x46, 0x52, 0x4f, 0x4d, 0x20, 0x66, 0x6f, 0x6f}, + // }) + + // sc := trace.NewSpanContext(trace.SpanContextConfig{ + // TraceID: traceID, + // SpanID: spanID, + // TraceFlags: trace.FlagsSampled, + // }) + // want := &probe.Event{ + // Library: instrumentedPkg, + // Name: "DB", + // Kind: trace.SpanKindClient, + // StartTime: int64(start.UnixNano()), + // EndTime: int64(end.UnixNano()), + // SpanContext: &sc, + // Attributes: []attribute.KeyValue{ + // semconv.DBStatementKey.String("SELECT * FROM foo"), + // }, + // } + // assert.Equal(t, want, got) +} diff --git a/internal/pkg/instrumentation/manager.go b/internal/pkg/instrumentation/manager.go index a3664ec8c..7db44619a 100644 --- a/internal/pkg/instrumentation/manager.go +++ b/internal/pkg/instrumentation/manager.go @@ -28,6 +28,7 @@ import ( grpcServer "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server" httpClient "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/net/http/client" httpServer "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/net/http/server" + otelSdkTrace "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs" "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" "go.opentelemetry.io/auto/internal/pkg/opentelemetry" @@ -208,6 +209,7 @@ func (m *Manager) registerProbes() error { httpClient.New(m.logger), gin.New(m.logger), dbSql.New(m.logger), + otelSdkTrace.New(m.logger), } for _, i := range insts { From cad101c34914a7c7b3669c582b4b493ac4eec623 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 10 Nov 2023 16:00:55 +0200 Subject: [PATCH 02/33] Add offsets for otel go sdk --- internal/pkg/inject/offset_results.json | 106 ++++++++++++++++++ internal/tools/inspect/cmd/offsetgen/main.go | 15 +++ internal/tools/inspect/render.go | 1 + .../go.opentelemetry.io/otel/sdk/go.mod.tmpl | 5 + .../go.opentelemetry.io/otel/sdk/main.go.tmpl | 15 +++ 5 files changed, 142 insertions(+) create mode 100644 internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl create mode 100644 internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl diff --git a/internal/pkg/inject/offset_results.json b/internal/pkg/inject/offset_results.json index 53b27a72c..425843584 100755 --- a/internal/pkg/inject/offset_results.json +++ b/internal/pkg/inject/offset_results.json @@ -1,4 +1,110 @@ [ + { + "module": "go.opentelemetry.io/otel/sdk", + "packages": [ + { + "package": "go.opentelemetry.io/otel/sdk/trace", + "structs": [ + { + "struct": "recordingSpan", + "fields": [ + { + "field": "attributes", + "offsets": [ + { + "offset": 240, + "versions": [ + "1.4.0", + "1.4.1", + "1.5.0", + "1.6.0", + "1.6.1", + "1.6.2", + "1.6.3", + "1.7.0", + "1.8.0", + "1.9.0", + "1.10.0", + "1.11.0", + "1.11.1", + "1.11.2", + "1.12.0", + "1.13.0", + "1.14.0", + "1.15.0-rc.1", + "1.15.0-rc.2", + "1.15.0", + "1.15.1", + "1.16.0-rc.1", + "1.16.0", + "1.17.0", + "1.18.0", + "1.19.0-rc.1", + "1.19.0" + ] + }, + { + "offset": 296, + "versions": [ + "1.0.0-RC3", + "1.0.0", + "1.0.1", + "1.1.0", + "1.2.0", + "1.3.0" + ] + } + ] + }, + { + "field": "name", + "offsets": [ + { + "offset": 80, + "versions": [ + "1.0.0-RC3", + "1.0.0", + "1.0.1", + "1.1.0", + "1.2.0", + "1.3.0", + "1.4.0", + "1.4.1", + "1.5.0", + "1.6.0", + "1.6.1", + "1.6.2", + "1.6.3", + "1.7.0", + "1.8.0", + "1.9.0", + "1.10.0", + "1.11.0", + "1.11.1", + "1.11.2", + "1.12.0", + "1.13.0", + "1.14.0", + "1.15.0-rc.1", + "1.15.0-rc.2", + "1.15.0", + "1.15.1", + "1.16.0-rc.1", + "1.16.0", + "1.17.0", + "1.18.0", + "1.19.0-rc.1", + "1.19.0" + ] + } + ] + } + ] + } + ] + } + ] + }, { "module": "golang.org/x/net", "packages": [ diff --git a/internal/tools/inspect/cmd/offsetgen/main.go b/internal/tools/inspect/cmd/offsetgen/main.go index b8e259f19..d2095270c 100644 --- a/internal/tools/inspect/cmd/offsetgen/main.go +++ b/internal/tools/inspect/cmd/offsetgen/main.go @@ -80,6 +80,11 @@ func manifests() ([]inspect.Manifest, error) { return nil, fmt.Errorf("failed to get \"golang.org/x/net\" versions: %w", err) } + otelSdkTraceVers, err := PkgVersions("go.opentelemetry.io/otel/sdk") + if err != nil { + return nil, fmt.Errorf("failed to get \"go.opentelemetry.io/otel/sdk\" versions: %w", err) + } + ren := func(src string) inspect.Renderer { return inspect.NewRenderer(logger, src, inspect.DefaultFS) } @@ -136,6 +141,16 @@ func manifests() ([]inspect.Manifest, error) { structfield.NewID("golang.org/x/net", "golang.org/x/net/http2", "FrameHeader", "StreamID"), }, }, + { + Application: inspect.Application{ + Renderer: ren("templates/go.opentelemetry.io/otel/sdk/*.tmpl"), + Versions: otelSdkTraceVers, + }, + StructFields: []structfield.ID{ + structfield.NewID("go.opentelemetry.io/otel/sdk", "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "name"), + structfield.NewID("go.opentelemetry.io/otel/sdk", "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "attributes"), + }, + }, }, nil } diff --git a/internal/tools/inspect/render.go b/internal/tools/inspect/render.go index 74c5bbe97..e44add78d 100644 --- a/internal/tools/inspect/render.go +++ b/internal/tools/inspect/render.go @@ -29,6 +29,7 @@ import ( //go:embed templates/google.golang.org/grpc/*.tmpl //go:embed templates/net/http/*.tmpl //go:embed templates/runtime/*.tmpl +//go:embed templates/go.opentelemetry.io/otel/sdk/*.tmpl var DefaultFS embed.FS // Renderer renders templates from an fs.FS. diff --git a/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl b/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl new file mode 100644 index 000000000..31a40a784 --- /dev/null +++ b/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl @@ -0,0 +1,5 @@ +module otelteacecapp + +go 1.12 + +require go.opentelemetry.io/otel/sdk {{ .Version }} diff --git a/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl b/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl new file mode 100644 index 000000000..cfca96930 --- /dev/null +++ b/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl @@ -0,0 +1,15 @@ +package main + +import ( + "context" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/trace" +) + +func main() { + tracerProvider := trace.NewTracerProvider(trace.WithResource(nil)) + otel.SetTracerProvider(tracerProvider) + tracer := otel.Tracer("dummy") + _, span := tracer.Start(context.Background(), "dummy") + defer span.End() +} \ No newline at end of file From dbad5ebe5f013f585707771febe6971598691ff7 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 10 Nov 2023 17:00:49 +0200 Subject: [PATCH 03/33] Pass the user defined span name through eBPF --- .../otel/sdk/trace/bpf/probe.bpf.c | 37 +++++-------------- .../otel/sdk/trace/probe.go | 18 +++++++-- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c index ac3d619f1..06a34d9ce 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c @@ -21,9 +21,11 @@ char __license[] SEC("license") = "Dual MIT/GPL"; #define MAX_CONCURRENT 50 +#define MAX_SPAN_NAME_LEN 64 struct otel_span_t { BASE_SPAN_PROPERTIES + char span_name[MAX_SPAN_NAME_LEN]; }; struct { @@ -37,32 +39,8 @@ struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); } events SEC(".maps"); -// This instrumentation attaches uprobe to the following function: -// func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) -// SEC("uprobe/Start") -// int uprobe_Start(struct pt_regs *ctx) { -// struct otel_span_t otel_span = {0}; -// otel_span.start_time = bpf_ktime_get_ns(); - -// // Get parent if exists -// 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 -// bpf_probe_read(&otel_span.psc, sizeof(otel_span.psc), span_ctx); -// copy_byte_arrays(otel_span.psc.TraceID, otel_span.sc.TraceID, TRACE_ID_SIZE); -// generate_random_bytes(otel_span.sc.SpanID, SPAN_ID_SIZE); -// } else { -// otel_span.sc = generate_span_context(); -// } - -// // Get key -// void *key = get_consistent_key(ctx, context_ptr_val); - -// bpf_map_update_elem(&active_spans, &key, &otel_span, 0); -// start_tracking_span(context_ptr_val, &otel_span.sc); -// return 0; -// } +// Injected in init +volatile const u64 span_name_pos; // This instrumentation attaches uprobe to the following function: // func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) @@ -71,7 +49,7 @@ int uprobe_Start_Returns(struct pt_regs *ctx) { struct otel_span_t otel_span = {0}; otel_span.start_time = bpf_ktime_get_ns(); - // Get the returned context and Span (concrete type of the interfaces) + // Get the ** returned ** context and Span (concrete type of the interfaces) void *context_ptr_val = get_argument(ctx, 2); void *span_ptr_val = get_argument(ctx, 4); @@ -104,6 +82,11 @@ int uprobe_End(struct pt_regs *ctx) { bpf_map_delete_elem(&active_spans, &recording_span_ptr); stop_tracking_span(&span->sc, &span->psc); + if (!get_go_string_from_user_ptr((void *)(recording_span_ptr + span_name_pos), span->span_name, sizeof(span->span_name))) { + bpf_printk("failed to get span name from manual span"); + return 0; + } + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, span, sizeof(*span)); return 0; } \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go index c0962ff50..d38c1c405 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go @@ -19,17 +19,19 @@ import ( "encoding/binary" "errors" "os" + // "strconv" "go.opentelemetry.io/auto/internal/pkg/inject" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs" "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" + "go.opentelemetry.io/auto/internal/pkg/structfield" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/perf" "github.com/go-logr/logr" - // "golang.org/x/sys/unix" + "golang.org/x/sys/unix" // "go.opentelemetry.io/otel/attribute" // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" @@ -47,6 +49,7 @@ const instrumentedPkg = "go.opentelemetry.io/otel/sdk/trace" // Event represents a manual span created by the user type Event struct { context.BaseSpanProperties + SpanName [64]byte } // Probe is the go.opentelemetry.io/otel/sdk/trace instrumentation probe. @@ -78,6 +81,9 @@ func (h *Probe) FuncNames() []string { // Load loads all instrumentation offsets. func (h *Probe) Load(exec *link.Executable, target *process.TargetDetails) error { + const otelSdkMod = "go.opentelemetry.io/otel/sdk" + otelSdkVer := target.Libraries[otelSdkMod] + spec, err := loadBpf() if err != nil { return err @@ -89,7 +95,11 @@ func (h *Probe) Load(exec *link.Executable, target *process.TargetDetails) error err = inject.Constants( spec, inject.WithRegistersABI(target.IsRegistersABI()), - inject.WithAllocationDetails(*target.AllocationDetails), + inject.WithOffset( + "span_name_pos", + structfield.NewID(otelSdkMod, "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "name"), + otelSdkVer, + ), ) if err != nil { return err @@ -173,6 +183,8 @@ func (h *Probe) Run(eventsChan chan<- *probe.Event) { } func (h *Probe) convertEvent(e *Event) *probe.Event { + spanName := unix.ByteSliceToString(e.SpanName[:]) + sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: e.SpanContext.TraceID, SpanID: e.SpanContext.SpanID, @@ -194,7 +206,7 @@ func (h *Probe) convertEvent(e *Event) *probe.Event { return &probe.Event{ Library: h.LibraryName(), - Name: "manual", + Name: spanName, Kind: trace.SpanKindClient, StartTime: int64(e.StartTime), EndTime: int64(e.EndTime), From 9c2c331749bca81b6e80bb18a263fb95af5bce97 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Sat, 11 Nov 2023 01:19:04 +0200 Subject: [PATCH 04/33] Initial attribute parsing --- internal/include/go_types.h | 12 +- internal/include/otel_types.h | 131 ++++++++++++++++++ .../github.com/gin-gonic/gin/bpf/probe.bpf.c | 4 +- .../otel/sdk/trace/bpf/probe.bpf.c | 37 +++-- .../otel/sdk/trace/bpf_bpfel_arm64.go | 43 +++--- .../otel/sdk/trace/bpf_bpfel_x86.go | 43 +++--- .../otel/sdk/trace/probe.go | 44 +++++- .../google.golang.org/grpc/bpf/probe.bpf.c | 2 +- .../grpc/server/bpf/probe.bpf.c | 2 +- .../bpf/net/http/client/bpf/probe.bpf.c | 4 +- .../bpf/net/http/server/bpf/probe.bpf.c | 4 +- 11 files changed, 269 insertions(+), 57 deletions(-) create mode 100644 internal/include/otel_types.h diff --git a/internal/include/go_types.h b/internal/include/go_types.h index 486dfc4fd..bcb9325b7 100644 --- a/internal/include/go_types.h +++ b/internal/include/go_types.h @@ -177,11 +177,11 @@ static __always_inline void append_item_to_slice(struct go_slice *slice, void *n } } -static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char *dst, u64 max_len) +static __always_inline s64 get_go_string_from_user_ptr(void *user_str_ptr, char *dst, u64 max_len) { if (user_str_ptr == NULL) { - return false; + return -1; } struct go_string user_str = {0}; @@ -189,16 +189,16 @@ static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char success = bpf_probe_read(&user_str, sizeof(struct go_string), user_str_ptr); if (success != 0 || user_str.len < 1) { - return false; + return -1; } - u64 size_to_read = user_str.len > max_len ? max_len : user_str.len; + s64 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 -1; } - return true; + return size_to_read; } #endif \ No newline at end of file diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h new file mode 100644 index 000000000..969b1b85a --- /dev/null +++ b/internal/include/otel_types.h @@ -0,0 +1,131 @@ +// Copyright The OpenTelemetry Authors +// +// 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 language governing permissions and +// limitations under the License. + +#ifndef _OTEL_TYPES_H +#define _OTEL_TYPES_H + +#include "go_types.h" +#include "common.h" + +/* Defintions should mimic structs defined in go.opentelemetry.io/otel/attribute */ +typedef u64 attr_val_type_t; + +#define INVALID 0 +#define BOOL 1 +#define INT64 2 +#define FLOAT64 3 +#define STRING 4 +#define BOOLSLICE 5 +#define INT64SLICE 6 +#define FLOAT64SLICE 7 +#define STRINGSLICE 8 + +typedef struct go_otel_attr_value { + attr_val_type_t vtype; + u64 numeric; + struct go_string string; + struct go_slice slice; +} go_otel_attr_value_t; + +typedef struct go_otel_key_value { + struct go_string key; + go_otel_attr_value_t value; +} go_otel_key_value_t; + +/* The following structs are the C-formated structs to be used by the eBPF code */ + +/* In the SDK the key is a string, but we must thave a limit */ +#define OTEL_ATTRIBUTE_KEY_MAX_LENGTH (64) +#define OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE (1024) +#define OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(bool)) +#define OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(s64)) +#define OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(double)) + +typedef struct otel_attribute { + attr_val_type_t vtype; + char key[OTEL_ATTRIBUTE_KEY_MAX_LENGTH]; + // union + // { + // s64 int_value;// INT64, BOOL + // double double_value; // FLOAT64 + // bool bool_buffer[OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE]; // BOOLSLICE + // s64 int_buffer[OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE]; // INT64SLICE + // double double_buffer[OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE]; // FLOAT64SLICE + // char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; + // }; + char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; +} otel_attribute_t; + +static __always_inline long convert_go_otel_attribute(go_otel_key_value_t *go_attr, otel_attribute_t *ebpf_attr) +{ + go_otel_attr_value_t go_attr_value = {0}; + bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); + bpf_probe_read(&ebpf_attr->vtype, sizeof(attr_val_type_t), &go_attr_value.vtype); + //ebpf_attr->vtype = go_attr_value.vtype; + if (get_go_string_from_user_ptr(&go_attr->key, ebpf_attr->key, OTEL_ATTRIBUTE_KEY_MAX_LENGTH) < 0){ + return -1; + } + long bytes_copied = 0; + switch (go_attr_value.vtype) + { + case BOOL: + case INT64: + case FLOAT64: + bpf_probe_read(ebpf_attr->buf, sizeof(s64), &go_attr_value.numeric); + bytes_copied = sizeof(s64); + break; + case STRING: + bytes_copied = get_go_string_from_user_ptr(&go_attr_value.string, ebpf_attr->buf, OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE); + break; + case BOOLSLICE: + // TODO + return -1; + case INT64SLICE: + // TODO + return -1; + case FLOAT64SLICE: + // TODO + return -1; + case STRINGSLICE: + // TODO + return -1; + case INVALID: + default: + return -1; + } + + return bytes_copied; +} + +static __always_inline void convert_attributes_slice(struct go_slice *attrs_slice, otel_attribute_t *attrs, u8 max_attrs) +{ + if (attrs_slice == NULL || attrs == NULL){ + return; + } + s64 slice_len = 0; + bpf_probe_read(&slice_len, sizeof(s64), &attrs_slice->len); + go_otel_key_value_t *go_attrs = NULL; + bpf_probe_read(&go_attrs, sizeof(go_otel_key_value_t*), &attrs_slice->array); + u8 attrs_count = ((slice_len > 4) ? 4 : slice_len); + for (u32 i = 0; i < 1; i++) + { + if (convert_go_otel_attribute(&go_attrs[i], &attrs[i]) < 0){ + break; + } + } +} + + + +#endif \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c index 8108c2490..cda472e51 100644 --- a/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c @@ -59,7 +59,7 @@ int uprobe_GinEngine_ServeHTTP(struct pt_regs *ctx) { void *req_ctx_ptr = get_Go_context(ctx, 4, ctx_ptr_pos, false); // Get method from request - if (!get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), httpReq.method, sizeof(httpReq.method))) { + if (get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), httpReq.method, sizeof(httpReq.method)) < 0) { bpf_printk("failed to get method from request"); return 0; } @@ -67,7 +67,7 @@ int uprobe_GinEngine_ServeHTTP(struct pt_regs *ctx) { // get path from Request.URL void *url_ptr = 0; bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr + url_ptr_pos)); - if (!get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), httpReq.path, sizeof(httpReq.path))) { + if (get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), httpReq.path, sizeof(httpReq.path)) < 0) { bpf_printk("failed to get path from Request.URL"); return 0; } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c index 06a34d9ce..da0a45bbe 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c @@ -17,15 +17,18 @@ #include "go_context.h" #include "go_types.h" #include "uprobe.h" +#include "otel_types.h" char __license[] SEC("license") = "Dual MIT/GPL"; #define MAX_CONCURRENT 50 #define MAX_SPAN_NAME_LEN 64 +#define MAX_ATTRIBUTES 4 struct otel_span_t { BASE_SPAN_PROPERTIES char span_name[MAX_SPAN_NAME_LEN]; + otel_attribute_t attributes[MAX_ATTRIBUTES]; }; struct { @@ -35,19 +38,33 @@ struct { __uint(max_entries, MAX_CONCURRENT); } active_spans SEC(".maps"); +struct +{ + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(struct otel_span_t)); + __uint(max_entries, 1); +} otel_span_storage_map SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); } events SEC(".maps"); // Injected in init volatile const u64 span_name_pos; +volatile const u64 span_attributes_pos; // This instrumentation attaches uprobe to the following function: // func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) SEC("uprobe/Start") int uprobe_Start_Returns(struct pt_regs *ctx) { - struct otel_span_t otel_span = {0}; - otel_span.start_time = bpf_ktime_get_ns(); + u32 map_key = 0; + struct otel_span_t *otel_span = bpf_map_lookup_elem(&otel_span_storage_map, &map_key); + if (otel_span == NULL) { + return 0; + } + bpf_memset((unsigned char *)otel_span, 0, sizeof(*otel_span)); + otel_span->start_time = bpf_ktime_get_ns(); // Get the ** returned ** context and Span (concrete type of the interfaces) void *context_ptr_val = get_argument(ctx, 2); @@ -56,15 +73,15 @@ int uprobe_Start_Returns(struct pt_regs *ctx) { struct span_context *span_ctx = get_parent_span_context(context_ptr_val); if (span_ctx != NULL) { // Set the parent context - bpf_probe_read(&otel_span.psc, sizeof(otel_span.psc), span_ctx); - copy_byte_arrays(otel_span.psc.TraceID, otel_span.sc.TraceID, TRACE_ID_SIZE); - generate_random_bytes(otel_span.sc.SpanID, SPAN_ID_SIZE); + bpf_probe_read(&otel_span->psc, sizeof(otel_span->psc), span_ctx); + copy_byte_arrays(otel_span->psc.TraceID, otel_span->sc.TraceID, TRACE_ID_SIZE); + generate_random_bytes(otel_span->sc.SpanID, SPAN_ID_SIZE); } else { - otel_span.sc = generate_span_context(); + otel_span->sc = generate_span_context(); } - bpf_map_update_elem(&active_spans, &span_ptr_val, &otel_span, 0); - start_tracking_span(context_ptr_val, &otel_span.sc); + bpf_map_update_elem(&active_spans, &span_ptr_val, otel_span, 0); + start_tracking_span(context_ptr_val, &otel_span->sc); return 0; } @@ -82,11 +99,13 @@ int uprobe_End(struct pt_regs *ctx) { bpf_map_delete_elem(&active_spans, &recording_span_ptr); stop_tracking_span(&span->sc, &span->psc); - if (!get_go_string_from_user_ptr((void *)(recording_span_ptr + span_name_pos), span->span_name, sizeof(span->span_name))) { + if (get_go_string_from_user_ptr((void *)(recording_span_ptr + span_name_pos), span->span_name, sizeof(span->span_name)) < 0) { bpf_printk("failed to get span name from manual span"); return 0; } + convert_attributes_slice((void *)(recording_span_ptr + span_attributes_pos), span->attributes, MAX_ATTRIBUTES); + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, span, sizeof(*span)); return 0; } \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go index 555f52e33..f26a8400b 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go @@ -13,10 +13,18 @@ import ( ) type bpfOtelSpanT struct { - StartTime uint64 - EndTime uint64 - Sc bpfSpanContext - Psc bpfSpanContext + StartTime uint64 + EndTime uint64 + Sc bpfSpanContext + Psc bpfSpanContext + SpanName [64]int8 + Attributes [4]struct { + Key [64]int8 + IntValue int64 + _ [1016]byte + Vtype uint32 + _ [4]byte + } } type bpfSliceArrayBuff struct{ Buff [1024]uint8 } @@ -75,12 +83,13 @@ type bpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` - AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` - Events *ebpf.MapSpec `ebpf:"events"` - SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` + ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + OtelSpanStorageMap *ebpf.MapSpec `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -102,12 +111,13 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - ActiveSpans *ebpf.Map `ebpf:"active_spans"` - AllocMap *ebpf.Map `ebpf:"alloc_map"` - Events *ebpf.Map `ebpf:"events"` - SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` + ActiveSpans *ebpf.Map `ebpf:"active_spans"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + OtelSpanStorageMap *ebpf.Map `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` } func (m *bpfMaps) Close() error { @@ -115,6 +125,7 @@ func (m *bpfMaps) Close() error { m.ActiveSpans, m.AllocMap, m.Events, + m.OtelSpanStorageMap, m.SliceArrayBuffMap, m.TrackedSpans, m.TrackedSpansBySc, diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go index 13d142727..1c11e1523 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go @@ -13,10 +13,18 @@ import ( ) type bpfOtelSpanT struct { - StartTime uint64 - EndTime uint64 - Sc bpfSpanContext - Psc bpfSpanContext + StartTime uint64 + EndTime uint64 + Sc bpfSpanContext + Psc bpfSpanContext + SpanName [64]int8 + Attributes [4]struct { + Key [64]int8 + IntValue int64 + _ [1016]byte + Vtype uint32 + _ [4]byte + } } type bpfSliceArrayBuff struct{ Buff [1024]uint8 } @@ -75,12 +83,13 @@ type bpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` - AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` - Events *ebpf.MapSpec `ebpf:"events"` - SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` + ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + OtelSpanStorageMap *ebpf.MapSpec `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -102,12 +111,13 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - ActiveSpans *ebpf.Map `ebpf:"active_spans"` - AllocMap *ebpf.Map `ebpf:"alloc_map"` - Events *ebpf.Map `ebpf:"events"` - SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` + ActiveSpans *ebpf.Map `ebpf:"active_spans"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + OtelSpanStorageMap *ebpf.Map `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` } func (m *bpfMaps) Close() error { @@ -115,6 +125,7 @@ func (m *bpfMaps) Close() error { m.ActiveSpans, m.AllocMap, m.Events, + m.OtelSpanStorageMap, m.SliceArrayBuffMap, m.TrackedSpans, m.TrackedSpansBySc, diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go index d38c1c405..f835dd4c0 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go @@ -33,7 +33,7 @@ import ( "github.com/go-logr/logr" "golang.org/x/sys/unix" - // "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/attribute" // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" "go.opentelemetry.io/otel/trace" @@ -46,10 +46,17 @@ import ( const instrumentedPkg = "go.opentelemetry.io/otel/sdk/trace" +type ebpfAttribute struct { + Vtype [8]byte + Key [64]byte + Value [1024]byte +} + // Event represents a manual span created by the user type Event struct { context.BaseSpanProperties SpanName [64]byte + Attributes [4]ebpfAttribute } // Probe is the go.opentelemetry.io/otel/sdk/trace instrumentation probe. @@ -100,6 +107,11 @@ func (h *Probe) Load(exec *link.Executable, target *process.TargetDetails) error structfield.NewID(otelSdkMod, "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "name"), otelSdkVer, ), + inject.WithOffset( + "span_attributes_pos", + structfield.NewID(otelSdkMod, "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "attributes"), + otelSdkVer, + ), ) if err != nil { return err @@ -146,7 +158,8 @@ func (h *Probe) Load(exec *link.Executable, target *process.TargetDetails) error h.uprobes = append(h.uprobes, up) - rd, err := perf.NewReader(h.bpfObjects.Events, os.Getpagesize()) + // Getting the attribute defined from the user might require more memory + rd, err := perf.NewReader(h.bpfObjects.Events, os.Getpagesize() * 8) if err != nil { return err } @@ -204,17 +217,44 @@ func (h *Probe) convertEvent(e *Event) *probe.Event { pscPtr = nil } + var attributes []attribute.KeyValue + for _, a := range e.Attributes { + h.logger.Info("attribute", "a", a) + if a.Vtype == [8]byte{0, 0, 0, 0} { + continue + } + attributes = append(attributes, convertAttribute(a)) + } + return &probe.Event{ Library: h.LibraryName(), Name: spanName, Kind: trace.SpanKindClient, StartTime: int64(e.StartTime), EndTime: int64(e.EndTime), + Attributes: attributes, SpanContext: &sc, ParentSpanContext: pscPtr, } } +func convertAttribute(a ebpfAttribute) attribute.KeyValue { + key := unix.ByteSliceToString(a.Key[:]) + vtype := attribute.Type(binary.LittleEndian.Uint32(a.Vtype[:])) + switch vtype { + case attribute.BOOL: + return attribute.Bool(key, binary.LittleEndian.Uint32(a.Value[:]) != 0) + case attribute.INT64: + return attribute.Int64(key, int64(binary.LittleEndian.Uint64(a.Value[:]))) + case attribute.FLOAT64: + return attribute.Float64(key, float64(binary.LittleEndian.Uint64(a.Value[:]))) + case attribute.STRING: + return attribute.String(key, unix.ByteSliceToString(a.Value[:])) + default: + return attribute.String(key, "unknown") + } +} + // Close stops the Probe. func (h *Probe) Close() { h.logger.Info("closing go.opentelemetry.io/otel/sdk/trace probe") diff --git a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c index 6f8c3a7cd..3575c55b7 100644 --- a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c @@ -100,7 +100,7 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) // Read ClientConn.Target void *clientconn_ptr = get_argument(ctx, clientconn_pos); - if (!get_go_string_from_user_ptr((void*)(clientconn_ptr + clientconn_target_ptr_pos), grpcReq.target, sizeof(grpcReq.target))) + if (get_go_string_from_user_ptr((void*)(clientconn_ptr + clientconn_target_ptr_pos), grpcReq.target, sizeof(grpcReq.target)) < 0) { bpf_printk("target write failed, aborting ebpf probe"); return 0; diff --git a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c index 9fc829f61..fea6bbc54 100644 --- a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c @@ -108,7 +108,7 @@ int uprobe_server_handleStream(struct pt_regs *ctx) grpcReq.start_time = bpf_ktime_get_ns(); // Set attributes - if (!get_go_string_from_user_ptr((void *)(stream_ptr + stream_method_ptr_pos), grpcReq.method, sizeof(grpcReq.method))) + if (get_go_string_from_user_ptr((void *)(stream_ptr + stream_method_ptr_pos), grpcReq.method, sizeof(grpcReq.method)) < 0) { bpf_printk("method write failed, aborting ebpf probe"); return 0; diff --git a/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c index e94795215..e9c665c42 100644 --- a/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c @@ -178,7 +178,7 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) { httpReq.sc = generate_span_context(); } - if (!get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq.method, sizeof(httpReq.method))) { + if (get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq.method, sizeof(httpReq.method)) < 0) { bpf_printk("uprobe_HttpClient_Do: Failed to get method from request"); return 0; } @@ -186,7 +186,7 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) { // get path from Request.URL void *url_ptr = 0; bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr+url_ptr_pos)); - if (!get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq.path, sizeof(httpReq.path))) { + if (get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq.path, sizeof(httpReq.path)) < 0) { bpf_printk("uprobe_HttpClient_Do: Failed to get path from Request.URL"); return 0; } diff --git a/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c index d1b4307b0..e7beb7b88 100644 --- a/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c @@ -267,14 +267,14 @@ int uprobe_HandlerFunc_ServeHTTP_Returns(struct pt_regs *ctx) { // Collect fields from response // Get method from request - if (!get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), http_server_span->method, sizeof(http_server_span->method))) { + if (get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), http_server_span->method, sizeof(http_server_span->method)) < 0) { bpf_printk("failed to get method from request"); return 0; } // get path from Request.URL void *url_ptr = 0; bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr + url_ptr_pos)); - if (!get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), http_server_span->path, sizeof(http_server_span->path))) { + if (get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), http_server_span->path, sizeof(http_server_span->path)) < 0) { bpf_printk("failed to get path from Request.URL"); return 0; } From f989d66d25738ffd54fa52eedd4104952ecfe9c9 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Sat, 11 Nov 2023 13:21:42 +0200 Subject: [PATCH 05/33] Fix struct for attribute value --- examples/rolldice/main.go | 5 ++++- internal/include/otel_types.h | 13 +++++++++---- internal/pkg/inject/offset_results.json | 12 ++++++++++++ .../bpf/go.opentelemetry.io/otel/sdk/trace/probe.go | 3 ++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/examples/rolldice/main.go b/examples/rolldice/main.go index 52cd334af..f07789369 100644 --- a/examples/rolldice/main.go +++ b/examples/rolldice/main.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "math" "math/rand" "net/http" "os" @@ -122,7 +123,9 @@ func (s *Server) rolldice(w http.ResponseWriter, r *http.Request) { n := s.rand.Intn(6) + 1 rollValueAttr := attribute.Int("roll.value", n) - span.SetAttributes(rollValueAttr) + piAttr := attribute.Float64("pi", math.Pi) + strAttr := attribute.String("nice.key", "string value!") + span.SetAttributes(rollValueAttr, piAttr, strAttr) logger.Info("rolldice called", zap.Int("dice", n)) fmt.Fprintf(w, "%v", n) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index 969b1b85a..b3d346001 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -35,7 +35,7 @@ typedef struct go_otel_attr_value { attr_val_type_t vtype; u64 numeric; struct go_string string; - struct go_slice slice; + struct go_iface slice; } go_otel_attr_value_t; typedef struct go_otel_key_value { @@ -71,8 +71,10 @@ static __always_inline long convert_go_otel_attribute(go_otel_key_value_t *go_at { go_otel_attr_value_t go_attr_value = {0}; bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); - bpf_probe_read(&ebpf_attr->vtype, sizeof(attr_val_type_t), &go_attr_value.vtype); - //ebpf_attr->vtype = go_attr_value.vtype; + if (go_attr_value.vtype == INVALID) { + return -1; + } + ebpf_attr->vtype = go_attr_value.vtype; if (get_go_string_from_user_ptr(&go_attr->key, ebpf_attr->key, OTEL_ATTRIBUTE_KEY_MAX_LENGTH) < 0){ return -1; } @@ -118,8 +120,11 @@ static __always_inline void convert_attributes_slice(struct go_slice *attrs_slic go_otel_key_value_t *go_attrs = NULL; bpf_probe_read(&go_attrs, sizeof(go_otel_key_value_t*), &attrs_slice->array); u8 attrs_count = ((slice_len > 4) ? 4 : slice_len); - for (u32 i = 0; i < 1; i++) + for (u32 i = 0; i < 4; i++) { + if (i >= slice_len){ + break; + } if (convert_go_otel_attribute(&go_attrs[i], &attrs[i]) < 0){ break; } diff --git a/internal/pkg/inject/offset_results.json b/internal/pkg/inject/offset_results.json index 425843584..606377024 100755 --- a/internal/pkg/inject/offset_results.json +++ b/internal/pkg/inject/offset_results.json @@ -43,6 +43,12 @@ "1.19.0" ] }, + { + "offset": 256, + "versions": [ + "1.20.0" + ] + }, { "offset": 296, "versions": [ @@ -96,6 +102,12 @@ "1.19.0-rc.1", "1.19.0" ] + }, + { + "offset": 96, + "versions": [ + "1.20.0" + ] } ] } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go index f835dd4c0..60a772e3c 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go @@ -18,6 +18,7 @@ import ( "bytes" "encoding/binary" "errors" + "math" "os" // "strconv" @@ -247,7 +248,7 @@ func convertAttribute(a ebpfAttribute) attribute.KeyValue { case attribute.INT64: return attribute.Int64(key, int64(binary.LittleEndian.Uint64(a.Value[:]))) case attribute.FLOAT64: - return attribute.Float64(key, float64(binary.LittleEndian.Uint64(a.Value[:]))) + return attribute.Float64(key, math.Float64frombits(binary.LittleEndian.Uint64(a.Value[:]))) case attribute.STRING: return attribute.String(key, unix.ByteSliceToString(a.Value[:])) default: From 9119c73dc63d8b0ff0ffd3ab33d0c44619a3fdfd Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Sat, 11 Nov 2023 21:57:48 +0200 Subject: [PATCH 06/33] Try more efficient attribute encoding, currently only working for numeric values, missing Go decoding --- examples/rolldice/docker-compose.yaml | 1 + internal/include/go_types.h | 8 +- internal/include/otel_types.h | 255 +++++++++++++----- .../otel/sdk/trace/bpf/probe.bpf.c | 6 +- .../otel/sdk/trace/bpf_bpfel_arm64.go | 9 +- .../otel/sdk/trace/bpf_bpfel_x86.go | 9 +- .../otel/sdk/trace/probe.go | 59 ++-- internal/pkg/instrumentation/utils/ebpf.go | 4 +- 8 files changed, 234 insertions(+), 117 deletions(-) diff --git a/examples/rolldice/docker-compose.yaml b/examples/rolldice/docker-compose.yaml index ecbf2431e..51e21158a 100644 --- a/examples/rolldice/docker-compose.yaml +++ b/examples/rolldice/docker-compose.yaml @@ -31,6 +31,7 @@ services: - OTEL_GO_AUTO_TARGET_EXE=/app/main - OTEL_SERVICE_NAME=rolldice - OTEL_PROPAGATORS=tracecontext,baggage + - OTEL_GO_AUTO_SHOW_VERIFIER_LOG=true volumes: - shared-data:/app - /proc:/host/proc diff --git a/internal/include/go_types.h b/internal/include/go_types.h index bcb9325b7..7b4b869a0 100644 --- a/internal/include/go_types.h +++ b/internal/include/go_types.h @@ -177,8 +177,12 @@ static __always_inline void append_item_to_slice(struct go_slice *slice, void *n } } -static __always_inline s64 get_go_string_from_user_ptr(void *user_str_ptr, char *dst, u64 max_len) +static __always_inline s64 get_go_string_from_user_ptr(void *user_str_ptr, char *dst, s32 max_len) { + if (max_len < 1) + { + return -1; + } if (user_str_ptr == NULL) { return -1; @@ -192,7 +196,7 @@ static __always_inline s64 get_go_string_from_user_ptr(void *user_str_ptr, char return -1; } - s64 size_to_read = user_str.len > max_len ? max_len : user_str.len; + u32 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) { diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index b3d346001..8fb1d305f 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -46,91 +46,204 @@ typedef struct go_otel_key_value { /* The following structs are the C-formated structs to be used by the eBPF code */ /* In the SDK the key is a string, but we must thave a limit */ -#define OTEL_ATTRIBUTE_KEY_MAX_LENGTH (64) -#define OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE (1024) -#define OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(bool)) -#define OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(s64)) -#define OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(double)) - -typedef struct otel_attribute { - attr_val_type_t vtype; - char key[OTEL_ATTRIBUTE_KEY_MAX_LENGTH]; - // union - // { - // s64 int_value;// INT64, BOOL - // double double_value; // FLOAT64 - // bool bool_buffer[OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE]; // BOOLSLICE - // s64 int_buffer[OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE]; // INT64SLICE - // double double_buffer[OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE]; // FLOAT64SLICE - // char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; - // }; - char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; -} otel_attribute_t; - -static __always_inline long convert_go_otel_attribute(go_otel_key_value_t *go_attr, otel_attribute_t *ebpf_attr) -{ - go_otel_attr_value_t go_attr_value = {0}; - bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); - if (go_attr_value.vtype == INVALID) { - return -1; - } - ebpf_attr->vtype = go_attr_value.vtype; - if (get_go_string_from_user_ptr(&go_attr->key, ebpf_attr->key, OTEL_ATTRIBUTE_KEY_MAX_LENGTH) < 0){ - return -1; - } - long bytes_copied = 0; - switch (go_attr_value.vtype) - { - case BOOL: - case INT64: - case FLOAT64: - bpf_probe_read(ebpf_attr->buf, sizeof(s64), &go_attr_value.numeric); - bytes_copied = sizeof(s64); - break; - case STRING: - bytes_copied = get_go_string_from_user_ptr(&go_attr_value.string, ebpf_attr->buf, OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE); - break; - case BOOLSLICE: - // TODO - return -1; - case INT64SLICE: - // TODO - return -1; - case FLOAT64SLICE: - // TODO - return -1; - case STRINGSLICE: - // TODO - return -1; - case INVALID: - default: - return -1; - } +// #define OTEL_ATTRIBUTE_KEY_MAX_LENGTH (64) +// #define OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE (1024) +// #define OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(bool)) +// #define OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(s64)) +// #define OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(double)) +#define OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH (256) +#define OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE (1024) +#define OTEL_ATTRUBUTE_MAX_COUNT OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH / 2 - return bytes_copied; -} +typedef struct otel_attr_header { + u16 val_length; + u8 vtype; + u8 reserved; +} otel_attr_header_t; + +typedef struct otel_attributes { + u8 keys[OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH]; + u8 values[OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE]; +} otel_attributes_t; + +// typedef struct otel_attribute { +// attr_val_type_t vtype; +// char key[OTEL_ATTRIBUTE_KEY_MAX_LENGTH]; +// // union +// // { +// // s64 int_value;// INT64, BOOL +// // double double_value; // FLOAT64 +// // bool bool_buffer[OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE]; // BOOLSLICE +// // s64 int_buffer[OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE]; // INT64SLICE +// // double double_buffer[OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE]; // FLOAT64SLICE +// // char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; +// // }; +// char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; +// } otel_attribute_t; -static __always_inline void convert_attributes_slice(struct go_slice *attrs_slice, otel_attribute_t *attrs, u8 max_attrs) +static __always_inline long convert_go_otel_attributes(struct go_slice *attrs_slice, otel_attributes_t *enc_attrs) { - if (attrs_slice == NULL || attrs == NULL){ - return; + if (attrs_slice == NULL || enc_attrs == NULL){ + return -1; } s64 slice_len = 0; bpf_probe_read(&slice_len, sizeof(s64), &attrs_slice->len); - go_otel_key_value_t *go_attrs = NULL; - bpf_probe_read(&go_attrs, sizeof(go_otel_key_value_t*), &attrs_slice->array); - u8 attrs_count = ((slice_len > 4) ? 4 : slice_len); - for (u32 i = 0; i < 4; i++) + go_otel_key_value_t *go_attr = NULL; + bpf_probe_read(&go_attr, sizeof(go_otel_key_value_t*), &attrs_slice->array); + + u16 keys_off = 0, values_off = 0; + s64 key_len = 0; + go_otel_attr_value_t go_attr_value = {0}; + struct go_string go_str = {0}; + otel_attr_header_t *attr_header = NULL; + s64 bytes_copied = 0; + s32 max_str_len = 0; + for (u32 i = 0; + i < 32 && + keys_off < OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH && + values_off < OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - sizeof(otel_attr_header_t) - sizeof(s64); + i++) { - if (i >= slice_len){ + // if (keys_off >= OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH){ + // break; + // } + // if (i >= slice_len){ + // break; + // } + __builtin_memset(&go_attr_value, 0, sizeof(go_otel_attr_value_t)); + bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); + if (go_attr_value.vtype == INVALID) { + break; + } + bpf_probe_read(&go_str, sizeof(struct go_string), &go_attr->key); + if (go_str.len <= 0){ + break; + } + if (go_str.len >= OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - keys_off - 1) { + break; + } + keys_off &= (OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1); + // u64 max_key_len = OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - keys_off; + // max_key_len &= (OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1); + key_len = get_go_string_from_user_ptr(&go_str, (char*)&enc_attrs->keys[keys_off], OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE); + if (key_len < 0){ + break; + } + keys_off += key_len; + enc_attrs->keys[keys_off] = 0; + keys_off++; + + if (values_off >= OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - sizeof(otel_attr_header_t) - sizeof(s64)){ break; } - if (convert_go_otel_attribute(&go_attrs[i], &attrs[i]) < 0){ + attr_header = (otel_attr_header_t*)&enc_attrs->values[values_off]; + attr_header->vtype = go_attr_value.vtype; + values_off += sizeof(otel_attr_header_t); + switch (go_attr_value.vtype) + { + case BOOL: + case INT64: + case FLOAT64: + bpf_probe_read(&enc_attrs->values[values_off], sizeof(s64), &go_attr_value.numeric); + values_off += sizeof(s64); break; + case STRING: + // go_str = go_attr_value.string; + // if (go_str.len <= 0){ + // return -1; + // } + // if (go_str.len >= OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - values_off - 1) { + // return 0; + // } + // if (values_off >= OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - 1){ + // return 0; + // } + // values_off &= (OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - 1); + // bytes_copied = get_go_string_from_user_ptr(&go_str, (char*)&enc_attrs->values[values_off], OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - sizeof(otel_attr_header_t) - 1); + // if (bytes_copied < 0){ + // return -1; + // } + // values_off += bytes_copied; + // enc_attrs->values[values_off] = 0; + // values_off++; + break; + case BOOLSLICE: + case INT64SLICE: + case FLOAT64SLICE: + case STRINGSLICE: + case INVALID: + default: + continue; } + go_attr++; } + return 0; } +// static __always_inline long convert_go_otel_attribute(go_otel_key_value_t *go_attr, otel_attribute_t *ebpf_attr) +// { +// go_otel_attr_value_t go_attr_value = {0}; +// bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); +// if (go_attr_value.vtype == INVALID) { +// return -1; +// } +// ebpf_attr->vtype = go_attr_value.vtype; +// if (get_go_string_from_user_ptr(&go_attr->key, ebpf_attr->key, OTEL_ATTRIBUTE_KEY_MAX_LENGTH) < 0){ +// return -1; +// } +// long bytes_copied = 0; +// switch (go_attr_value.vtype) +// { +// case BOOL: +// case INT64: +// case FLOAT64: +// bpf_probe_read(ebpf_attr->buf, sizeof(s64), &go_attr_value.numeric); +// bytes_copied = sizeof(s64); +// break; +// case STRING: +// bytes_copied = get_go_string_from_user_ptr(&go_attr_value.string, ebpf_attr->buf, OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE); +// break; +// case BOOLSLICE: +// // TODO +// return -1; +// case INT64SLICE: +// // TODO +// return -1; +// case FLOAT64SLICE: +// // TODO +// return -1; +// case STRINGSLICE: +// // TODO +// return -1; +// case INVALID: +// default: +// return -1; +// } + +// return bytes_copied; +// } + +// static __always_inline void convert_attributes_slice(struct go_slice *attrs_slice, otel_attribute_t *attrs, u8 max_attrs) +// { +// if (attrs_slice == NULL || attrs == NULL){ +// return; +// } +// s64 slice_len = 0; +// bpf_probe_read(&slice_len, sizeof(s64), &attrs_slice->len); +// go_otel_key_value_t *go_attrs = NULL; +// bpf_probe_read(&go_attrs, sizeof(go_otel_key_value_t*), &attrs_slice->array); +// u8 attrs_count = ((slice_len > 4) ? 4 : slice_len); +// for (u32 i = 0; i < 4; i++) +// { +// if (i >= slice_len){ +// break; +// } +// if (convert_go_otel_attribute(&go_attrs[i], &attrs[i]) < 0){ +// break; +// } +// } +// } + #endif \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c index da0a45bbe..11e9c1710 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c @@ -28,7 +28,8 @@ char __license[] SEC("license") = "Dual MIT/GPL"; struct otel_span_t { BASE_SPAN_PROPERTIES char span_name[MAX_SPAN_NAME_LEN]; - otel_attribute_t attributes[MAX_ATTRIBUTES]; + // otel_attribute_t attributes[MAX_ATTRIBUTES]; + otel_attributes_t attributes; }; struct { @@ -104,7 +105,8 @@ int uprobe_End(struct pt_regs *ctx) { return 0; } - convert_attributes_slice((void *)(recording_span_ptr + span_attributes_pos), span->attributes, MAX_ATTRIBUTES); + convert_go_otel_attributes((void *)(recording_span_ptr + span_attributes_pos), &span->attributes); + //convert_attributes_slice((void *)(recording_span_ptr + span_attributes_pos), span->attributes, MAX_ATTRIBUTES); bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, span, sizeof(*span)); return 0; diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go index f26a8400b..e83899181 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go @@ -18,12 +18,9 @@ type bpfOtelSpanT struct { Sc bpfSpanContext Psc bpfSpanContext SpanName [64]int8 - Attributes [4]struct { - Key [64]int8 - IntValue int64 - _ [1016]byte - Vtype uint32 - _ [4]byte + Attributes struct { + Keys [256]uint8 + Values [1024]uint8 } } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go index 1c11e1523..4fd111657 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go @@ -18,12 +18,9 @@ type bpfOtelSpanT struct { Sc bpfSpanContext Psc bpfSpanContext SpanName [64]int8 - Attributes [4]struct { - Key [64]int8 - IntValue int64 - _ [1016]byte - Vtype uint32 - _ [4]byte + Attributes struct { + Keys [256]uint8 + Values [1024]uint8 } } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go index 60a772e3c..d760f2caa 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go @@ -18,7 +18,7 @@ import ( "bytes" "encoding/binary" "errors" - "math" + // "math" "os" // "strconv" @@ -34,7 +34,7 @@ import ( "github.com/go-logr/logr" "golang.org/x/sys/unix" - "go.opentelemetry.io/otel/attribute" + // "go.opentelemetry.io/otel/attribute" // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" "go.opentelemetry.io/otel/trace" @@ -57,7 +57,8 @@ type ebpfAttribute struct { type Event struct { context.BaseSpanProperties SpanName [64]byte - Attributes [4]ebpfAttribute + Keys [256]byte + Values [1024]byte } // Probe is the go.opentelemetry.io/otel/sdk/trace instrumentation probe. @@ -218,14 +219,16 @@ func (h *Probe) convertEvent(e *Event) *probe.Event { pscPtr = nil } - var attributes []attribute.KeyValue - for _, a := range e.Attributes { - h.logger.Info("attribute", "a", a) - if a.Vtype == [8]byte{0, 0, 0, 0} { - continue - } - attributes = append(attributes, convertAttribute(a)) - } + h.logger.Info("attribute keys", "keys", e.Keys) + h.logger.Info("attribute values", "values", e.Values) + // var attributes []attribute.KeyValue + // for _, a := range e.Attributes { + // h.logger.Info("attribute", "a", a) + // if a.Vtype == [8]byte{0, 0, 0, 0} { + // continue + // } + // attributes = append(attributes, convertAttribute(a)) + // } return &probe.Event{ Library: h.LibraryName(), @@ -233,28 +236,28 @@ func (h *Probe) convertEvent(e *Event) *probe.Event { Kind: trace.SpanKindClient, StartTime: int64(e.StartTime), EndTime: int64(e.EndTime), - Attributes: attributes, + // Attributes: attributes, SpanContext: &sc, ParentSpanContext: pscPtr, } } -func convertAttribute(a ebpfAttribute) attribute.KeyValue { - key := unix.ByteSliceToString(a.Key[:]) - vtype := attribute.Type(binary.LittleEndian.Uint32(a.Vtype[:])) - switch vtype { - case attribute.BOOL: - return attribute.Bool(key, binary.LittleEndian.Uint32(a.Value[:]) != 0) - case attribute.INT64: - return attribute.Int64(key, int64(binary.LittleEndian.Uint64(a.Value[:]))) - case attribute.FLOAT64: - return attribute.Float64(key, math.Float64frombits(binary.LittleEndian.Uint64(a.Value[:]))) - case attribute.STRING: - return attribute.String(key, unix.ByteSliceToString(a.Value[:])) - default: - return attribute.String(key, "unknown") - } -} +// func convertAttribute(a ebpfAttribute) attribute.KeyValue { +// key := unix.ByteSliceToString(a.Key[:]) +// vtype := attribute.Type(binary.LittleEndian.Uint32(a.Vtype[:])) +// switch vtype { +// case attribute.BOOL: +// return attribute.Bool(key, binary.LittleEndian.Uint32(a.Value[:]) != 0) +// case attribute.INT64: +// return attribute.Int64(key, int64(binary.LittleEndian.Uint64(a.Value[:]))) +// case attribute.FLOAT64: +// return attribute.Float64(key, math.Float64frombits(binary.LittleEndian.Uint64(a.Value[:]))) +// case attribute.STRING: +// return attribute.String(key, unix.ByteSliceToString(a.Value[:])) +// default: +// return attribute.String(key, "unknown") +// } +// } // Close stops the Probe. func (h *Probe) Close() { diff --git a/internal/pkg/instrumentation/utils/ebpf.go b/internal/pkg/instrumentation/utils/ebpf.go index 3cad8d98d..9a6af667e 100644 --- a/internal/pkg/instrumentation/utils/ebpf.go +++ b/internal/pkg/instrumentation/utils/ebpf.go @@ -33,8 +33,8 @@ func LoadEBPFObjects(spec *ebpf.CollectionSpec, to interface{}, opts *ebpf.Colle // Getting full verifier log is expensive, so we only do it if the user explicitly asks for it. showVerifierLogs := shouldShowVerifierLogs() if showVerifierLogs { - opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 100 - opts.Programs.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelBranch | ebpf.LogLevelStats + opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 10000 + opts.Programs.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelStats } err := spec.LoadAndAssign(to, opts) From 6ba47b8020c45cb28912af9d8c393b4739f003d6 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Sun, 12 Nov 2023 16:05:47 +0200 Subject: [PATCH 07/33] Initial draft --- examples/rolldice/main.go | 3 +- internal/include/otel_types.h | 173 ++++-------------- .../otel/sdk/trace/bpf_bpfel_arm64.go | 10 +- .../otel/sdk/trace/bpf_bpfel_x86.go | 10 +- .../otel/sdk/trace/probe.go | 91 +++++---- 5 files changed, 111 insertions(+), 176 deletions(-) diff --git a/examples/rolldice/main.go b/examples/rolldice/main.go index f07789369..49eb48c3f 100644 --- a/examples/rolldice/main.go +++ b/examples/rolldice/main.go @@ -125,7 +125,8 @@ func (s *Server) rolldice(w http.ResponseWriter, r *http.Request) { rollValueAttr := attribute.Int("roll.value", n) piAttr := attribute.Float64("pi", math.Pi) strAttr := attribute.String("nice.key", "string value!") - span.SetAttributes(rollValueAttr, piAttr, strAttr) + strAttr2 := attribute.String("nice.key2", "string value 2!") + span.SetAttributes(rollValueAttr, piAttr, strAttr, strAttr2) logger.Info("rolldice called", zap.Int("dice", n)) fmt.Fprintf(w, "%v", n) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index 8fb1d305f..bd943dccc 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -44,15 +44,9 @@ typedef struct go_otel_key_value { } go_otel_key_value_t; /* The following structs are the C-formated structs to be used by the eBPF code */ - -/* In the SDK the key is a string, but we must thave a limit */ -// #define OTEL_ATTRIBUTE_KEY_MAX_LENGTH (64) -// #define OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE (1024) -// #define OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(bool)) -// #define OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(s64)) -// #define OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE (OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE / sizeof(double)) #define OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH (256) -#define OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE (1024) +#define OTEL_ATTRIBUTE_NUMERIC_VALUES_BUFFER_SIZE (32) +#define OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE (1024) #define OTEL_ATTRUBUTE_MAX_COUNT OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH / 2 typedef struct otel_attr_header { @@ -62,54 +56,42 @@ typedef struct otel_attr_header { } otel_attr_header_t; typedef struct otel_attributes { - u8 keys[OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH]; - u8 values[OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE]; + otel_attr_header_t headers[OTEL_ATTRUBUTE_MAX_COUNT]; + char keys[OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH]; + s64 numeric_values[OTEL_ATTRIBUTE_NUMERIC_VALUES_BUFFER_SIZE]; + char str_values[OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE]; } otel_attributes_t; -// typedef struct otel_attribute { -// attr_val_type_t vtype; -// char key[OTEL_ATTRIBUTE_KEY_MAX_LENGTH]; -// // union -// // { -// // s64 int_value;// INT64, BOOL -// // double double_value; // FLOAT64 -// // bool bool_buffer[OTEL_ATTRIBUTE_VALUE_MAX_BOOL_SLICE_SIZE]; // BOOLSLICE -// // s64 int_buffer[OTEL_ATTRIBUTE_VALUE_MAX_INT64_SLICE_SIZE]; // INT64SLICE -// // double double_buffer[OTEL_ATTRIBUTE_VALUE_MAX_FLOAT64_SLICE_SIZE]; // FLOAT64SLICE -// // char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; -// // }; -// char buf[OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE]; -// } otel_attribute_t; static __always_inline long convert_go_otel_attributes(struct go_slice *attrs_slice, otel_attributes_t *enc_attrs) { if (attrs_slice == NULL || enc_attrs == NULL){ return -1; } + + bpf_memset((unsigned char*)enc_attrs->headers, 0, sizeof(enc_attrs->headers)); + bpf_memset((unsigned char*)enc_attrs->keys, 0, sizeof(enc_attrs->keys)); + bpf_memset((unsigned char*)enc_attrs->numeric_values, 0, sizeof(enc_attrs->numeric_values)); + bpf_memset((unsigned char*)enc_attrs->str_values, 0, sizeof(enc_attrs->str_values)); + s64 slice_len = 0; bpf_probe_read(&slice_len, sizeof(s64), &attrs_slice->len); go_otel_key_value_t *go_attr = NULL; bpf_probe_read(&go_attr, sizeof(go_otel_key_value_t*), &attrs_slice->array); - u16 keys_off = 0, values_off = 0; + u16 keys_off = 0, str_values_off = 0, numeric_index = 0; s64 key_len = 0; go_otel_attr_value_t go_attr_value = {0}; struct go_string go_str = {0}; - otel_attr_header_t *attr_header = NULL; s64 bytes_copied = 0; - s32 max_str_len = 0; for (u32 i = 0; - i < 32 && - keys_off < OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH && - values_off < OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - sizeof(otel_attr_header_t) - sizeof(s64); + i < OTEL_ATTRUBUTE_MAX_COUNT && + i < slice_len && + keys_off < OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1 && + str_values_off < OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - 1 && + numeric_index < OTEL_ATTRIBUTE_NUMERIC_VALUES_BUFFER_SIZE; i++) { - // if (keys_off >= OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH){ - // break; - // } - // if (i >= slice_len){ - // break; - // } __builtin_memset(&go_attr_value, 0, sizeof(go_otel_attr_value_t)); bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); if (go_attr_value.vtype == INVALID) { @@ -123,127 +105,52 @@ static __always_inline long convert_go_otel_attributes(struct go_slice *attrs_sl break; } keys_off &= (OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1); - // u64 max_key_len = OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - keys_off; - // max_key_len &= (OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1); - key_len = get_go_string_from_user_ptr(&go_str, (char*)&enc_attrs->keys[keys_off], OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE); + key_len = get_go_string_from_user_ptr(&go_str, (char*)&enc_attrs->keys[keys_off], OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH); if (key_len < 0){ break; } keys_off += key_len; - enc_attrs->keys[keys_off] = 0; + // Keep the null terminator between keys keys_off++; - if (values_off >= OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - sizeof(otel_attr_header_t) - sizeof(s64)){ - break; - } - attr_header = (otel_attr_header_t*)&enc_attrs->values[values_off]; - attr_header->vtype = go_attr_value.vtype; - values_off += sizeof(otel_attr_header_t); + enc_attrs->headers[i].vtype = go_attr_value.vtype; switch (go_attr_value.vtype) { case BOOL: case INT64: case FLOAT64: - bpf_probe_read(&enc_attrs->values[values_off], sizeof(s64), &go_attr_value.numeric); - values_off += sizeof(s64); + bpf_probe_read(&enc_attrs->numeric_values[numeric_index], sizeof(s64), &go_attr_value.numeric); + numeric_index++; break; case STRING: - // go_str = go_attr_value.string; - // if (go_str.len <= 0){ - // return -1; - // } - // if (go_str.len >= OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - values_off - 1) { - // return 0; - // } - // if (values_off >= OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - 1){ - // return 0; - // } - // values_off &= (OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - 1); - // bytes_copied = get_go_string_from_user_ptr(&go_str, (char*)&enc_attrs->values[values_off], OTEL_ATTRIBUTE_VALUES_BUFFER_MAX_SIZE - sizeof(otel_attr_header_t) - 1); - // if (bytes_copied < 0){ - // return -1; - // } - // values_off += bytes_copied; - // enc_attrs->values[values_off] = 0; - // values_off++; + go_str = go_attr_value.string; + if (go_str.len <= 0){ + return -1; + } + if (go_str.len >= OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - str_values_off - 1) { + return 0; + } + str_values_off &= (OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - 1); + bytes_copied = get_go_string_from_user_ptr(&go_str, &enc_attrs->str_values[str_values_off], OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE); + if (bytes_copied < 0){ + return -1; + } + str_values_off += bytes_copied; + // Keep the null terminator between strings + str_values_off++; break; + // TODO: handle slices case BOOLSLICE: case INT64SLICE: case FLOAT64SLICE: case STRINGSLICE: case INVALID: default: - continue; + break;; } go_attr++; } return 0; } -// static __always_inline long convert_go_otel_attribute(go_otel_key_value_t *go_attr, otel_attribute_t *ebpf_attr) -// { -// go_otel_attr_value_t go_attr_value = {0}; -// bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); -// if (go_attr_value.vtype == INVALID) { -// return -1; -// } -// ebpf_attr->vtype = go_attr_value.vtype; -// if (get_go_string_from_user_ptr(&go_attr->key, ebpf_attr->key, OTEL_ATTRIBUTE_KEY_MAX_LENGTH) < 0){ -// return -1; -// } -// long bytes_copied = 0; -// switch (go_attr_value.vtype) -// { -// case BOOL: -// case INT64: -// case FLOAT64: -// bpf_probe_read(ebpf_attr->buf, sizeof(s64), &go_attr_value.numeric); -// bytes_copied = sizeof(s64); -// break; -// case STRING: -// bytes_copied = get_go_string_from_user_ptr(&go_attr_value.string, ebpf_attr->buf, OTEL_ATTRIBUTE_VALUE_MAX_BUFFER_SIZE); -// break; -// case BOOLSLICE: -// // TODO -// return -1; -// case INT64SLICE: -// // TODO -// return -1; -// case FLOAT64SLICE: -// // TODO -// return -1; -// case STRINGSLICE: -// // TODO -// return -1; -// case INVALID: -// default: -// return -1; -// } - -// return bytes_copied; -// } - -// static __always_inline void convert_attributes_slice(struct go_slice *attrs_slice, otel_attribute_t *attrs, u8 max_attrs) -// { -// if (attrs_slice == NULL || attrs == NULL){ -// return; -// } -// s64 slice_len = 0; -// bpf_probe_read(&slice_len, sizeof(s64), &attrs_slice->len); -// go_otel_key_value_t *go_attrs = NULL; -// bpf_probe_read(&go_attrs, sizeof(go_otel_key_value_t*), &attrs_slice->array); -// u8 attrs_count = ((slice_len > 4) ? 4 : slice_len); -// for (u32 i = 0; i < 4; i++) -// { -// if (i >= slice_len){ -// break; -// } -// if (convert_go_otel_attribute(&go_attrs[i], &attrs[i]) < 0){ -// break; -// } -// } -// } - - - #endif \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go index e83899181..ecd112c03 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go @@ -19,8 +19,14 @@ type bpfOtelSpanT struct { Psc bpfSpanContext SpanName [64]int8 Attributes struct { - Keys [256]uint8 - Values [1024]uint8 + Headers [128]struct { + ValLength uint16 + Vtype uint8 + Reserved uint8 + } + Keys [256]uint8 + NumericValues [32]int64 + StrValues [1024]int8 } } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go index 4fd111657..e0b754db4 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go @@ -19,8 +19,14 @@ type bpfOtelSpanT struct { Psc bpfSpanContext SpanName [64]int8 Attributes struct { - Keys [256]uint8 - Values [1024]uint8 + Headers [128]struct { + ValLength uint16 + Vtype uint8 + Reserved uint8 + } + Keys [256]uint8 + NumericValues [32]int64 + StrValues [1024]int8 } } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go index d760f2caa..805a48321 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go @@ -18,11 +18,9 @@ import ( "bytes" "encoding/binary" "errors" - // "math" + "math" "os" - // "strconv" - "go.opentelemetry.io/auto/internal/pkg/inject" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs" "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" @@ -34,8 +32,7 @@ import ( "github.com/go-logr/logr" "golang.org/x/sys/unix" - // "go.opentelemetry.io/otel/attribute" - // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" @@ -47,18 +44,25 @@ import ( const instrumentedPkg = "go.opentelemetry.io/otel/sdk/trace" -type ebpfAttribute struct { - Vtype [8]byte - Key [64]byte - Value [1024]byte + +type attributeHeader struct { + ValLength uint16 + Vtype uint8 + Reserved uint8 +} + +type attributesBuffer struct { + Headers [128]attributeHeader + Keys [256]byte + NumericValues [32]int64 + StrValues [1024]byte } // Event represents a manual span created by the user type Event struct { context.BaseSpanProperties SpanName [64]byte - Keys [256]byte - Values [1024]byte + Attributes attributesBuffer } // Probe is the go.opentelemetry.io/otel/sdk/trace instrumentation probe. @@ -219,16 +223,6 @@ func (h *Probe) convertEvent(e *Event) *probe.Event { pscPtr = nil } - h.logger.Info("attribute keys", "keys", e.Keys) - h.logger.Info("attribute values", "values", e.Values) - // var attributes []attribute.KeyValue - // for _, a := range e.Attributes { - // h.logger.Info("attribute", "a", a) - // if a.Vtype == [8]byte{0, 0, 0, 0} { - // continue - // } - // attributes = append(attributes, convertAttribute(a)) - // } return &probe.Event{ Library: h.LibraryName(), @@ -236,28 +230,49 @@ func (h *Probe) convertEvent(e *Event) *probe.Event { Kind: trace.SpanKindClient, StartTime: int64(e.StartTime), EndTime: int64(e.EndTime), - // Attributes: attributes, + Attributes: h.convertAttributes(&e.Attributes), SpanContext: &sc, ParentSpanContext: pscPtr, } } -// func convertAttribute(a ebpfAttribute) attribute.KeyValue { -// key := unix.ByteSliceToString(a.Key[:]) -// vtype := attribute.Type(binary.LittleEndian.Uint32(a.Vtype[:])) -// switch vtype { -// case attribute.BOOL: -// return attribute.Bool(key, binary.LittleEndian.Uint32(a.Value[:]) != 0) -// case attribute.INT64: -// return attribute.Int64(key, int64(binary.LittleEndian.Uint64(a.Value[:]))) -// case attribute.FLOAT64: -// return attribute.Float64(key, math.Float64frombits(binary.LittleEndian.Uint64(a.Value[:]))) -// case attribute.STRING: -// return attribute.String(key, unix.ByteSliceToString(a.Value[:])) -// default: -// return attribute.String(key, "unknown") -// } -// } +func (h *Probe) convertAttributes(a *attributesBuffer) []attribute.KeyValue { + var attributes []attribute.KeyValue + var keyOffset int = 0 + var numericValuesIndex int = 0 + var strValuesOffset int = 0 + for i := 0; i < 128; i++ { + if a.Headers[i].Vtype == uint8(attribute.INVALID) { + break; + } + key := unix.ByteSliceToString(a.Keys[keyOffset : ]) + keyOffset += (len(key) + 1) + switch a.Headers[i].Vtype { + case uint8(attribute.BOOL): + attributes = append(attributes, attribute.Bool(key, a.NumericValues[numericValuesIndex] != 0)) + numericValuesIndex++ + break + case uint8(attribute.INT64): + attributes = append(attributes, attribute.Int64(key, a.NumericValues[numericValuesIndex])) + numericValuesIndex++ + break + case uint8(attribute.FLOAT64): + attributes = append(attributes, attribute.Float64(key, math.Float64frombits(uint64(a.NumericValues[numericValuesIndex])))) + numericValuesIndex++ + break + case uint8(attribute.STRING): + strVal := unix.ByteSliceToString(a.StrValues[strValuesOffset : ]) + attributes = append(attributes, attribute.String(key, strVal)) + strValuesOffset += (len(strVal) + 1) + break + // TODO: handle slices + default: + break + } + } + return attributes +} + // Close stops the Probe. func (h *Probe) Close() { From 390a4b7e10d06bd20b7aba2819ed7114949700ae Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Mon, 20 Nov 2023 17:49:47 +0200 Subject: [PATCH 08/33] Instrument Otel API functions to integrate manual spans with automatic ones --- examples/go.mod | 4 +- examples/go.sum | 4 - examples/rolldice/docker-compose.yaml | 2 +- examples/rolldice/main.go | 101 +----- instrumentation.go | 23 +- internal/include/otel_types.h | 21 +- internal/pkg/inject/offset_results.json | 118 ------- .../otel/global/bpf/probe.bpf.c | 173 ++++++++++ .../{sdk/trace => global}/bpf_bpfel_arm64.go | 55 ++-- .../{sdk/trace => global}/bpf_bpfel_x86.go | 55 ++-- .../go.opentelemetry.io/otel/global/probe.go | 219 +++++++++++++ .../otel/{sdk/trace => global}/probe_test.go | 13 +- .../otel/sdk/trace/bpf/probe.bpf.c | 113 ------- .../otel/sdk/trace/probe.go | 295 ------------------ internal/pkg/instrumentation/manager.go | 11 +- internal/tools/inspect/cmd/offsetgen/main.go | 15 - internal/tools/inspect/render.go | 1 - .../go.opentelemetry.io/otel/sdk/go.mod.tmpl | 5 - .../go.opentelemetry.io/otel/sdk/main.go.tmpl | 15 - 19 files changed, 515 insertions(+), 728 deletions(-) create mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{sdk/trace => global}/bpf_bpfel_arm64.go (63%) rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{sdk/trace => global}/bpf_bpfel_x86.go (63%) create mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{sdk/trace => global}/probe_test.go (94%) delete mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c delete mode 100644 internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go delete mode 100644 internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl delete mode 100644 internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl diff --git a/examples/go.mod b/examples/go.mod index 2eb697d21..31e8375b3 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/mattn/go-sqlite3 v1.14.18 go.opentelemetry.io/otel v1.19.0 - go.opentelemetry.io/otel/sdk v1.19.0 + go.opentelemetry.io/otel/trace v1.19.0 go.uber.org/zap v1.26.0 ) @@ -13,7 +13,5 @@ require ( github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/sys v0.12.0 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index ffc7ec3dc..f4c2f9865 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -13,8 +13,6 @@ go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= @@ -22,6 +20,4 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/examples/rolldice/docker-compose.yaml b/examples/rolldice/docker-compose.yaml index 67bee931c..aaec615f0 100644 --- a/examples/rolldice/docker-compose.yaml +++ b/examples/rolldice/docker-compose.yaml @@ -30,7 +30,7 @@ services: - OTEL_GO_AUTO_TARGET_EXE=/app/main - OTEL_SERVICE_NAME=rolldice - OTEL_PROPAGATORS=tracecontext,baggage - - OTEL_GO_AUTO_SHOW_VERIFIER_LOG=true + - OTEL_GO_AUTO_MANUAL_INTEGRATION=true volumes: - /proc:/host/proc diff --git a/examples/rolldice/main.go b/examples/rolldice/main.go index 49eb48c3f..2e8089a6d 100644 --- a/examples/rolldice/main.go +++ b/examples/rolldice/main.go @@ -16,23 +16,17 @@ package main import ( "context" - "errors" "fmt" "math" "math/rand" "net/http" - "os" - "os/signal" "time" "go.uber.org/zap" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/propagation" - "go.opentelemetry.io/otel/sdk/resource" - "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.17.0" + "go.opentelemetry.io/otel/trace" ) // Server is Http server that exposes multiple endpoints. @@ -44,70 +38,6 @@ var ( tracer = otel.Tracer("rolldice") ) -// setupOTelSDK bootstraps the OpenTelemetry pipeline. -// If it does not return an error, make sure to call shutdown for proper cleanup. -func setupOTelSDK(ctx context.Context, serviceName, serviceVersion string) (shutdown func(context.Context) error, err error) { - var shutdownFuncs []func(context.Context) error - - // shutdown calls cleanup functions registered via shutdownFuncs. - // The errors from the calls are joined. - // Each registered cleanup will be invoked once. - shutdown = func(ctx context.Context) error { - var err error - for _, fn := range shutdownFuncs { - err = errors.Join(err, fn(ctx)) - } - shutdownFuncs = nil - return err - } - - // handleErr calls shutdown for cleanup and makes sure that all errors are returned. - handleErr := func(inErr error) { - err = errors.Join(inErr, shutdown(ctx)) - } - - // Set up resource. - res, err := newResource(serviceName, serviceVersion) - if err != nil { - handleErr(err) - return - } - - // Set up propagator. - prop := newPropagator() - otel.SetTextMapPropagator(prop) - - // Set up trace provider. - tracerProvider, err := newTraceProvider(res) - if err != nil { - handleErr(err) - return - } - shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown) - otel.SetTracerProvider(tracerProvider) - - return -} - -func newResource(serviceName, serviceVersion string) (*resource.Resource, error) { - return resource.NewWithAttributes(semconv.SchemaURL, - semconv.ServiceName(serviceName), - semconv.ServiceVersion(serviceVersion)), nil -} - -func newPropagator() propagation.TextMapPropagator { - return propagation.NewCompositeTextMapPropagator( - propagation.TraceContext{}, - propagation.Baggage{}, - ) -} - -func newTraceProvider(res *resource.Resource) (*trace.TracerProvider, error) { - traceProvider := trace.NewTracerProvider( - trace.WithResource(res), - ) - return traceProvider, nil -} // NewServer creates a server struct after initialing rand. func NewServer() *Server { @@ -117,13 +47,23 @@ func NewServer() *Server { } } +func (s *Server) innerFunction(ctx context.Context) { + _, span := tracer.Start(ctx, "innerFunction") + defer span.End() + + span.SetAttributes(attribute.String("inner.key", "inner.value")) +} + func (s *Server) rolldice(w http.ResponseWriter, r *http.Request) { - _, span := tracer.Start(r.Context(), "roll") + ctx, span := tracer.Start(r.Context(), "roll", trace.WithAttributes()) defer span.End() n := s.rand.Intn(6) + 1 rollValueAttr := attribute.Int("roll.value", n) piAttr := attribute.Float64("pi", math.Pi) + + s.innerFunction(ctx) + strAttr := attribute.String("nice.key", "string value!") strAttr2 := attribute.String("nice.key2", "string value 2!") span.SetAttributes(rollValueAttr, piAttr, strAttr, strAttr2) @@ -148,23 +88,6 @@ func main() { return } - // Handle SIGINT (CTRL+C) gracefully. - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) - defer stop() - - // Set up OpenTelemetry. - serviceName := "dice" - serviceVersion := "0.1.0" - otelShutdown, err := setupOTelSDK(ctx, serviceName, serviceVersion) - if err != nil { - fmt.Printf("error setting up otel sdk, error:%v", err) - return - } - // Handle shutdown properly so nothing leaks. - defer func() { - err = errors.Join(err, otelShutdown(context.Background())) - }() - port := fmt.Sprintf(":%d", 8080) logger.Info("starting http server", zap.String("port", port)) diff --git a/instrumentation.go b/instrumentation.go index 192899e3b..3cbfbf8ab 100644 --- a/instrumentation.go +++ b/instrumentation.go @@ -22,6 +22,7 @@ import ( "os" "path/filepath" "runtime" + "strconv" "strings" "github.com/go-logr/logr" @@ -51,6 +52,9 @@ const ( // envTracesExportersKey is the key for the environment variable value // containing what OpenTelemetry trace exporter to use. envTracesExportersKey = "OTEL_TRACES_EXPORTER" + // envOtelAPIIntegrationKey is the key for the environment variable value + // containing whether to create spans from non recording spans declared manually. + envOtelAPIIntegrationKey = "OTEL_GO_AUTO_MANUAL_INTEGRATION" ) // Instrumentation manages and controls all OpenTelemetry Go @@ -112,7 +116,7 @@ func NewInstrumentation(ctx context.Context, opts ...InstrumentationOption) (*In return nil, err } - mngr, err := instrumentation.NewManager(logger, ctrl) + mngr, err := instrumentation.NewManager(logger, ctrl, c.withOtelAPI) if err != nil { return nil, err } @@ -167,6 +171,7 @@ type instConfig struct { traceExp trace.SpanExporter target process.TargetArgs serviceName string + withOtelAPI bool } func newInstConfig(ctx context.Context, opts []InstrumentationOption) (instConfig, error) { @@ -345,6 +350,11 @@ func WithEnv() InstrumentationOption { if v, ok := lookupServiceName(); ok { c.serviceName = v } + if v, ok := lookupEnv(envOtelAPIIntegrationKey); ok { + if val, err := strconv.ParseBool(v); err == nil { + c.withOtelAPI = val + } + } return c, err }) } @@ -396,3 +406,14 @@ func WithSampler(sampler trace.Sampler) InstrumentationOption { return c, nil }) } + +// WithOtelAPIIntegration returns an [InstrumentationOption] that will configure +// an [Instrumentation] to create spans from non recording spans declared manually +// using the OpenTelemetry API. This is turned off by default and will allow to include +// manually created spans in the trace produced by the auto instrumentation. +func WithOtelAPIIntegration() InstrumentationOption { + return fnOpt(func(_ context.Context, c instConfig) (instConfig, error) { + c.withOtelAPI = true + return c, nil + }) +} diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index bd943dccc..f53661e20 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -63,21 +63,13 @@ typedef struct otel_attributes { } otel_attributes_t; -static __always_inline long convert_go_otel_attributes(struct go_slice *attrs_slice, otel_attributes_t *enc_attrs) +static __always_inline long convert_go_otel_attributes(void *attrs_buf, s64 slice_len, otel_attributes_t *enc_attrs) { - if (attrs_slice == NULL || enc_attrs == NULL){ + if (attrs_buf == NULL || enc_attrs == NULL){ return -1; } - bpf_memset((unsigned char*)enc_attrs->headers, 0, sizeof(enc_attrs->headers)); - bpf_memset((unsigned char*)enc_attrs->keys, 0, sizeof(enc_attrs->keys)); - bpf_memset((unsigned char*)enc_attrs->numeric_values, 0, sizeof(enc_attrs->numeric_values)); - bpf_memset((unsigned char*)enc_attrs->str_values, 0, sizeof(enc_attrs->str_values)); - - s64 slice_len = 0; - bpf_probe_read(&slice_len, sizeof(s64), &attrs_slice->len); - go_otel_key_value_t *go_attr = NULL; - bpf_probe_read(&go_attr, sizeof(go_otel_key_value_t*), &attrs_slice->array); + go_otel_key_value_t *go_attr = (go_otel_key_value_t*)attrs_buf; u16 keys_off = 0, str_values_off = 0, numeric_index = 0; s64 key_len = 0; @@ -97,11 +89,13 @@ static __always_inline long convert_go_otel_attributes(struct go_slice *attrs_sl if (go_attr_value.vtype == INVALID) { break; } + // Read the key string bpf_probe_read(&go_str, sizeof(struct go_string), &go_attr->key); if (go_str.len <= 0){ break; } if (go_str.len >= OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - keys_off - 1) { + // No room left in keys buffer break; } keys_off &= (OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1); @@ -128,7 +122,8 @@ static __always_inline long convert_go_otel_attributes(struct go_slice *attrs_sl return -1; } if (go_str.len >= OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - str_values_off - 1) { - return 0; + // No room left in string values buffer + return -1; } str_values_off &= (OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - 1); bytes_copied = get_go_string_from_user_ptr(&go_str, &enc_attrs->str_values[str_values_off], OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE); @@ -136,7 +131,7 @@ static __always_inline long convert_go_otel_attributes(struct go_slice *attrs_sl return -1; } str_values_off += bytes_copied; - // Keep the null terminator between strings + // Keep the null terminator between string values str_values_off++; break; // TODO: handle slices diff --git a/internal/pkg/inject/offset_results.json b/internal/pkg/inject/offset_results.json index 1d2cbfcf7..6d144fdc1 100755 --- a/internal/pkg/inject/offset_results.json +++ b/internal/pkg/inject/offset_results.json @@ -1,122 +1,4 @@ [ - { - "module": "go.opentelemetry.io/otel/sdk", - "packages": [ - { - "package": "go.opentelemetry.io/otel/sdk/trace", - "structs": [ - { - "struct": "recordingSpan", - "fields": [ - { - "field": "attributes", - "offsets": [ - { - "offset": 240, - "versions": [ - "1.4.0", - "1.4.1", - "1.5.0", - "1.6.0", - "1.6.1", - "1.6.2", - "1.6.3", - "1.7.0", - "1.8.0", - "1.9.0", - "1.10.0", - "1.11.0", - "1.11.1", - "1.11.2", - "1.12.0", - "1.13.0", - "1.14.0", - "1.15.0-rc.1", - "1.15.0-rc.2", - "1.15.0", - "1.15.1", - "1.16.0-rc.1", - "1.16.0", - "1.17.0", - "1.18.0", - "1.19.0-rc.1", - "1.19.0" - ] - }, - { - "offset": 256, - "versions": [ - "1.20.0" - ] - }, - { - "offset": 296, - "versions": [ - "1.0.0-RC3", - "1.0.0", - "1.0.1", - "1.1.0", - "1.2.0", - "1.3.0" - ] - } - ] - }, - { - "field": "name", - "offsets": [ - { - "offset": 80, - "versions": [ - "1.0.0-RC3", - "1.0.0", - "1.0.1", - "1.1.0", - "1.2.0", - "1.3.0", - "1.4.0", - "1.4.1", - "1.5.0", - "1.6.0", - "1.6.1", - "1.6.2", - "1.6.3", - "1.7.0", - "1.8.0", - "1.9.0", - "1.10.0", - "1.11.0", - "1.11.1", - "1.11.2", - "1.12.0", - "1.13.0", - "1.14.0", - "1.15.0-rc.1", - "1.15.0-rc.2", - "1.15.0", - "1.15.1", - "1.16.0-rc.1", - "1.16.0", - "1.17.0", - "1.18.0", - "1.19.0-rc.1", - "1.19.0" - ] - }, - { - "offset": 96, - "versions": [ - "1.20.0" - ] - } - ] - } - ] - } - ] - } - ] - }, { "module": "golang.org/x/net", "packages": [ diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c new file mode 100644 index 000000000..d5e138d98 --- /dev/null +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c @@ -0,0 +1,173 @@ +// Copyright The OpenTelemetry Authors +// +// 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 language governing permissions and +// limitations under the License. + +#include "arguments.h" +#include "span_context.h" +#include "go_context.h" +#include "go_types.h" +#include "uprobe.h" +#include "otel_types.h" + +char __license[] SEC("license") = "Dual MIT/GPL"; + +#define MAX_CONCURRENT 50 +#define MAX_SPAN_NAME_LEN 64 +#define MAX_ATTRIBUTES 4 + +struct span_name_t { + char buf[MAX_SPAN_NAME_LEN]; +}; + +struct otel_span_t { + BASE_SPAN_PROPERTIES + struct span_name_t span_name; + otel_attributes_t attributes; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, void*); + __type(value, struct otel_span_t); + __uint(max_entries, MAX_CONCURRENT); +} active_spans_by_span_ptr SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, void*); + __type(value, struct span_name_t); + __uint(max_entries, MAX_CONCURRENT); +} span_name_by_context SEC(".maps"); + +struct +{ + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(struct otel_span_t)); + __uint(max_entries, 2); +} otel_span_storage_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); +} events SEC(".maps"); + +// Injected in init +volatile const u64 span_name_pos; +volatile const u64 span_attributes_pos; + +// This instrumentation attaches uprobe to the following function: +// func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) +SEC("uprobe/Start") +int uprobe_Start(struct pt_regs *ctx) { + struct span_name_t span_name = {0}; + + // Getting span name + void *span_name_ptr = get_argument(ctx, 4); + u64 span_name_len = (u64)get_argument(ctx, 5); + u64 span_name_size = MAX_SPAN_NAME_LEN < span_name_len ? MAX_SPAN_NAME_LEN : span_name_len; + bpf_probe_read(span_name.buf, span_name_size, span_name_ptr); + + // Save the span name in map to be read once the Start function returns + void *context_ptr_val = get_Go_context(ctx, 3, 0, true); + void *key = get_consistent_key(ctx, context_ptr_val); + bpf_map_update_elem(&span_name_by_context, &key, &span_name, 0); + return 0; +} + +// This instrumentation attaches uprobe to the following function: +// func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) +SEC("uprobe/Start") +int uprobe_Start_Returns(struct pt_regs *ctx) { + // Get the span name passed to the Start function + void *context_ptr_val = get_Go_context(ctx, 3, 0, true); + void *key = get_consistent_key(ctx, context_ptr_val); + struct span_name_t *span_name = bpf_map_lookup_elem(&span_name_by_context, &key); + if (span_name == NULL) { + return 0; + } + bpf_map_delete_elem(&span_name_by_context, &key); + + u32 zero_span_key = 0; + struct otel_span_t *zero_span = bpf_map_lookup_elem(&otel_span_storage_map, &zero_span_key); + if (zero_span == NULL) { + return 0; + } + + u32 otel_span_key = 1; + // Zero the span we are about to build, eBPF doesn't support memset of large structs (more than 1024 bytes) + bpf_map_update_elem(&otel_span_storage_map, &otel_span_key, zero_span, 0); + // Get a pointer to the zeroed span + struct otel_span_t *otel_span = bpf_map_lookup_elem(&otel_span_storage_map, &otel_span_key); + if (otel_span == NULL) { + return 0; + } + + otel_span->start_time = bpf_ktime_get_ns(); + copy_byte_arrays((unsigned char*)span_name->buf, (unsigned char*)otel_span->span_name.buf, MAX_SPAN_NAME_LEN); + + // Get the ** returned ** context and Span (concrete type of the interfaces) + void *ret_context_ptr_val = get_argument(ctx, 2); + void *span_ptr_val = get_argument(ctx, 4); + + struct span_context *span_ctx = get_parent_span_context(ret_context_ptr_val); + if (span_ctx != NULL) { + // Set the parent context + bpf_probe_read(&otel_span->psc, sizeof(otel_span->psc), span_ctx); + copy_byte_arrays(otel_span->psc.TraceID, otel_span->sc.TraceID, TRACE_ID_SIZE); + generate_random_bytes(otel_span->sc.SpanID, SPAN_ID_SIZE); + } else { + otel_span->sc = generate_span_context(); + } + + bpf_map_update_elem(&active_spans_by_span_ptr, &span_ptr_val, otel_span, 0); + start_tracking_span(ret_context_ptr_val, &otel_span->sc); + return 0; +} + +// This instrumentation attaches uprobe to the following function: +// func (*nonRecordingSpan) SetAttributes(...attribute.KeyValue) +SEC("uprobe/SetAttributes") +int uprobe_SetAttributes(struct pt_regs *ctx) { + void *non_recording_span_ptr = get_argument(ctx, 1); + struct otel_span_t *span = bpf_map_lookup_elem(&active_spans_by_span_ptr, &non_recording_span_ptr); + if (span == NULL) { + return 0; + } + + // In Go, "..." is equilaent to passing a slice: https://go.dev/ref/spec#Passing_arguments_to_..._parameters + void *attributes_usr_buf = get_argument(ctx, 2); + u64 attributes_len = (u64)get_argument(ctx, 3); + convert_go_otel_attributes(attributes_usr_buf, attributes_len, &span->attributes); + + // Update the map entry with the new attributes + bpf_map_update_elem(&active_spans_by_span_ptr, &non_recording_span_ptr, span, 0); + return 0; +} + + +// This instrumentation attaches uprobe to the following function: +// func (*nonRecordingSpan) End(...trace.SpanEndOption) +SEC("uprobe/End") +int uprobe_End(struct pt_regs *ctx) { + void *non_recording_span_ptr = get_argument(ctx, 1); + struct otel_span_t *span = bpf_map_lookup_elem(&active_spans_by_span_ptr, &non_recording_span_ptr); + if (span == NULL) { + return 0; + } + span->end_time = bpf_ktime_get_ns(); + bpf_map_delete_elem(&active_spans_by_span_ptr, &non_recording_span_ptr); + stop_tracking_span(&span->sc, &span->psc); + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, span, sizeof(*span)); + return 0; +} \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go similarity index 63% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go index ecd112c03..ddf4ee71a 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_arm64.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go @@ -1,7 +1,7 @@ // Code generated by bpf2go; DO NOT EDIT. //go:build arm64 -package sdk +package global import ( "bytes" @@ -17,14 +17,14 @@ type bpfOtelSpanT struct { EndTime uint64 Sc bpfSpanContext Psc bpfSpanContext - SpanName [64]int8 + SpanName bpfSpanNameT Attributes struct { Headers [128]struct { ValLength uint16 Vtype uint8 Reserved uint8 } - Keys [256]uint8 + Keys [256]int8 NumericValues [32]int64 StrValues [1024]int8 } @@ -37,6 +37,8 @@ type bpfSpanContext struct { SpanID [8]uint8 } +type bpfSpanNameT struct{ Buf [64]int8 } + // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) @@ -78,21 +80,24 @@ type bpfSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { - UprobeEnd *ebpf.ProgramSpec `ebpf:"uprobe_End"` - UprobeStartReturns *ebpf.ProgramSpec `ebpf:"uprobe_Start_Returns"` + UprobeEnd *ebpf.ProgramSpec `ebpf:"uprobe_End"` + UprobeSetAttributes *ebpf.ProgramSpec `ebpf:"uprobe_SetAttributes"` + UprobeStart *ebpf.ProgramSpec `ebpf:"uprobe_Start"` + UprobeStartReturns *ebpf.ProgramSpec `ebpf:"uprobe_Start_Returns"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` - AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` - Events *ebpf.MapSpec `ebpf:"events"` - OtelSpanStorageMap *ebpf.MapSpec `ebpf:"otel_span_storage_map"` - SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` + ActiveSpansBySpanPtr *ebpf.MapSpec `ebpf:"active_spans_by_span_ptr"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + OtelSpanStorageMap *ebpf.MapSpec `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + SpanNameByContext *ebpf.MapSpec `ebpf:"span_name_by_context"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -114,22 +119,24 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - ActiveSpans *ebpf.Map `ebpf:"active_spans"` - AllocMap *ebpf.Map `ebpf:"alloc_map"` - Events *ebpf.Map `ebpf:"events"` - OtelSpanStorageMap *ebpf.Map `ebpf:"otel_span_storage_map"` - SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` + ActiveSpansBySpanPtr *ebpf.Map `ebpf:"active_spans_by_span_ptr"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + OtelSpanStorageMap *ebpf.Map `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + SpanNameByContext *ebpf.Map `ebpf:"span_name_by_context"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` } func (m *bpfMaps) Close() error { return _BpfClose( - m.ActiveSpans, + m.ActiveSpansBySpanPtr, m.AllocMap, m.Events, m.OtelSpanStorageMap, m.SliceArrayBuffMap, + m.SpanNameByContext, m.TrackedSpans, m.TrackedSpansBySc, ) @@ -139,13 +146,17 @@ func (m *bpfMaps) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { - UprobeEnd *ebpf.Program `ebpf:"uprobe_End"` - UprobeStartReturns *ebpf.Program `ebpf:"uprobe_Start_Returns"` + UprobeEnd *ebpf.Program `ebpf:"uprobe_End"` + UprobeSetAttributes *ebpf.Program `ebpf:"uprobe_SetAttributes"` + UprobeStart *ebpf.Program `ebpf:"uprobe_Start"` + UprobeStartReturns *ebpf.Program `ebpf:"uprobe_Start_Returns"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.UprobeEnd, + p.UprobeSetAttributes, + p.UprobeStart, p.UprobeStartReturns, ) } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go similarity index 63% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go index e0b754db4..b3e810502 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf_bpfel_x86.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go @@ -1,7 +1,7 @@ // Code generated by bpf2go; DO NOT EDIT. //go:build 386 || amd64 -package sdk +package global import ( "bytes" @@ -17,14 +17,14 @@ type bpfOtelSpanT struct { EndTime uint64 Sc bpfSpanContext Psc bpfSpanContext - SpanName [64]int8 + SpanName bpfSpanNameT Attributes struct { Headers [128]struct { ValLength uint16 Vtype uint8 Reserved uint8 } - Keys [256]uint8 + Keys [256]int8 NumericValues [32]int64 StrValues [1024]int8 } @@ -37,6 +37,8 @@ type bpfSpanContext struct { SpanID [8]uint8 } +type bpfSpanNameT struct{ Buf [64]int8 } + // loadBpf returns the embedded CollectionSpec for bpf. func loadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) @@ -78,21 +80,24 @@ type bpfSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfProgramSpecs struct { - UprobeEnd *ebpf.ProgramSpec `ebpf:"uprobe_End"` - UprobeStartReturns *ebpf.ProgramSpec `ebpf:"uprobe_Start_Returns"` + UprobeEnd *ebpf.ProgramSpec `ebpf:"uprobe_End"` + UprobeSetAttributes *ebpf.ProgramSpec `ebpf:"uprobe_SetAttributes"` + UprobeStart *ebpf.ProgramSpec `ebpf:"uprobe_Start"` + UprobeStartReturns *ebpf.ProgramSpec `ebpf:"uprobe_Start_Returns"` } // bpfMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - ActiveSpans *ebpf.MapSpec `ebpf:"active_spans"` - AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` - Events *ebpf.MapSpec `ebpf:"events"` - OtelSpanStorageMap *ebpf.MapSpec `ebpf:"otel_span_storage_map"` - SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` + ActiveSpansBySpanPtr *ebpf.MapSpec `ebpf:"active_spans_by_span_ptr"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + OtelSpanStorageMap *ebpf.MapSpec `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + SpanNameByContext *ebpf.MapSpec `ebpf:"span_name_by_context"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -114,22 +119,24 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - ActiveSpans *ebpf.Map `ebpf:"active_spans"` - AllocMap *ebpf.Map `ebpf:"alloc_map"` - Events *ebpf.Map `ebpf:"events"` - OtelSpanStorageMap *ebpf.Map `ebpf:"otel_span_storage_map"` - SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` + ActiveSpansBySpanPtr *ebpf.Map `ebpf:"active_spans_by_span_ptr"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + OtelSpanStorageMap *ebpf.Map `ebpf:"otel_span_storage_map"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + SpanNameByContext *ebpf.Map `ebpf:"span_name_by_context"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` } func (m *bpfMaps) Close() error { return _BpfClose( - m.ActiveSpans, + m.ActiveSpansBySpanPtr, m.AllocMap, m.Events, m.OtelSpanStorageMap, m.SliceArrayBuffMap, + m.SpanNameByContext, m.TrackedSpans, m.TrackedSpansBySc, ) @@ -139,13 +146,17 @@ func (m *bpfMaps) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfPrograms struct { - UprobeEnd *ebpf.Program `ebpf:"uprobe_End"` - UprobeStartReturns *ebpf.Program `ebpf:"uprobe_Start_Returns"` + UprobeEnd *ebpf.Program `ebpf:"uprobe_End"` + UprobeSetAttributes *ebpf.Program `ebpf:"uprobe_SetAttributes"` + UprobeStart *ebpf.Program `ebpf:"uprobe_Start"` + UprobeStartReturns *ebpf.Program `ebpf:"uprobe_Start_Returns"` } func (p *bpfPrograms) Close() error { return _BpfClose( p.UprobeEnd, + p.UprobeSetAttributes, + p.UprobeStart, p.UprobeStartReturns, ) } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go new file mode 100644 index 000000000..0cfde854d --- /dev/null +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go @@ -0,0 +1,219 @@ +// Copyright The OpenTelemetry Authors +// +// 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 language governing permissions and +// limitations under the License. + +package global + +import ( + "math" + "os" + + "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" + + "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/perf" + "github.com/go-logr/logr" + "golang.org/x/sys/unix" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" + "go.opentelemetry.io/auto/internal/pkg/process" +) + +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target amd64,arm64 -cc clang -cflags $CFLAGS bpf ./bpf/probe.bpf.c + +const ( + // name is the instrumentation name. + name = "go.opentelemetry.io/otel/internal/global" + // pkg is the package being instrumented. + pkg = "go.opentelemetry.io/otel/internal/global" + // maxAttributes is the maximum number of attributes that can be added to a span. + maxAttributes = 128 +) + +// New returns a new [probe.Probe]. +func New(logger logr.Logger) probe.Probe { + return &probe.Base[bpfObjects, event]{ + Name: name, + Logger: logger.WithName(name), + InstrumentedPkg: pkg, + Consts: []probe.Const{ + probe.RegistersABIConst{}, + probe.AllocationConst{}, + }, + Uprobes: map[string]probe.UprobeFunc[bpfObjects]{ + "go.opentelemetry.io/otel/internal/global.(*tracer).Start": uprobeTracerStart, + "go.opentelemetry.io/otel/internal/global.(*nonRecordingSpan).SetAttributes": uprobeSetAttributes, + "go.opentelemetry.io/otel/internal/global.(*nonRecordingSpan).End": uprobeSpanEnd, + }, + + ReaderFn: func(obj bpfObjects) (*perf.Reader, error) { + return perf.NewReader(obj.Events, os.Getpagesize()*8) + }, + SpecFn: loadBpf, + ProcessFn: convertEvent, + } +} + +func uprobeTracerStart(name string, exec *link.Executable, target *process.TargetDetails, obj *bpfObjects) ([]link.Link, error) { + offset, err := target.GetFunctionOffset(name) + if err != nil { + return nil, err + } + + opts := &link.UprobeOptions{Address: offset} + l, err := exec.Uprobe("", obj.UprobeStart, opts) + if err != nil { + return nil, err + } + + links := []link.Link{l} + + retOffsets, err := target.GetFunctionReturns(name) + if err != nil { + return nil, err + } + + for _, ret := range retOffsets { + opts := &link.UprobeOptions{Address: ret} + l, err := exec.Uprobe("", obj.UprobeStartReturns, opts) + if err != nil { + return nil, err + } + links = append(links, l) + } + + return links, nil +} + +func uprobeSetAttributes(name string, exec *link.Executable, target *process.TargetDetails, obj *bpfObjects) ([]link.Link, error) { + offset, err := target.GetFunctionOffset(name) + if err != nil { + return nil, err + } + + opts := &link.UprobeOptions{Address: offset} + l, err := exec.Uprobe("", obj.UprobeSetAttributes, opts) + if err != nil { + return nil, err + } + + links := []link.Link{l} + + return links, nil +} + +func uprobeSpanEnd(name string, exec *link.Executable, target *process.TargetDetails, obj *bpfObjects) ([]link.Link, error) { + offset, err := target.GetFunctionOffset(name) + if err != nil { + return nil, err + } + + opts := &link.UprobeOptions{Address: offset} + l, err := exec.Uprobe("", obj.UprobeEnd, opts) + if err != nil { + return nil, err + } + + links := []link.Link{l} + + return links, nil +} + +type attributeHeader struct { + ValLength uint16 + Vtype uint8 + Reserved uint8 +} + +type attributesBuffer struct { + Headers [128]attributeHeader + Keys [256]byte + NumericValues [32]int64 + StrValues [1024]byte +} + +// event represents a manual span created by the user. +type event struct { + context.BaseSpanProperties + SpanName [64]byte + Attributes attributesBuffer +} + +func convertEvent(e *event) *probe.Event { + spanName := unix.ByteSliceToString(e.SpanName[:]) + + sc := trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: e.SpanContext.TraceID, + SpanID: e.SpanContext.SpanID, + TraceFlags: trace.FlagsSampled, + }) + + var pscPtr *trace.SpanContext + if e.ParentSpanContext.TraceID.IsValid() { + psc := trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: e.ParentSpanContext.TraceID, + SpanID: e.ParentSpanContext.SpanID, + TraceFlags: trace.FlagsSampled, + Remote: true, + }) + pscPtr = &psc + } else { + pscPtr = nil + } + + return &probe.Event{ + Package: pkg, + Name: spanName, + Kind: trace.SpanKindClient, + StartTime: int64(e.StartTime), + EndTime: int64(e.EndTime), + Attributes: convertAttributes(&e.Attributes), + SpanContext: &sc, + ParentSpanContext: pscPtr, + } +} + +func convertAttributes(a *attributesBuffer) []attribute.KeyValue { + var attributes []attribute.KeyValue + var keyOffset int + var numericValuesIndex int + var strValuesOffset int + for i := 0; i < maxAttributes; i++ { + if a.Headers[i].Vtype == uint8(attribute.INVALID) { + break + } + key := unix.ByteSliceToString(a.Keys[keyOffset:]) + keyOffset += (len(key) + 1) + switch a.Headers[i].Vtype { + case uint8(attribute.BOOL): + attributes = append(attributes, attribute.Bool(key, a.NumericValues[numericValuesIndex] != 0)) + numericValuesIndex++ + case uint8(attribute.INT64): + attributes = append(attributes, attribute.Int64(key, a.NumericValues[numericValuesIndex])) + numericValuesIndex++ + case uint8(attribute.FLOAT64): + attributes = append(attributes, attribute.Float64(key, math.Float64frombits(uint64(a.NumericValues[numericValuesIndex])))) + numericValuesIndex++ + case uint8(attribute.STRING): + strVal := unix.ByteSliceToString(a.StrValues[strValuesOffset:]) + attributes = append(attributes, attribute.String(key, strVal)) + strValuesOffset += (len(strVal) + 1) + // TODO: handle slices + default: + } + } + return attributes +} diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe_test.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go similarity index 94% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe_test.go rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go index e1ed4fe1a..ef9b2e86c 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe_test.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go @@ -12,21 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package sdk +package global import ( "testing" - // "time" - + // "time". // "github.com/go-logr/logr/testr" - // "github.com/stretchr/testify/assert" - + // "github.com/stretchr/testify/assert". // "go.opentelemetry.io/otel/attribute" // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" - // "go.opentelemetry.io/otel/trace" - + // "go.opentelemetry.io/otel/trace". // "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" - // "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" + // "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe". ) func TestProbeConvertEvent(t *testing.T) { diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c deleted file mode 100644 index 11e9c1710..000000000 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/bpf/probe.bpf.c +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// 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 language governing permissions and -// limitations under the License. - -#include "arguments.h" -#include "span_context.h" -#include "go_context.h" -#include "go_types.h" -#include "uprobe.h" -#include "otel_types.h" - -char __license[] SEC("license") = "Dual MIT/GPL"; - -#define MAX_CONCURRENT 50 -#define MAX_SPAN_NAME_LEN 64 -#define MAX_ATTRIBUTES 4 - -struct otel_span_t { - BASE_SPAN_PROPERTIES - char span_name[MAX_SPAN_NAME_LEN]; - // otel_attribute_t attributes[MAX_ATTRIBUTES]; - otel_attributes_t attributes; -}; - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __type(key, void*); - __type(value, struct otel_span_t); - __uint(max_entries, MAX_CONCURRENT); -} active_spans SEC(".maps"); - -struct -{ - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); - __uint(key_size, sizeof(u32)); - __uint(value_size, sizeof(struct otel_span_t)); - __uint(max_entries, 1); -} otel_span_storage_map SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); -} events SEC(".maps"); - -// Injected in init -volatile const u64 span_name_pos; -volatile const u64 span_attributes_pos; - -// This instrumentation attaches uprobe to the following function: -// func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) -SEC("uprobe/Start") -int uprobe_Start_Returns(struct pt_regs *ctx) { - u32 map_key = 0; - struct otel_span_t *otel_span = bpf_map_lookup_elem(&otel_span_storage_map, &map_key); - if (otel_span == NULL) { - return 0; - } - bpf_memset((unsigned char *)otel_span, 0, sizeof(*otel_span)); - otel_span->start_time = bpf_ktime_get_ns(); - - // Get the ** returned ** context and Span (concrete type of the interfaces) - void *context_ptr_val = get_argument(ctx, 2); - void *span_ptr_val = get_argument(ctx, 4); - - struct span_context *span_ctx = get_parent_span_context(context_ptr_val); - if (span_ctx != NULL) { - // Set the parent context - bpf_probe_read(&otel_span->psc, sizeof(otel_span->psc), span_ctx); - copy_byte_arrays(otel_span->psc.TraceID, otel_span->sc.TraceID, TRACE_ID_SIZE); - generate_random_bytes(otel_span->sc.SpanID, SPAN_ID_SIZE); - } else { - otel_span->sc = generate_span_context(); - } - - bpf_map_update_elem(&active_spans, &span_ptr_val, otel_span, 0); - start_tracking_span(context_ptr_val, &otel_span->sc); - return 0; -} - - -// This instrumentation attaches uprobe to the following function: -// unc (s *recordingSpan) End(options ...trace.SpanEndOption) -SEC("uprobe/End") -int uprobe_End(struct pt_regs *ctx) { - void *recording_span_ptr = get_argument(ctx, 1); - struct otel_span_t *span = bpf_map_lookup_elem(&active_spans, &recording_span_ptr); - if (span == NULL) { - return 0; - } - span->end_time = bpf_ktime_get_ns(); - bpf_map_delete_elem(&active_spans, &recording_span_ptr); - stop_tracking_span(&span->sc, &span->psc); - - if (get_go_string_from_user_ptr((void *)(recording_span_ptr + span_name_pos), span->span_name, sizeof(span->span_name)) < 0) { - bpf_printk("failed to get span name from manual span"); - return 0; - } - - convert_go_otel_attributes((void *)(recording_span_ptr + span_attributes_pos), &span->attributes); - //convert_attributes_slice((void *)(recording_span_ptr + span_attributes_pos), span->attributes, MAX_ATTRIBUTES); - - bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, span, sizeof(*span)); - return 0; -} \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go deleted file mode 100644 index 805a48321..000000000 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace/probe.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// 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 language governing permissions and -// limitations under the License. - -package sdk - -import ( - "bytes" - "encoding/binary" - "errors" - "math" - "os" - - "go.opentelemetry.io/auto/internal/pkg/inject" - "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs" - "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" - "go.opentelemetry.io/auto/internal/pkg/structfield" - - "github.com/cilium/ebpf" - "github.com/cilium/ebpf/link" - "github.com/cilium/ebpf/perf" - "github.com/go-logr/logr" - "golang.org/x/sys/unix" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - - "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" - "go.opentelemetry.io/auto/internal/pkg/instrumentation/utils" - "go.opentelemetry.io/auto/internal/pkg/process" -) - -//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target amd64,arm64 -cc clang -cflags $CFLAGS bpf ./bpf/probe.bpf.c - -const instrumentedPkg = "go.opentelemetry.io/otel/sdk/trace" - - -type attributeHeader struct { - ValLength uint16 - Vtype uint8 - Reserved uint8 -} - -type attributesBuffer struct { - Headers [128]attributeHeader - Keys [256]byte - NumericValues [32]int64 - StrValues [1024]byte -} - -// Event represents a manual span created by the user -type Event struct { - context.BaseSpanProperties - SpanName [64]byte - Attributes attributesBuffer -} - -// Probe is the go.opentelemetry.io/otel/sdk/trace instrumentation probe. -type Probe struct { - logger logr.Logger - bpfObjects *bpfObjects - uprobes []link.Link - returnProbs []link.Link - eventsReader *perf.Reader -} - -// New returns a new [Probe]. -func New(logger logr.Logger) *Probe { - return &Probe{logger: logger.WithName("Probe/otel")} -} - -// LibraryName returns the /otel/sdk/trace package name. -func (h *Probe) LibraryName() string { - return instrumentedPkg -} - -// FuncNames returns the function names from "go.opentelemetry.io/otel/sdk/trace" that are instrumented. -func (h *Probe) FuncNames() []string { - return []string{ - "go.opentelemetry.io/otel/sdk/trace.(*tracer).Start", - "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End", - } -} - -// Load loads all instrumentation offsets. -func (h *Probe) Load(exec *link.Executable, target *process.TargetDetails) error { - const otelSdkMod = "go.opentelemetry.io/otel/sdk" - otelSdkVer := target.Libraries[otelSdkMod] - - spec, err := loadBpf() - if err != nil { - return err - } - if target.AllocationDetails == nil { - // This Probe requires allocation. - return errors.New("no allocation details") - } - err = inject.Constants( - spec, - inject.WithRegistersABI(target.IsRegistersABI()), - inject.WithOffset( - "span_name_pos", - structfield.NewID(otelSdkMod, "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "name"), - otelSdkVer, - ), - inject.WithOffset( - "span_attributes_pos", - structfield.NewID(otelSdkMod, "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "attributes"), - otelSdkVer, - ), - ) - if err != nil { - return err - } - - h.bpfObjects = &bpfObjects{} - - err = utils.LoadEBPFObjects(spec, h.bpfObjects, &ebpf.CollectionOptions{ - Maps: ebpf.MapOptions{ - PinPath: bpffs.PathForTargetApplication(target), - }, - }) - - if err != nil { - return err - } - - retOffsets, err := target.GetFunctionReturns(h.FuncNames()[0]) - if err != nil { - return err - } - - for _, ret := range retOffsets { - retProbe, err := exec.Uprobe("", h.bpfObjects.UprobeStartReturns, &link.UprobeOptions{ - Address: ret, - }) - if err != nil { - return err - } - h.returnProbs = append(h.returnProbs, retProbe) - } - - offset, err := target.GetFunctionOffset(h.FuncNames()[1]) - if err != nil { - return err - } - - up, err := exec.Uprobe("", h.bpfObjects.UprobeEnd, &link.UprobeOptions{ - Address: offset, - }) - if err != nil { - return err - } - - h.uprobes = append(h.uprobes, up) - - // Getting the attribute defined from the user might require more memory - rd, err := perf.NewReader(h.bpfObjects.Events, os.Getpagesize() * 8) - if err != nil { - return err - } - h.eventsReader = rd - - return nil -} - -// Run runs the events processing loop. -func (h *Probe) Run(eventsChan chan<- *probe.Event) { - var event Event - for { - record, err := h.eventsReader.Read() - if err != nil { - if errors.Is(err, perf.ErrClosed) { - return - } - h.logger.Error(err, "error reading from perf reader") - continue - } - - if record.LostSamples != 0 { - h.logger.V(0).Info("perf event ring buffer full", "dropped", record.LostSamples) - continue - } - - if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { - h.logger.Error(err, "error parsing perf event") - continue - } - - eventsChan <- h.convertEvent(&event) - } -} - -func (h *Probe) convertEvent(e *Event) *probe.Event { - spanName := unix.ByteSliceToString(e.SpanName[:]) - - sc := trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: e.SpanContext.TraceID, - SpanID: e.SpanContext.SpanID, - TraceFlags: trace.FlagsSampled, - }) - - var pscPtr *trace.SpanContext - if e.ParentSpanContext.TraceID.IsValid() { - psc := trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: e.ParentSpanContext.TraceID, - SpanID: e.ParentSpanContext.SpanID, - TraceFlags: trace.FlagsSampled, - Remote: true, - }) - pscPtr = &psc - } else { - pscPtr = nil - } - - - return &probe.Event{ - Library: h.LibraryName(), - Name: spanName, - Kind: trace.SpanKindClient, - StartTime: int64(e.StartTime), - EndTime: int64(e.EndTime), - Attributes: h.convertAttributes(&e.Attributes), - SpanContext: &sc, - ParentSpanContext: pscPtr, - } -} - -func (h *Probe) convertAttributes(a *attributesBuffer) []attribute.KeyValue { - var attributes []attribute.KeyValue - var keyOffset int = 0 - var numericValuesIndex int = 0 - var strValuesOffset int = 0 - for i := 0; i < 128; i++ { - if a.Headers[i].Vtype == uint8(attribute.INVALID) { - break; - } - key := unix.ByteSliceToString(a.Keys[keyOffset : ]) - keyOffset += (len(key) + 1) - switch a.Headers[i].Vtype { - case uint8(attribute.BOOL): - attributes = append(attributes, attribute.Bool(key, a.NumericValues[numericValuesIndex] != 0)) - numericValuesIndex++ - break - case uint8(attribute.INT64): - attributes = append(attributes, attribute.Int64(key, a.NumericValues[numericValuesIndex])) - numericValuesIndex++ - break - case uint8(attribute.FLOAT64): - attributes = append(attributes, attribute.Float64(key, math.Float64frombits(uint64(a.NumericValues[numericValuesIndex])))) - numericValuesIndex++ - break - case uint8(attribute.STRING): - strVal := unix.ByteSliceToString(a.StrValues[strValuesOffset : ]) - attributes = append(attributes, attribute.String(key, strVal)) - strValuesOffset += (len(strVal) + 1) - break - // TODO: handle slices - default: - break - } - } - return attributes -} - - -// Close stops the Probe. -func (h *Probe) Close() { - h.logger.Info("closing go.opentelemetry.io/otel/sdk/trace probe") - if h.eventsReader != nil { - h.eventsReader.Close() - } - - for _, r := range h.uprobes { - r.Close() - } - - for _, r := range h.returnProbs { - r.Close() - } - - if h.bpfObjects != nil { - h.bpfObjects.Close() - } -} diff --git a/internal/pkg/instrumentation/manager.go b/internal/pkg/instrumentation/manager.go index 298eb7cef..9f6f1e523 100644 --- a/internal/pkg/instrumentation/manager.go +++ b/internal/pkg/instrumentation/manager.go @@ -24,11 +24,11 @@ import ( dbSql "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/database/sql" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin" + otelAPITrace "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/google.golang.org/grpc" grpcServer "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server" httpClient "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/net/http/client" httpServer "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/net/http/server" - otelSdkTrace "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs" "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" "go.opentelemetry.io/auto/internal/pkg/opentelemetry" @@ -45,10 +45,11 @@ type Manager struct { done chan bool incomingEvents chan *probe.Event otelController *opentelemetry.Controller + withOtelAPI bool } // NewManager returns a new [Manager]. -func NewManager(logger logr.Logger, otelController *opentelemetry.Controller) (*Manager, error) { +func NewManager(logger logr.Logger, otelController *opentelemetry.Controller, withOtelAPI bool) (*Manager, error) { logger = logger.WithName("Manager") m := &Manager{ logger: logger, @@ -56,6 +57,7 @@ func NewManager(logger logr.Logger, otelController *opentelemetry.Controller) (* done: make(chan bool, 1), incomingEvents: make(chan *probe.Event), otelController: otelController, + withOtelAPI: withOtelAPI, } err := m.registerProbes() @@ -210,7 +212,10 @@ func (m *Manager) registerProbes() error { httpClient.New(m.logger), gin.New(m.logger), dbSql.New(m.logger), - otelSdkTrace.New(m.logger), + } + + if m.withOtelAPI { + insts = append(insts, otelAPITrace.New(m.logger)) } for _, i := range insts { diff --git a/internal/tools/inspect/cmd/offsetgen/main.go b/internal/tools/inspect/cmd/offsetgen/main.go index d2095270c..b8e259f19 100644 --- a/internal/tools/inspect/cmd/offsetgen/main.go +++ b/internal/tools/inspect/cmd/offsetgen/main.go @@ -80,11 +80,6 @@ func manifests() ([]inspect.Manifest, error) { return nil, fmt.Errorf("failed to get \"golang.org/x/net\" versions: %w", err) } - otelSdkTraceVers, err := PkgVersions("go.opentelemetry.io/otel/sdk") - if err != nil { - return nil, fmt.Errorf("failed to get \"go.opentelemetry.io/otel/sdk\" versions: %w", err) - } - ren := func(src string) inspect.Renderer { return inspect.NewRenderer(logger, src, inspect.DefaultFS) } @@ -141,16 +136,6 @@ func manifests() ([]inspect.Manifest, error) { structfield.NewID("golang.org/x/net", "golang.org/x/net/http2", "FrameHeader", "StreamID"), }, }, - { - Application: inspect.Application{ - Renderer: ren("templates/go.opentelemetry.io/otel/sdk/*.tmpl"), - Versions: otelSdkTraceVers, - }, - StructFields: []structfield.ID{ - structfield.NewID("go.opentelemetry.io/otel/sdk", "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "name"), - structfield.NewID("go.opentelemetry.io/otel/sdk", "go.opentelemetry.io/otel/sdk/trace", "recordingSpan", "attributes"), - }, - }, }, nil } diff --git a/internal/tools/inspect/render.go b/internal/tools/inspect/render.go index e44add78d..74c5bbe97 100644 --- a/internal/tools/inspect/render.go +++ b/internal/tools/inspect/render.go @@ -29,7 +29,6 @@ import ( //go:embed templates/google.golang.org/grpc/*.tmpl //go:embed templates/net/http/*.tmpl //go:embed templates/runtime/*.tmpl -//go:embed templates/go.opentelemetry.io/otel/sdk/*.tmpl var DefaultFS embed.FS // Renderer renders templates from an fs.FS. diff --git a/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl b/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl deleted file mode 100644 index 31a40a784..000000000 --- a/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/go.mod.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -module otelteacecapp - -go 1.12 - -require go.opentelemetry.io/otel/sdk {{ .Version }} diff --git a/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl b/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl deleted file mode 100644 index cfca96930..000000000 --- a/internal/tools/inspect/templates/go.opentelemetry.io/otel/sdk/main.go.tmpl +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "context" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/sdk/trace" -) - -func main() { - tracerProvider := trace.NewTracerProvider(trace.WithResource(nil)) - otel.SetTracerProvider(tracerProvider) - tracer := otel.Tracer("dummy") - _, span := tracer.Start(context.Background(), "dummy") - defer span.End() -} \ No newline at end of file From 6302d75196a146959c2138921e0f24d1a7fa8b96 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 22 Nov 2023 09:37:06 +0200 Subject: [PATCH 09/33] revert changes to verifier log collection settings --- internal/pkg/instrumentation/utils/ebpf.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/instrumentation/utils/ebpf.go b/internal/pkg/instrumentation/utils/ebpf.go index 9a6af667e..3cad8d98d 100644 --- a/internal/pkg/instrumentation/utils/ebpf.go +++ b/internal/pkg/instrumentation/utils/ebpf.go @@ -33,8 +33,8 @@ func LoadEBPFObjects(spec *ebpf.CollectionSpec, to interface{}, opts *ebpf.Colle // Getting full verifier log is expensive, so we only do it if the user explicitly asks for it. showVerifierLogs := shouldShowVerifierLogs() if showVerifierLogs { - opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 10000 - opts.Programs.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelStats + opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 100 + opts.Programs.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelBranch | ebpf.LogLevelStats } err := spec.LoadAndAssign(to, opts) From 78987249cf2a10630eaf06800b379621d2ccd278 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 22 Nov 2023 15:58:18 +0200 Subject: [PATCH 10/33] Add tests for otel API instrumentation --- .github/dependabot.yml | 9 ++ .github/workflows/e2e/k8s/sample-job.yml | 2 + .github/workflows/kind.yml | 2 +- Makefile | 5 +- examples/go.mod | 2 +- examples/rolldice/main.go | 9 +- .../otel/global/probe_test.go | 119 ++++++++++++------ internal/test/e2e/otelglobal/Dockerfile | 4 + internal/test/e2e/otelglobal/main.go | 55 ++++++++ internal/test/e2e/otelglobal/traces.json | 107 ++++++++++++++++ internal/test/e2e/otelglobal/verify.bats | 65 ++++++++++ 11 files changed, 329 insertions(+), 50 deletions(-) create mode 100644 internal/test/e2e/otelglobal/Dockerfile create mode 100644 internal/test/e2e/otelglobal/main.go create mode 100644 internal/test/e2e/otelglobal/traces.json create mode 100644 internal/test/e2e/otelglobal/verify.bats diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c4d7b3906..f43dbda3d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -73,6 +73,15 @@ updates: schedule: interval: weekly day: sunday + - package-ecosystem: docker + directory: /internal/test/e2e/otelglobal + labels: + - dependencies + - docker + - Skip Changelog + schedule: + interval: weekly + day: sunday - package-ecosystem: gomod directory: / labels: diff --git a/.github/workflows/e2e/k8s/sample-job.yml b/.github/workflows/e2e/k8s/sample-job.yml index 62f24e63c..b458d22aa 100644 --- a/.github/workflows/e2e/k8s/sample-job.yml +++ b/.github/workflows/e2e/k8s/sample-job.yml @@ -37,6 +37,8 @@ spec: value: "tracecontext,baggage" - name: OTEL_GO_AUTO_INCLUDE_DB_STATEMENT value: "true" + - name: OTEL_GO_AUTO_MANUAL_INTEGRATION + value: "true" resources: {} securityContext: runAsUser: 0 diff --git a/.github/workflows/kind.yml b/.github/workflows/kind.yml index 0d3efa2dc..8c9a8da09 100644 --- a/.github/workflows/kind.yml +++ b/.github/workflows/kind.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: k8s-version: ["v1.26.0"] - library: ["nethttp", "gin", "databasesql", "grpc"] + library: ["nethttp", "gin", "databasesql", "grpc", "otelglobal"] runs-on: ubuntu-latest steps: - name: Checkout Repo diff --git a/Makefile b/Makefile index bd166f9b1..51da72727 100644 --- a/Makefile +++ b/Makefile @@ -142,11 +142,12 @@ license-header-check: exit 1; \ fi -.PHONY: fixture-nethttp fixture-gin fixture-databasesql +.PHONY: fixture-nethttp fixture-gin fixture-databasesql fixture-otelglobal fixture-nethttp: fixtures/nethttp fixture-gin: fixtures/gin fixture-databasesql: fixtures/databasesql fixture-grpc: fixtures/grpc +fixture-otelglobal: fixtures/otelglobal fixtures/%: LIBRARY=$* fixtures/%: $(MAKE) docker-build @@ -157,7 +158,7 @@ fixtures/%: if [ ! -d "opentelemetry-helm-charts" ]; then \ git clone https://github.com/open-telemetry/opentelemetry-helm-charts.git; \ fi - helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/opentelemetry-collector + helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/charts/opentelemetry-collector kubectl wait --for=condition=Ready --timeout=60s pod/test-opentelemetry-collector-0 kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml kubectl wait --for=condition=Complete --timeout=60s job/sample-job diff --git a/examples/go.mod b/examples/go.mod index 31e8375b3..76a0440c4 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -5,7 +5,6 @@ go 1.20 require ( github.com/mattn/go-sqlite3 v1.14.18 go.opentelemetry.io/otel v1.19.0 - go.opentelemetry.io/otel/trace v1.19.0 go.uber.org/zap v1.26.0 ) @@ -13,5 +12,6 @@ require ( github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect go.uber.org/multierr v1.10.0 // indirect ) diff --git a/examples/rolldice/main.go b/examples/rolldice/main.go index 2e8089a6d..c757be140 100644 --- a/examples/rolldice/main.go +++ b/examples/rolldice/main.go @@ -26,7 +26,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" ) // Server is Http server that exposes multiple endpoints. @@ -34,11 +33,7 @@ type Server struct { rand *rand.Rand } -var ( - tracer = otel.Tracer("rolldice") -) - - +var tracer = otel.Tracer("rolldice") // NewServer creates a server struct after initialing rand. func NewServer() *Server { rd := rand.New(rand.NewSource(time.Now().Unix())) @@ -55,7 +50,7 @@ func (s *Server) innerFunction(ctx context.Context) { } func (s *Server) rolldice(w http.ResponseWriter, r *http.Request) { - ctx, span := tracer.Start(r.Context(), "roll", trace.WithAttributes()) + ctx, span := tracer.Start(r.Context(), "roll") defer span.End() n := s.rand.Intn(6) + 1 diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go index ef9b2e86c..4d47451f5 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go @@ -15,50 +15,91 @@ package global import ( + "math" "testing" - // "time". - // "github.com/go-logr/logr/testr" - // "github.com/stretchr/testify/assert". - // "go.opentelemetry.io/otel/attribute" - // semconv "go.opentelemetry.io/otel/semconv/v1.21.0" - // "go.opentelemetry.io/otel/trace". - // "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" - // "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe". + "time" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" ) func TestProbeConvertEvent(t *testing.T) { - // start := time.Now() - // end := start.Add(1 * time.Second) + start := time.Now() + end := start.Add(1 * time.Second) + + traceID := trace.TraceID{1} + spanID := trace.SpanID{1} - // traceID := trace.TraceID{1} - // spanID := trace.SpanID{1} + got := convertEvent(&event{ + BaseSpanProperties: context.BaseSpanProperties{ + StartTime: uint64(start.UnixNano()), + EndTime: uint64(end.UnixNano()), + SpanContext: context.EBPFSpanContext{TraceID: traceID, SpanID: spanID}, + }, + // span name: "Foo" + SpanName: [64]byte{0x46, 0x6f, 0x6f}, - // i := New(testr.New(t)) - // got := i.convertEvent(&Event{ - // BaseSpanProperties: context.BaseSpanProperties{ - // StartTime: uint64(start.UnixNano()), - // EndTime: uint64(end.UnixNano()), - // SpanContext: context.EBPFSpanContext{TraceID: traceID, SpanID: spanID}, - // }, - // // "SELECT * FROM foo" - // Query: [256]byte{0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x20, 0x2a, 0x20, 0x46, 0x52, 0x4f, 0x4d, 0x20, 0x66, 0x6f, 0x6f}, - // }) + Attributes: attributesBuffer{ + Headers: [128]attributeHeader{ + { + ValLength: 0, + Vtype: uint8(attribute.BOOL), + Reserved: 0, + }, + { + ValLength: 0, + Vtype: uint8(attribute.STRING), + Reserved: 0, + }, + { + ValLength: 0, + Vtype: uint8(attribute.FLOAT64), + Reserved: 0, + }, + { + ValLength: 0, + Vtype: uint8(attribute.INT64), + Reserved: 0, + }, + { + ValLength: 0, + Vtype: uint8(attribute.STRING), + Reserved: 0, + }, + }, + // Keys are strings separated by null bytes: "bool_key\0string_key1\0float_key\0int_key\0string_key2\0" + Keys: [256]byte{0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x31, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x00, 0x69, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x32, 0x00}, + // Numberc values of: true, 3.14(pi), 42 + NumericValues: [32]int64{1, int64(math.Float64bits(math.Pi)), 42}, + // String values of: "string value 1", "string value 2", saved as null terminated strings + StrValues: [1024]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x31, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x32, 0x00}, + }, + }) - // sc := trace.NewSpanContext(trace.SpanContextConfig{ - // TraceID: traceID, - // SpanID: spanID, - // TraceFlags: trace.FlagsSampled, - // }) - // want := &probe.Event{ - // Library: instrumentedPkg, - // Name: "DB", - // Kind: trace.SpanKindClient, - // StartTime: int64(start.UnixNano()), - // EndTime: int64(end.UnixNano()), - // SpanContext: &sc, - // Attributes: []attribute.KeyValue{ - // semconv.DBStatementKey.String("SELECT * FROM foo"), - // }, - // } - // assert.Equal(t, want, got) + sc := trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceFlags: trace.FlagsSampled, + }) + want := &probe.Event{ + Package: pkg, + Name: "Foo", + Kind: trace.SpanKindClient, + StartTime: int64(start.UnixNano()), + EndTime: int64(end.UnixNano()), + SpanContext: &sc, + Attributes: []attribute.KeyValue{ + attribute.Bool("bool_key", true), + attribute.String("string_key1", "string value 1"), + attribute.Float64("float_key", math.Pi), + attribute.Int64("int_key", 42), + attribute.String("string_key2", "string value 2"), + }, + } + assert.Equal(t, want, got) } diff --git a/internal/test/e2e/otelglobal/Dockerfile b/internal/test/e2e/otelglobal/Dockerfile new file mode 100644 index 000000000..0773884c4 --- /dev/null +++ b/internal/test/e2e/otelglobal/Dockerfile @@ -0,0 +1,4 @@ +FROM golang:1.21.4 +WORKDIR /sample-app +COPY . . +RUN go mod init go.opentelemetry.io/auto/internal/test/e2e/otelglobal && go mod tidy && go build -o main diff --git a/internal/test/e2e/otelglobal/main.go b/internal/test/e2e/otelglobal/main.go new file mode 100644 index 000000000..b2670cb2c --- /dev/null +++ b/internal/test/e2e/otelglobal/main.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// +// 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 language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" +) + +var tracer = otel.Tracer("trace-example") + +func innerFunction(ctx context.Context) { + _, span := tracer.Start(ctx, "child") + defer span.End() + + span.SetAttributes(attribute.String("inner.key", "inner.value")) +} + +func createMainSpan(ctx context.Context) { + ctx, span := tracer.Start(ctx, "parent") + defer span.End() + intAttr := attribute.Int("int_key", 42) + + innerFunction(ctx) + + strAttr := attribute.String("string_key", "forty-two") + boolAttr := attribute.Bool("bool_key", true) + floatAttr := attribute.Float64("float_key", 42.3) + span.SetAttributes(intAttr, strAttr, boolAttr, floatAttr) +} + +func main() { + // give time for auto-instrumentation to start up + time.Sleep(5 * time.Second) + + createMainSpan(context.Background()) + + // give time for auto-instrumentation to report signal + time.Sleep(5 * time.Second) +} diff --git a/internal/test/e2e/otelglobal/traces.json b/internal/test/e2e/otelglobal/traces.json new file mode 100644 index 000000000..a01885fa2 --- /dev/null +++ b/internal/test/e2e/otelglobal/traces.json @@ -0,0 +1,107 @@ +{ + "resourceSpans": [ + { + "resource": { + "attributes": [ + { + "key": "process.runtime.description", + "value": { + "stringValue": "go version 1.21.4 linux/arm64" + } + }, + { + "key": "process.runtime.name", + "value": { + "stringValue": "go" + } + }, + { + "key": "process.runtime.version", + "value": { + "stringValue": "1.21.4" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "sample-app" + } + }, + { + "key": "telemetry.auto.version", + "value": { + "stringValue": "v0.8.0-alpha" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "go" + } + } + ] + }, + "schemaUrl": "https://opentelemetry.io/schemas/1.21.0", + "scopeSpans": [ + { + "scope": { + "name": "go.opentelemetry.io/auto/go.opentelemetry.io/otel/internal/global", + "version": "v0.8.0-alpha" + }, + "spans": [ + { + "attributes": [ + { + "key": "inner.key", + "value": { + "stringValue": "inner.value" + } + } + ], + "kind": 3, + "name": "child", + "parentSpanId": "xxxxx", + "spanId": "xxxxx", + "status": {}, + "traceId": "xxxxx" + }, + { + "attributes": [ + { + "key": "int_key", + "value": { + "intValue": "42" + } + }, + { + "key": "string_key", + "value": { + "stringValue": "forty-two" + } + }, + { + "key": "bool_key", + "value": { + "boolValue": true + } + }, + { + "key": "float_key", + "value": { + "doubleValue": 42.3 + } + } + ], + "kind": 3, + "name": "parent", + "parentSpanId": "", + "spanId": "xxxxx", + "status": {}, + "traceId": "xxxxx" + } + ] + } + ] + } + ] +} diff --git a/internal/test/e2e/otelglobal/verify.bats b/internal/test/e2e/otelglobal/verify.bats new file mode 100644 index 000000000..382b22f2e --- /dev/null +++ b/internal/test/e2e/otelglobal/verify.bats @@ -0,0 +1,65 @@ +#!/usr/bin/env bats + +load ../../test_helpers/utilities + +SCOPE="go.opentelemetry.io/auto/go.opentelemetry.io/otel/internal/global" + +@test "go-auto :: includes service.name in resource attributes" { + result=$(resource_attributes_received | jq "select(.key == \"service.name\").value.stringValue") + assert_equal "$result" '"sample-app"' +} + +@test "server :: valid int attribute" { + result=$(span_attributes_for ${SCOPE} | jq "select(.key == \"int_key\").value.intValue") + assert_equal "$result" '"42"' +} + +@test "server :: valid string attribute" { + result=$(span_attributes_for ${SCOPE} | jq "select(.key == \"string_key\").value.stringValue") + assert_equal "$result" '"forty-two"' +} + +@test "server :: valid string attribute in child span" { + result=$(span_attributes_for ${SCOPE} | jq "select(.key == \"inner.key\").value.stringValue") + assert_equal "$result" '"inner.value"' +} + +@test "server :: valid bool attribute" { + result=$(span_attributes_for ${SCOPE} | jq "select(.key == \"bool_key\").value.boolValue") + assert_equal "$result" 'true' +} + +@test "server :: valid float attribute" { + result=$(span_attributes_for ${SCOPE} | jq "select(.key == \"float_key\").value.doubleValue") + assert_equal "$result" '42.3' +} + +@test "server :: trace ID present and valid in child span" { + trace_id=$(spans_from_scope_named ${SCOPE} | jq "select(.name == \"child\")" | jq ".traceId") + assert_regex "$trace_id" ${MATCH_A_TRACE_ID} +} + +@test "server :: trace ID present and valid in parent span" { + trace_id=$(spans_from_scope_named ${SCOPE} | jq "select(.name == \"parent\")" | jq ".traceId") + assert_regex "$trace_id" ${MATCH_A_TRACE_ID} +} + +@test "server :: span ID present and valid in child span" { + trace_id=$(spans_from_scope_named ${SCOPE} | jq "select(.name == \"child\")" | jq ".spanId") + assert_regex "$trace_id" ${MATCH_A_SPAN_ID} +} + +@test "server :: span ID present and valid in parent span" { + trace_id=$(spans_from_scope_named ${SCOPE} | jq "select(.name == \"parent\")" | jq ".spanId") + assert_regex "$trace_id" ${MATCH_A_SPAN_ID} +} + +@test "server :: parent span ID present and valid in child span" { + parent_span_id=$(spans_from_scope_named ${SCOPE} | jq "select(.name == \"child\")" | jq ".parentSpanId") + assert_regex "$parent_span_id" ${MATCH_A_SPAN_ID} +} + +@test "server :: expected (redacted) trace output" { + redact_json + assert_equal "$(git --no-pager diff ${BATS_TEST_DIRNAME}/traces.json)" "" +} From 53a0daaa5fe8d779bcc34aa159aa4f470ffbaa10 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 22 Nov 2023 16:33:39 +0200 Subject: [PATCH 11/33] update changelog and lint --- CHANGELOG.md | 4 ++++ examples/rolldice/main.go | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54cf81abc..5d96b0f96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http ## [Unreleased] +### Added + +- Manual instrumentation support - Phase 1 ([#523]https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/523) + ### Changed - The instrumentation scope name for the `database/sql` instrumentation is now `go.opentelemtry.io/auto/database/sql`. (#507) diff --git a/examples/rolldice/main.go b/examples/rolldice/main.go index c757be140..bd6866940 100644 --- a/examples/rolldice/main.go +++ b/examples/rolldice/main.go @@ -34,6 +34,7 @@ type Server struct { } var tracer = otel.Tracer("rolldice") + // NewServer creates a server struct after initialing rand. func NewServer() *Server { rd := rand.New(rand.NewSource(time.Now().Unix())) From 6d1b33dbc018bf20f2fa9ca6c6a30c0df9800d45 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 22 Nov 2023 17:26:17 +0200 Subject: [PATCH 12/33] Check kernel version --- .github/workflows/kind.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/kind.yml b/.github/workflows/kind.yml index 8c9a8da09..032e31981 100644 --- a/.github/workflows/kind.yml +++ b/.github/workflows/kind.yml @@ -36,6 +36,8 @@ jobs: uses: azure/setup-helm@v3.5 with: version: v3.9.0 + - name: Kernel version + run: uname -r - name: Create kind cluster uses: helm/kind-action@v1.8.0 with: From 6d8cdfd2cf1bdfa9f3265e19260dd2f29ba24d24 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 24 Nov 2023 13:55:21 +0200 Subject: [PATCH 13/33] Change format of attributes in eBPF to make the verification easier --- internal/include/go_types.h | 12 +- internal/include/otel_types.h | 150 +++++++++--------- .../github.com/gin-gonic/gin/bpf/probe.bpf.c | 4 +- .../otel/global/bpf_bpfel_arm64.go | 9 +- .../otel/global/bpf_bpfel_x86.go | 9 +- .../go.opentelemetry.io/otel/global/probe.go | 50 +++--- .../otel/global/probe_test.go | 33 +++- .../google.golang.org/grpc/bpf/probe.bpf.c | 2 +- .../grpc/server/bpf/probe.bpf.c | 2 +- .../bpf/net/http/client/bpf/probe.bpf.c | 4 +- .../bpf/net/http/server/bpf/probe.bpf.c | 4 +- 11 files changed, 140 insertions(+), 139 deletions(-) diff --git a/internal/include/go_types.h b/internal/include/go_types.h index 7b4b869a0..56f5377d5 100644 --- a/internal/include/go_types.h +++ b/internal/include/go_types.h @@ -177,15 +177,15 @@ static __always_inline void append_item_to_slice(struct go_slice *slice, void *n } } -static __always_inline s64 get_go_string_from_user_ptr(void *user_str_ptr, char *dst, s32 max_len) +static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char *dst, s32 max_len) { if (max_len < 1) { - return -1; + return false; } if (user_str_ptr == NULL) { - return -1; + return false; } struct go_string user_str = {0}; @@ -193,16 +193,16 @@ static __always_inline s64 get_go_string_from_user_ptr(void *user_str_ptr, char success = bpf_probe_read(&user_str, sizeof(struct go_string), user_str_ptr); if (success != 0 || user_str.len < 1) { - return -1; + return false; } u32 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 -1; + return false; } - return size_to_read; + return true; } #endif \ No newline at end of file diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index f53661e20..c75c75df9 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -43,109 +43,103 @@ typedef struct go_otel_key_value { go_otel_attr_value_t value; } go_otel_key_value_t; -/* The following structs are the C-formated structs to be used by the eBPF code */ -#define OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH (256) -#define OTEL_ATTRIBUTE_NUMERIC_VALUES_BUFFER_SIZE (32) -#define OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE (1024) -#define OTEL_ATTRUBUTE_MAX_COUNT OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH / 2 +#define OTEL_ATTRIBUTE_KEY_MAX_LEN (32) +#define OTEL_ATTRIBUTE_VALUE_MAX_LEN (128) +#define OTEL_ATTRUBUTE_MAX_COUNT (16) -typedef struct otel_attr_header { +typedef struct otel_attirbute { u16 val_length; u8 vtype; u8 reserved; -} otel_attr_header_t; + char key[OTEL_ATTRIBUTE_KEY_MAX_LEN]; + char value[OTEL_ATTRIBUTE_VALUE_MAX_LEN]; +} otel_attirbute_t; typedef struct otel_attributes { - otel_attr_header_t headers[OTEL_ATTRUBUTE_MAX_COUNT]; - char keys[OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH]; - s64 numeric_values[OTEL_ATTRIBUTE_NUMERIC_VALUES_BUFFER_SIZE]; - char str_values[OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE]; -} otel_attributes_t; + otel_attirbute_t attrs[OTEL_ATTRUBUTE_MAX_COUNT]; + u8 valid_attrs; +}__attribute__((packed)) otel_attributes_t; +static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_value_t *go_attr_value) +{ + switch (go_attr_value->vtype) + { + case BOOL: + case INT64: + case FLOAT64: + bpf_probe_read(&attr->value, sizeof(s64), &go_attr_value->numeric); + return true; + case STRING: + if (go_attr_value->string.len <= 0){ + return false; + } + if (go_attr_value->string.len >= OTEL_ATTRIBUTE_VALUE_MAX_LEN) { + // String value is too large + return false; + } + if (!get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN)) { + return false; + } + return true; + // TODO: handle slices + case BOOLSLICE: + case INT64SLICE: + case FLOAT64SLICE: + case STRINGSLICE: + case INVALID: + default: + return false; + } +} -static __always_inline long convert_go_otel_attributes(void *attrs_buf, s64 slice_len, otel_attributes_t *enc_attrs) +static __always_inline void convert_go_otel_attributes(void *attrs_buf, s64 slice_len, otel_attributes_t *enc_attrs) { if (attrs_buf == NULL || enc_attrs == NULL){ - return -1; + return; } - go_otel_key_value_t *go_attr = (go_otel_key_value_t*)attrs_buf; + if (slice_len < 1) { + return; + } - u16 keys_off = 0, str_values_off = 0, numeric_index = 0; - s64 key_len = 0; + s64 num_attrs = slice_len < OTEL_ATTRUBUTE_MAX_COUNT ? slice_len : OTEL_ATTRUBUTE_MAX_COUNT; + go_otel_key_value_t *go_attr = (go_otel_key_value_t*)attrs_buf; go_otel_attr_value_t go_attr_value = {0}; struct go_string go_str = {0}; - s64 bytes_copied = 0; - for (u32 i = 0; - i < OTEL_ATTRUBUTE_MAX_COUNT && - i < slice_len && - keys_off < OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1 && - str_values_off < OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - 1 && - numeric_index < OTEL_ATTRIBUTE_NUMERIC_VALUES_BUFFER_SIZE; - i++) - { + u8 valid_attrs = 0; + + for (u32 go_attr_index = 0; go_attr_index < num_attrs; go_attr_index++) { __builtin_memset(&go_attr_value, 0, sizeof(go_otel_attr_value_t)); - bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr->value); + // Read the value struct + bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr[go_attr_index].value); + if (go_attr_value.vtype == INVALID) { - break; + continue; } + // Read the key string - bpf_probe_read(&go_str, sizeof(struct go_string), &go_attr->key); + bpf_probe_read(&go_str, sizeof(struct go_string), &go_attr[go_attr_index].key); if (go_str.len <= 0){ - break; + continue; } - if (go_str.len >= OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - keys_off - 1) { - // No room left in keys buffer - break; + if (go_str.len >= OTEL_ATTRIBUTE_KEY_MAX_LEN) { + // key string is too large + continue; } - keys_off &= (OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH - 1); - key_len = get_go_string_from_user_ptr(&go_str, (char*)&enc_attrs->keys[keys_off], OTEL_ATTRIBUTE_KEYS_BUFFER_MAX_LENGTH); - if (key_len < 0){ - break; + + if (!get_go_string_from_user_ptr(&go_str, enc_attrs->attrs[valid_attrs].key, OTEL_ATTRIBUTE_KEY_MAX_LEN)) { + continue; } - keys_off += key_len; - // Keep the null terminator between keys - keys_off++; - - enc_attrs->headers[i].vtype = go_attr_value.vtype; - switch (go_attr_value.vtype) - { - case BOOL: - case INT64: - case FLOAT64: - bpf_probe_read(&enc_attrs->numeric_values[numeric_index], sizeof(s64), &go_attr_value.numeric); - numeric_index++; - break; - case STRING: - go_str = go_attr_value.string; - if (go_str.len <= 0){ - return -1; - } - if (go_str.len >= OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - str_values_off - 1) { - // No room left in string values buffer - return -1; - } - str_values_off &= (OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE - 1); - bytes_copied = get_go_string_from_user_ptr(&go_str, &enc_attrs->str_values[str_values_off], OTEL_ATTRIBUTE_STRING_VALUES_BUFFER_SIZE); - if (bytes_copied < 0){ - return -1; - } - str_values_off += bytes_copied; - // Keep the null terminator between string values - str_values_off++; - break; - // TODO: handle slices - case BOOLSLICE: - case INT64SLICE: - case FLOAT64SLICE: - case STRINGSLICE: - case INVALID: - default: - break;; + + if (!set_attr_value(&enc_attrs->attrs[valid_attrs], &go_attr_value)) { + continue; } - go_attr++; + + enc_attrs->attrs[valid_attrs].vtype = go_attr_value.vtype; + valid_attrs++; } - return 0; + + enc_attrs->valid_attrs = valid_attrs; } #endif \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c index cda472e51..8108c2490 100644 --- a/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin/bpf/probe.bpf.c @@ -59,7 +59,7 @@ int uprobe_GinEngine_ServeHTTP(struct pt_regs *ctx) { void *req_ctx_ptr = get_Go_context(ctx, 4, ctx_ptr_pos, false); // Get method from request - if (get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), httpReq.method, sizeof(httpReq.method)) < 0) { + if (!get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), httpReq.method, sizeof(httpReq.method))) { bpf_printk("failed to get method from request"); return 0; } @@ -67,7 +67,7 @@ int uprobe_GinEngine_ServeHTTP(struct pt_regs *ctx) { // get path from Request.URL void *url_ptr = 0; bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr + url_ptr_pos)); - if (get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), httpReq.path, sizeof(httpReq.path)) < 0) { + if (!get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), httpReq.path, sizeof(httpReq.path))) { bpf_printk("failed to get path from Request.URL"); return 0; } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go index ddf4ee71a..4e44153b9 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go @@ -19,15 +19,16 @@ type bpfOtelSpanT struct { Psc bpfSpanContext SpanName bpfSpanNameT Attributes struct { - Headers [128]struct { + Attrs [16]struct { ValLength uint16 Vtype uint8 Reserved uint8 + Key [32]int8 + Value [128]int8 } - Keys [256]int8 - NumericValues [32]int64 - StrValues [1024]int8 + ValidAttrs uint8 } + _ [7]byte } type bpfSliceArrayBuff struct{ Buff [1024]uint8 } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go index b3e810502..e3dacd47c 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go @@ -19,15 +19,16 @@ type bpfOtelSpanT struct { Psc bpfSpanContext SpanName bpfSpanNameT Attributes struct { - Headers [128]struct { + Attrs [16]struct { ValLength uint16 Vtype uint8 Reserved uint8 + Key [32]int8 + Value [128]int8 } - Keys [256]int8 - NumericValues [32]int64 - StrValues [1024]int8 + ValidAttrs uint8 } + _ [7]byte } type bpfSliceArrayBuff struct{ Buff [1024]uint8 } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go index 0cfde854d..2adf0e84d 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go @@ -15,6 +15,7 @@ package global import ( + "encoding/binary" "math" "os" @@ -39,8 +40,6 @@ const ( name = "go.opentelemetry.io/otel/internal/global" // pkg is the package being instrumented. pkg = "go.opentelemetry.io/otel/internal/global" - // maxAttributes is the maximum number of attributes that can be added to a span. - maxAttributes = 128 ) // New returns a new [probe.Probe]. @@ -132,17 +131,17 @@ func uprobeSpanEnd(name string, exec *link.Executable, target *process.TargetDet return links, nil } -type attributeHeader struct { +type attributeKeyVal struct { ValLength uint16 Vtype uint8 Reserved uint8 + Key [32]byte + Value [128]byte } type attributesBuffer struct { - Headers [128]attributeHeader - Keys [256]byte - NumericValues [32]int64 - StrValues [1024]byte + AttrsKv [16]attributeKeyVal + ValidAttrs uint8 } // event represents a manual span created by the user. @@ -180,40 +179,27 @@ func convertEvent(e *event) *probe.Event { Kind: trace.SpanKindClient, StartTime: int64(e.StartTime), EndTime: int64(e.EndTime), - Attributes: convertAttributes(&e.Attributes), + Attributes: convertAttributes(e.Attributes), SpanContext: &sc, ParentSpanContext: pscPtr, } } -func convertAttributes(a *attributesBuffer) []attribute.KeyValue { - var attributes []attribute.KeyValue - var keyOffset int - var numericValuesIndex int - var strValuesOffset int - for i := 0; i < maxAttributes; i++ { - if a.Headers[i].Vtype == uint8(attribute.INVALID) { - break - } - key := unix.ByteSliceToString(a.Keys[keyOffset:]) - keyOffset += (len(key) + 1) - switch a.Headers[i].Vtype { +func convertAttributes(ab attributesBuffer) []attribute.KeyValue { + var res []attribute.KeyValue + for i := 0; i < int(ab.ValidAttrs); i++ { + akv := ab.AttrsKv[i] + key := unix.ByteSliceToString(akv.Key[:]) + switch akv.Vtype { case uint8(attribute.BOOL): - attributes = append(attributes, attribute.Bool(key, a.NumericValues[numericValuesIndex] != 0)) - numericValuesIndex++ + res = append(res, attribute.Bool(key, akv.Value[0] != 0)) case uint8(attribute.INT64): - attributes = append(attributes, attribute.Int64(key, a.NumericValues[numericValuesIndex])) - numericValuesIndex++ + res = append(res, attribute.Int64(key, int64(binary.LittleEndian.Uint64(akv.Value[:8])))) case uint8(attribute.FLOAT64): - attributes = append(attributes, attribute.Float64(key, math.Float64frombits(uint64(a.NumericValues[numericValuesIndex])))) - numericValuesIndex++ + res = append(res, attribute.Float64(key, math.Float64frombits(binary.LittleEndian.Uint64(akv.Value[:8])))) case uint8(attribute.STRING): - strVal := unix.ByteSliceToString(a.StrValues[strValuesOffset:]) - attributes = append(attributes, attribute.String(key, strVal)) - strValuesOffset += (len(strVal) + 1) - // TODO: handle slices - default: + res = append(res, attribute.String(key, unix.ByteSliceToString(akv.Value[:]))) } } - return attributes + return res } diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go index 4d47451f5..db8e63bda 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go @@ -15,6 +15,7 @@ package global import ( + "encoding/binary" "math" "testing" "time" @@ -35,6 +36,9 @@ func TestProbeConvertEvent(t *testing.T) { traceID := trace.TraceID{1} spanID := trace.SpanID{1} + var floatBuf [128]byte + binary.LittleEndian.PutUint64(floatBuf[:], math.Float64bits(math.Pi)) + got := convertEvent(&event{ BaseSpanProperties: context.BaseSpanProperties{ StartTime: uint64(start.UnixNano()), @@ -45,39 +49,54 @@ func TestProbeConvertEvent(t *testing.T) { SpanName: [64]byte{0x46, 0x6f, 0x6f}, Attributes: attributesBuffer{ - Headers: [128]attributeHeader{ + AttrsKv: [16]attributeKeyVal{ { ValLength: 0, Vtype: uint8(attribute.BOOL), Reserved: 0, + // "bool_key" + Key: [32]byte{0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x6b, 0x65, 0x79}, + // true + Value: [128]byte{0x01}, }, { ValLength: 0, Vtype: uint8(attribute.STRING), Reserved: 0, + // "string_key1" + Key: [32]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x31}, + // "string value 1" + Value: [128]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x31}, }, { ValLength: 0, Vtype: uint8(attribute.FLOAT64), Reserved: 0, + // "float_key" + Key: [32]byte{0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x6b, 0x65, 0x79}, + // math.Pi + Value: floatBuf, }, { ValLength: 0, Vtype: uint8(attribute.INT64), Reserved: 0, + // "int_key" + Key: [32]byte{0x69, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79}, + // 42 + Value: [128]byte{42}, }, { ValLength: 0, Vtype: uint8(attribute.STRING), Reserved: 0, + // "string_key2" + Key: [32]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x32}, + // "string value 2" + Value: [128]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x32}, }, }, - // Keys are strings separated by null bytes: "bool_key\0string_key1\0float_key\0int_key\0string_key2\0" - Keys: [256]byte{0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x31, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x00, 0x69, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x32, 0x00}, - // Numberc values of: true, 3.14(pi), 42 - NumericValues: [32]int64{1, int64(math.Float64bits(math.Pi)), 42}, - // String values of: "string value 1", "string value 2", saved as null terminated strings - StrValues: [1024]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x31, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x32, 0x00}, + ValidAttrs: 5, }, }) diff --git a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c index 3575c55b7..6f8c3a7cd 100644 --- a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/bpf/probe.bpf.c @@ -100,7 +100,7 @@ int uprobe_ClientConn_Invoke(struct pt_regs *ctx) // Read ClientConn.Target void *clientconn_ptr = get_argument(ctx, clientconn_pos); - if (get_go_string_from_user_ptr((void*)(clientconn_ptr + clientconn_target_ptr_pos), grpcReq.target, sizeof(grpcReq.target)) < 0) + if (!get_go_string_from_user_ptr((void*)(clientconn_ptr + clientconn_target_ptr_pos), grpcReq.target, sizeof(grpcReq.target))) { bpf_printk("target write failed, aborting ebpf probe"); return 0; diff --git a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c index 776ea3485..d07d6e437 100644 --- a/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server/bpf/probe.bpf.c @@ -108,7 +108,7 @@ int uprobe_server_handleStream(struct pt_regs *ctx) grpcReq.start_time = bpf_ktime_get_ns(); // Set attributes - if (get_go_string_from_user_ptr((void *)(stream_ptr + stream_method_ptr_pos), grpcReq.method, sizeof(grpcReq.method)) < 0) + if (!get_go_string_from_user_ptr((void *)(stream_ptr + stream_method_ptr_pos), grpcReq.method, sizeof(grpcReq.method))) { bpf_printk("method write failed, aborting ebpf probe"); return 0; diff --git a/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c index e9c665c42..e94795215 100644 --- a/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c @@ -178,7 +178,7 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) { httpReq.sc = generate_span_context(); } - if (get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq.method, sizeof(httpReq.method)) < 0) { + if (!get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq.method, sizeof(httpReq.method))) { bpf_printk("uprobe_HttpClient_Do: Failed to get method from request"); return 0; } @@ -186,7 +186,7 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) { // get path from Request.URL void *url_ptr = 0; bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr+url_ptr_pos)); - if (get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq.path, sizeof(httpReq.path)) < 0) { + if (!get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq.path, sizeof(httpReq.path))) { bpf_printk("uprobe_HttpClient_Do: Failed to get path from Request.URL"); return 0; } diff --git a/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c index e7beb7b88..d1b4307b0 100644 --- a/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/net/http/server/bpf/probe.bpf.c @@ -267,14 +267,14 @@ int uprobe_HandlerFunc_ServeHTTP_Returns(struct pt_regs *ctx) { // Collect fields from response // Get method from request - if (get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), http_server_span->method, sizeof(http_server_span->method)) < 0) { + if (!get_go_string_from_user_ptr((void *)(req_ptr + method_ptr_pos), http_server_span->method, sizeof(http_server_span->method))) { bpf_printk("failed to get method from request"); return 0; } // get path from Request.URL void *url_ptr = 0; bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr + url_ptr_pos)); - if (get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), http_server_span->path, sizeof(http_server_span->path)) < 0) { + if (!get_go_string_from_user_ptr((void *)(url_ptr + path_ptr_pos), http_server_span->path, sizeof(http_server_span->path))) { bpf_printk("failed to get path from Request.URL"); return 0; } From ac6d4aba9abdb64b34872c81c7c32ae204318839 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Fri, 24 Nov 2023 14:11:56 +0200 Subject: [PATCH 14/33] Small fix --- .github/workflows/kind.yml | 2 -- internal/include/otel_types.h | 6 +++--- internal/test/e2e/otelglobal/traces.json | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/kind.yml b/.github/workflows/kind.yml index 032e31981..8c9a8da09 100644 --- a/.github/workflows/kind.yml +++ b/.github/workflows/kind.yml @@ -36,8 +36,6 @@ jobs: uses: azure/setup-helm@v3.5 with: version: v3.9.0 - - name: Kernel version - run: uname -r - name: Create kind cluster uses: helm/kind-action@v1.8.0 with: diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index c75c75df9..33ddecbf8 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -43,9 +43,9 @@ typedef struct go_otel_key_value { go_otel_attr_value_t value; } go_otel_key_value_t; -#define OTEL_ATTRIBUTE_KEY_MAX_LEN (32) -#define OTEL_ATTRIBUTE_VALUE_MAX_LEN (128) -#define OTEL_ATTRUBUTE_MAX_COUNT (16) +#define OTEL_ATTRIBUTE_KEY_MAX_LEN (32) +#define OTEL_ATTRIBUTE_VALUE_MAX_LEN (128) +#define OTEL_ATTRUBUTE_MAX_COUNT (16) typedef struct otel_attirbute { u16 val_length; diff --git a/internal/test/e2e/otelglobal/traces.json b/internal/test/e2e/otelglobal/traces.json index a01885fa2..daa0725d2 100644 --- a/internal/test/e2e/otelglobal/traces.json +++ b/internal/test/e2e/otelglobal/traces.json @@ -6,7 +6,7 @@ { "key": "process.runtime.description", "value": { - "stringValue": "go version 1.21.4 linux/arm64" + "stringValue": "go version 1.21.4 linux/amd64" } }, { From 9ff76c1b1af6bde2a835e7253611d2e9ecf55f56 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Mon, 27 Nov 2023 14:25:53 +0200 Subject: [PATCH 15/33] Adding printk if attribute is too long for buffer --- internal/include/otel_types.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index 33ddecbf8..533bb0241 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -74,7 +74,7 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ return false; } if (go_attr_value->string.len >= OTEL_ATTRIBUTE_VALUE_MAX_LEN) { - // String value is too large + bpf_printk("Aattribute string value is too long\n"); return false; } if (!get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN)) { @@ -124,6 +124,7 @@ static __always_inline void convert_go_otel_attributes(void *attrs_buf, s64 slic } if (go_str.len >= OTEL_ATTRIBUTE_KEY_MAX_LEN) { // key string is too large + bpf_printk("Attribute key string is too long\n"); continue; } From ea1f315a2963d85e6fe94d2a6ca5719ac14e35e5 Mon Sep 17 00:00:00 2001 From: Ron Federman <73110295+RonFed@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:36:46 +0200 Subject: [PATCH 16/33] Update internal/include/otel_types.h Co-authored-by: Mike Goldsmith --- internal/include/otel_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index 533bb0241..314336504 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -77,7 +77,7 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ bpf_printk("Aattribute string value is too long\n"); return false; } - if (!get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN)) { + return get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN) return false; } return true; From bb5657a66b5bc630983f81ec739975b42cbef2c3 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Mon, 27 Nov 2023 15:45:26 +0200 Subject: [PATCH 17/33] Code review 1 --- internal/include/go_types.h | 8 ++------ internal/include/otel_types.h | 7 +++---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/internal/include/go_types.h b/internal/include/go_types.h index 56f5377d5..486dfc4fd 100644 --- a/internal/include/go_types.h +++ b/internal/include/go_types.h @@ -177,12 +177,8 @@ static __always_inline void append_item_to_slice(struct go_slice *slice, void *n } } -static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char *dst, s32 max_len) +static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char *dst, u64 max_len) { - if (max_len < 1) - { - return false; - } if (user_str_ptr == NULL) { return false; @@ -196,7 +192,7 @@ static __always_inline bool get_go_string_from_user_ptr(void *user_str_ptr, char return false; } - u32 size_to_read = user_str.len > max_len ? max_len : user_str.len; + 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) { diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index 314336504..c0ae68986 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -77,17 +77,16 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ bpf_printk("Aattribute string value is too long\n"); return false; } - return get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN) - return false; - } - return true; + return get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN); // TODO: handle slices case BOOLSLICE: case INT64SLICE: case FLOAT64SLICE: case STRINGSLICE: + return false; case INVALID: default: + bpf_printk("Invalid attribute value type\n"); return false; } } From 267e9314680667d7b583027c2d8300a984cdf0e0 Mon Sep 17 00:00:00 2001 From: Ron Federman <73110295+RonFed@users.noreply.github.com> Date: Wed, 29 Nov 2023 09:18:31 +0200 Subject: [PATCH 18/33] Apply suggestions from code review Co-authored-by: Tyler Yahn --- instrumentation.go | 23 +++++++++++++++---- .../otel/global/bpf/probe.bpf.c | 4 +++- internal/test/e2e/otelglobal/main.go | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/instrumentation.go b/instrumentation.go index 864fcc460..f9fde31d2 100644 --- a/instrumentation.go +++ b/instrumentation.go @@ -427,11 +427,24 @@ func WithSampler(sampler trace.Sampler) InstrumentationOption { }) } -// WithOtelAPIIntegration returns an [InstrumentationOption] that will configure -// an [Instrumentation] to create spans from non recording spans declared manually -// using the OpenTelemetry API. This is turned off by default and will allow to include -// manually created spans in the trace produced by the auto instrumentation. -func WithOtelAPIIntegration() InstrumentationOption { +// WithGlobal returns an [InstrumentationOption] that will configure an +// [Instrumentation] to record telemetry from the [OpenTelemetry default global +// implementation]. By default, the OpenTelemetry global implementation is a +// no-op implementation of the OpenTelemetry API. However, by using this +// option, all telemetry that would have been dropped by the global +// implementation will be recorded using telemetry pipelines from the +// configured [Instrumentation]. +// +// If the target process overrides the default global implementation (e.g. +// [otel.SetTracerProvider]), the telemetry from that process will go to the +// set implementation. It will not be recorded using the telemetry pipelines +// from the configured [Instrumentation] even if this option is used. +// +// The OpenTelemetry default global implementation is left unchanged (i.e. it +// remains a no-op implementation) if this options is not used. +// +// [default global OpenTelemetry implementation]: https://pkg.go.dev/go.opentelemetry.io/otel +func WithGlobal() InstrumentationOption { return fnOpt(func(_ context.Context, c instConfig) (instConfig, error) { c.withOtelAPI = true return c, nil diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c index d5e138d98..47156c4a9 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c @@ -67,6 +67,7 @@ volatile const u64 span_attributes_pos; // This instrumentation attaches uprobe to the following function: // func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) +// https://github.com/open-telemetry/opentelemetry-go/blob/98b32a6c3a87fbee5d34c063b9096f416b250897/internal/global/trace.go#L149 SEC("uprobe/Start") int uprobe_Start(struct pt_regs *ctx) { struct span_name_t span_name = {0}; @@ -86,6 +87,7 @@ int uprobe_Start(struct pt_regs *ctx) { // This instrumentation attaches uprobe to the following function: // func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) +// https://github.com/open-telemetry/opentelemetry-go/blob/98b32a6c3a87fbee5d34c063b9096f416b250897/internal/global/trace.go#L149 SEC("uprobe/Start") int uprobe_Start_Returns(struct pt_regs *ctx) { // Get the span name passed to the Start function @@ -144,7 +146,7 @@ int uprobe_SetAttributes(struct pt_regs *ctx) { return 0; } - // In Go, "..." is equilaent to passing a slice: https://go.dev/ref/spec#Passing_arguments_to_..._parameters + // In Go, "..." is equivalent to passing a slice: https://go.dev/ref/spec#Passing_arguments_to_..._parameters void *attributes_usr_buf = get_argument(ctx, 2); u64 attributes_len = (u64)get_argument(ctx, 3); convert_go_otel_attributes(attributes_usr_buf, attributes_len, &span->attributes); diff --git a/internal/test/e2e/otelglobal/main.go b/internal/test/e2e/otelglobal/main.go index b2670cb2c..cc3ef572b 100644 --- a/internal/test/e2e/otelglobal/main.go +++ b/internal/test/e2e/otelglobal/main.go @@ -34,10 +34,10 @@ func innerFunction(ctx context.Context) { func createMainSpan(ctx context.Context) { ctx, span := tracer.Start(ctx, "parent") defer span.End() - intAttr := attribute.Int("int_key", 42) innerFunction(ctx) + intAttr := attribute.Int("int_key", 42) strAttr := attribute.String("string_key", "forty-two") boolAttr := attribute.Bool("bool_key", true) floatAttr := attribute.Float64("float_key", 42.3) From c5c73195b1f6c4bc29688b3e262a6667ffa335b2 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 15:00:20 +0200 Subject: [PATCH 19/33] rename WithOtelApi to globalImpl --- instrumentation.go | 8 ++++---- internal/pkg/instrumentation/manager.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/instrumentation.go b/instrumentation.go index f9fde31d2..8887ddec6 100644 --- a/instrumentation.go +++ b/instrumentation.go @@ -118,7 +118,7 @@ func NewInstrumentation(ctx context.Context, opts ...InstrumentationOption) (*In return nil, err } - mngr, err := instrumentation.NewManager(logger, ctrl, c.withOtelAPI) + mngr, err := instrumentation.NewManager(logger, ctrl, c.globalImpl) if err != nil { return nil, err } @@ -174,7 +174,7 @@ type instConfig struct { target process.TargetArgs serviceName string additionalResAttrs []attribute.KeyValue - withOtelAPI bool + globalImpl bool } func newInstConfig(ctx context.Context, opts []InstrumentationOption) (instConfig, error) { @@ -364,7 +364,7 @@ func WithEnv() InstrumentationOption { } if v, ok := lookupEnv(envOtelAPIIntegrationKey); ok { if val, err := strconv.ParseBool(v); err == nil { - c.withOtelAPI = val + c.globalImpl = val } } return c, err @@ -446,7 +446,7 @@ func WithSampler(sampler trace.Sampler) InstrumentationOption { // [default global OpenTelemetry implementation]: https://pkg.go.dev/go.opentelemetry.io/otel func WithGlobal() InstrumentationOption { return fnOpt(func(_ context.Context, c instConfig) (instConfig, error) { - c.withOtelAPI = true + c.globalImpl = true return c, nil }) } diff --git a/internal/pkg/instrumentation/manager.go b/internal/pkg/instrumentation/manager.go index 9f6f1e523..08b59cc99 100644 --- a/internal/pkg/instrumentation/manager.go +++ b/internal/pkg/instrumentation/manager.go @@ -45,11 +45,11 @@ type Manager struct { done chan bool incomingEvents chan *probe.Event otelController *opentelemetry.Controller - withOtelAPI bool + globalImpl bool } // NewManager returns a new [Manager]. -func NewManager(logger logr.Logger, otelController *opentelemetry.Controller, withOtelAPI bool) (*Manager, error) { +func NewManager(logger logr.Logger, otelController *opentelemetry.Controller, globalImpl bool) (*Manager, error) { logger = logger.WithName("Manager") m := &Manager{ logger: logger, @@ -57,7 +57,7 @@ func NewManager(logger logr.Logger, otelController *opentelemetry.Controller, wi done: make(chan bool, 1), incomingEvents: make(chan *probe.Event), otelController: otelController, - withOtelAPI: withOtelAPI, + globalImpl: globalImpl, } err := m.registerProbes() @@ -214,7 +214,7 @@ func (m *Manager) registerProbes() error { dbSql.New(m.logger), } - if m.withOtelAPI { + if m.globalImpl { insts = append(insts, otelAPITrace.New(m.logger)) } From 2051021ca91dc991405730eb5907912b97111f75 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 15:05:53 +0200 Subject: [PATCH 20/33] Rename probe folder --- .../otel/{global => traceglobal}/bpf/probe.bpf.c | 0 .../otel/{global => traceglobal}/bpf_bpfel_arm64.go | 0 .../otel/{global => traceglobal}/bpf_bpfel_x86.go | 0 .../go.opentelemetry.io/otel/{global => traceglobal}/probe.go | 0 .../otel/{global => traceglobal}/probe_test.go | 0 internal/pkg/instrumentation/manager.go | 4 ++-- 6 files changed, 2 insertions(+), 2 deletions(-) rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{global => traceglobal}/bpf/probe.bpf.c (100%) rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{global => traceglobal}/bpf_bpfel_arm64.go (100%) rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{global => traceglobal}/bpf_bpfel_x86.go (100%) rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{global => traceglobal}/probe.go (100%) rename internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/{global => traceglobal}/probe_test.go (100%) diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf/probe.bpf.c similarity index 100% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf/probe.bpf.c rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf/probe.bpf.c diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf_bpfel_arm64.go similarity index 100% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_arm64.go rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf_bpfel_arm64.go diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf_bpfel_x86.go similarity index 100% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/bpf_bpfel_x86.go rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/bpf_bpfel_x86.go diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go similarity index 100% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe.go rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe_test.go similarity index 100% rename from internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global/probe_test.go rename to internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe_test.go diff --git a/internal/pkg/instrumentation/manager.go b/internal/pkg/instrumentation/manager.go index 08b59cc99..d965c9de7 100644 --- a/internal/pkg/instrumentation/manager.go +++ b/internal/pkg/instrumentation/manager.go @@ -24,7 +24,7 @@ import ( dbSql "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/database/sql" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/github.com/gin-gonic/gin" - otelAPITrace "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/global" + otelTraceGlobal "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/google.golang.org/grpc" grpcServer "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server" httpClient "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/net/http/client" @@ -215,7 +215,7 @@ func (m *Manager) registerProbes() error { } if m.globalImpl { - insts = append(insts, otelAPITrace.New(m.logger)) + insts = append(insts, otelTraceGlobal.New(m.logger)) } for _, i := range insts { From 6e838b42deff5c27f067e02de33b6f85b3daabb3 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 15:40:27 +0200 Subject: [PATCH 21/33] Inject attribute types consts from Go --- internal/include/otel_types.h | 59 +++++++++++-------- .../otel/traceglobal/probe.go | 36 +++++++++++ 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index c0ae68986..8d6bc6021 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -21,15 +21,18 @@ /* Defintions should mimic structs defined in go.opentelemetry.io/otel/attribute */ typedef u64 attr_val_type_t; -#define INVALID 0 -#define BOOL 1 -#define INT64 2 -#define FLOAT64 3 -#define STRING 4 -#define BOOLSLICE 5 -#define INT64SLICE 6 -#define FLOAT64SLICE 7 -#define STRINGSLICE 8 +// Injected in init +volatile const attr_val_type_t attr_type_invalid; + +volatile const attr_val_type_t attr_type_bool; +volatile const attr_val_type_t attr_type_int64; +volatile const attr_val_type_t attr_type_float64; +volatile const attr_val_type_t attr_type_string; + +volatile const attr_val_type_t attr_type_boolslice; +volatile const attr_val_type_t attr_type_int64slice; +volatile const attr_val_type_t attr_type_float64slice; +volatile const attr_val_type_t attr_type_stringslice; typedef struct go_otel_attr_value { attr_val_type_t vtype; @@ -62,14 +65,25 @@ typedef struct otel_attributes { static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_value_t *go_attr_value) { - switch (go_attr_value->vtype) - { - case BOOL: - case INT64: - case FLOAT64: + if (attr == NULL || go_attr_value == NULL){ + return false; + } + + if (go_attr_value->vtype == attr_type_invalid) { + bpf_printk("Invalid attribute value type\n"); + return false; + } + + // Constant size values + if (go_attr_value->vtype == attr_type_bool || + go_attr_value->vtype == attr_type_int64 || + go_attr_value->vtype == attr_type_float64) { bpf_probe_read(&attr->value, sizeof(s64), &go_attr_value->numeric); return true; - case STRING: + } + + // String values + if (go_attr_value->vtype == attr_type_string) { if (go_attr_value->string.len <= 0){ return false; } @@ -78,17 +92,10 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ return false; } return get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN); - // TODO: handle slices - case BOOLSLICE: - case INT64SLICE: - case FLOAT64SLICE: - case STRINGSLICE: - return false; - case INVALID: - default: - bpf_printk("Invalid attribute value type\n"); - return false; } + + // TODO: handle slices + return false; } static __always_inline void convert_go_otel_attributes(void *attrs_buf, s64 slice_len, otel_attributes_t *enc_attrs) @@ -112,7 +119,7 @@ static __always_inline void convert_go_otel_attributes(void *attrs_buf, s64 slic // Read the value struct bpf_probe_read(&go_attr_value, sizeof(go_otel_attr_value_t), &go_attr[go_attr_index].value); - if (go_attr_value.vtype == INVALID) { + if (go_attr_value.vtype == attr_type_invalid) { continue; } 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 2adf0e84d..827ee7443 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 @@ -51,6 +51,42 @@ func New(logger logr.Logger) probe.Probe { Consts: []probe.Const{ probe.RegistersABIConst{}, probe.AllocationConst{}, + probe.KeyValConst{ + Key: "attr_type_invalid", + Val: uint64(attribute.INVALID), + }, + probe.KeyValConst{ + Key: "attr_type_bool", + Val: uint64(attribute.BOOL), + }, + probe.KeyValConst{ + Key: "attr_type_int64", + Val: uint64(attribute.INT64), + }, + probe.KeyValConst{ + Key: "attr_type_float64", + Val: uint64(attribute.FLOAT64), + }, + probe.KeyValConst{ + Key: "attr_type_string", + Val: uint64(attribute.STRING), + }, + probe.KeyValConst{ + Key: "attr_type_boolslice", + Val: uint64(attribute.BOOLSLICE), + }, + probe.KeyValConst{ + Key: "attr_type_int64slice", + Val: uint64(attribute.INT64SLICE), + }, + probe.KeyValConst{ + Key: "attr_type_float64slice", + Val: uint64(attribute.FLOAT64SLICE), + }, + probe.KeyValConst{ + Key: "attr_type_stringslice", + Val: uint64(attribute.STRINGSLICE), + }, }, Uprobes: map[string]probe.UprobeFunc[bpfObjects]{ "go.opentelemetry.io/otel/internal/global.(*tracer).Start": uprobeTracerStart, From fdea0b01981c2faf6b21774946ae93d61e6594be Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 16:56:57 +0200 Subject: [PATCH 22/33] add cli flag for otel-global to record telemetry from the OpenTelemetry default global --- .github/workflows/e2e/k8s/sample-job.yml | 3 +-- cli/main.go | 13 +++++++++++-- examples/rolldice/docker-compose.yaml | 2 +- instrumentation.go | 11 +---------- internal/pkg/instrumentation/manager.go | 4 ++-- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/e2e/k8s/sample-job.yml b/.github/workflows/e2e/k8s/sample-job.yml index b458d22aa..f241f5bb2 100644 --- a/.github/workflows/e2e/k8s/sample-job.yml +++ b/.github/workflows/e2e/k8s/sample-job.yml @@ -26,6 +26,7 @@ spec: - name: auto-instrumentation image: otel-go-instrumentation imagePullPolicy: IfNotPresent + command: ["/otel-go-instrumentation", "-global-impl"] env: - name: OTEL_GO_AUTO_TARGET_EXE value: /sample-app/main @@ -37,8 +38,6 @@ spec: value: "tracecontext,baggage" - name: OTEL_GO_AUTO_INCLUDE_DB_STATEMENT value: "true" - - name: OTEL_GO_AUTO_MANUAL_INTEGRATION - value: "true" resources: {} securityContext: runAsUser: 0 diff --git a/cli/main.go b/cli/main.go index d2d98f321..ae96c978e 100644 --- a/cli/main.go +++ b/cli/main.go @@ -67,6 +67,9 @@ func newLogger() logr.Logger { } func main() { + var globalImpl bool + + flag.BoolVar(&globalImpl, "global-impl", false, "Record telemetry from the OpenTelemetry default global implementation") flag.Usage = usage flag.Parse() @@ -88,8 +91,14 @@ func main() { } }() - logger.Info("building OpenTelemetry Go instrumentation ...") - inst, err := auto.NewInstrumentation(ctx, auto.WithEnv()) + logger.Info("building OpenTelemetry Go instrumentation ...", "globalImpl", globalImpl) + + instOptions := []auto.InstrumentationOption{auto.WithEnv()} + if globalImpl { + instOptions = append(instOptions, auto.WithGlobal()) + } + + inst, err := auto.NewInstrumentation(ctx, instOptions...) if err != nil { logger.Error(err, "failed to create instrumentation") return diff --git a/examples/rolldice/docker-compose.yaml b/examples/rolldice/docker-compose.yaml index aaec615f0..870d6ebc2 100644 --- a/examples/rolldice/docker-compose.yaml +++ b/examples/rolldice/docker-compose.yaml @@ -30,9 +30,9 @@ services: - OTEL_GO_AUTO_TARGET_EXE=/app/main - OTEL_SERVICE_NAME=rolldice - OTEL_PROPAGATORS=tracecontext,baggage - - OTEL_GO_AUTO_MANUAL_INTEGRATION=true volumes: - /proc:/host/proc + command: ["/otel-go-instrumentation", "-global-impl"] jaeger: image: jaegertracing/all-in-one:latest diff --git a/instrumentation.go b/instrumentation.go index 8887ddec6..18c9f944a 100644 --- a/instrumentation.go +++ b/instrumentation.go @@ -22,7 +22,6 @@ import ( "os" "path/filepath" "runtime" - "strconv" "strings" "github.com/go-logr/logr" @@ -54,9 +53,6 @@ const ( // envTracesExportersKey is the key for the environment variable value // containing what OpenTelemetry trace exporter to use. envTracesExportersKey = "OTEL_TRACES_EXPORTER" - // envOtelAPIIntegrationKey is the key for the environment variable value - // containing whether to create spans from non recording spans declared manually. - envOtelAPIIntegrationKey = "OTEL_GO_AUTO_MANUAL_INTEGRATION" ) // Instrumentation manages and controls all OpenTelemetry Go @@ -174,7 +170,7 @@ type instConfig struct { target process.TargetArgs serviceName string additionalResAttrs []attribute.KeyValue - globalImpl bool + globalImpl bool } func newInstConfig(ctx context.Context, opts []InstrumentationOption) (instConfig, error) { @@ -362,11 +358,6 @@ func WithEnv() InstrumentationOption { c.serviceName = name c.additionalResAttrs = append(c.additionalResAttrs, attrs...) } - if v, ok := lookupEnv(envOtelAPIIntegrationKey); ok { - if val, err := strconv.ParseBool(v); err == nil { - c.globalImpl = val - } - } return c, err }) } diff --git a/internal/pkg/instrumentation/manager.go b/internal/pkg/instrumentation/manager.go index d965c9de7..8ee046f48 100644 --- a/internal/pkg/instrumentation/manager.go +++ b/internal/pkg/instrumentation/manager.go @@ -45,7 +45,7 @@ type Manager struct { done chan bool incomingEvents chan *probe.Event otelController *opentelemetry.Controller - globalImpl bool + globalImpl bool } // NewManager returns a new [Manager]. @@ -57,7 +57,7 @@ func NewManager(logger logr.Logger, otelController *opentelemetry.Controller, gl done: make(chan bool, 1), incomingEvents: make(chan *probe.Event), otelController: otelController, - globalImpl: globalImpl, + globalImpl: globalImpl, } err := m.registerProbes() From a5a6964ac2526fb3074f9ac41d27ea18bf960826 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 18:02:57 +0200 Subject: [PATCH 23/33] debug tests --- .github/workflows/kind.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/kind.yml b/.github/workflows/kind.yml index 8c9a8da09..a907def91 100644 --- a/.github/workflows/kind.yml +++ b/.github/workflows/kind.yml @@ -68,7 +68,7 @@ jobs: kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml - name: check job status run: | - kubectl wait --for=condition=Complete --timeout=60s job/sample-job + kubectl wait --for=condition=Complete --timeout=60s job/sample-job && kubectl logs -l app=sample -c auto-instrumentation - name: copy telemetry trace output run: | kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./internal/test/e2e/${{ matrix.library }}/traces-orig.json diff --git a/Makefile b/Makefile index 51da72727..1834aaa64 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ fixtures/%: helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/charts/opentelemetry-collector kubectl wait --for=condition=Ready --timeout=60s pod/test-opentelemetry-collector-0 kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml - kubectl wait --for=condition=Complete --timeout=60s job/sample-job + kubectl wait --for=condition=Complete --timeout=60s job/sample-job && kubectl logs -l app=sample -c auto-instrumentation kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./internal/test/e2e/$(LIBRARY)/traces-orig.json rm -f ./internal/test/e2e/$(LIBRARY)/traces.json bats ./internal/test/e2e/$(LIBRARY)/verify.bats From abff81820defc65950aef249bbbffe44d41137db Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 18:14:48 +0200 Subject: [PATCH 24/33] fix debug --- .github/workflows/kind.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/kind.yml b/.github/workflows/kind.yml index a907def91..8ae96a468 100644 --- a/.github/workflows/kind.yml +++ b/.github/workflows/kind.yml @@ -68,7 +68,7 @@ jobs: kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml - name: check job status run: | - kubectl wait --for=condition=Complete --timeout=60s job/sample-job && kubectl logs -l app=sample -c auto-instrumentation + kubectl wait --for=condition=Complete --timeout=60s job/sample-job || kubectl logs -l app=sample -c auto-instrumentation - name: copy telemetry trace output run: | kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./internal/test/e2e/${{ matrix.library }}/traces-orig.json diff --git a/Makefile b/Makefile index 1834aaa64..ac810e4b8 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ fixtures/%: helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/charts/opentelemetry-collector kubectl wait --for=condition=Ready --timeout=60s pod/test-opentelemetry-collector-0 kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml - kubectl wait --for=condition=Complete --timeout=60s job/sample-job && kubectl logs -l app=sample -c auto-instrumentation + kubectl wait --for=condition=Complete --timeout=60s job/sample-job || kubectl logs -l app=sample -c auto-instrumentation kubectl cp -c filecp default/test-opentelemetry-collector-0:tmp/trace.json ./internal/test/e2e/$(LIBRARY)/traces-orig.json rm -f ./internal/test/e2e/$(LIBRARY)/traces.json bats ./internal/test/e2e/$(LIBRARY)/verify.bats From f5a262777b9fd16615378958c30145c9f8dfd9ab Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 22:11:29 +0200 Subject: [PATCH 25/33] modify set_attr_value --- internal/include/otel_types.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index 8d6bc6021..b7e680a4e 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -69,21 +69,23 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ return false; } - if (go_attr_value->vtype == attr_type_invalid) { + attr_val_type_t vtype = go_attr_value->vtype; + + if (vtype == attr_type_invalid) { bpf_printk("Invalid attribute value type\n"); return false; } // Constant size values - if (go_attr_value->vtype == attr_type_bool || - go_attr_value->vtype == attr_type_int64 || - go_attr_value->vtype == attr_type_float64) { - bpf_probe_read(&attr->value, sizeof(s64), &go_attr_value->numeric); + if (vtype == attr_type_bool || + vtype == attr_type_int64 || + vtype == attr_type_float64) { + bpf_probe_read(attr->value, sizeof(s64), &go_attr_value->numeric); return true; } // String values - if (go_attr_value->vtype == attr_type_string) { + if (vtype == attr_type_string) { if (go_attr_value->string.len <= 0){ return false; } From 3dd9447010611f754a8c7db1d57feec2ffb8de4f Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 22:24:56 +0200 Subject: [PATCH 26/33] print verifier log --- .github/workflows/e2e/k8s/sample-job.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/e2e/k8s/sample-job.yml b/.github/workflows/e2e/k8s/sample-job.yml index f241f5bb2..c1538d1ee 100644 --- a/.github/workflows/e2e/k8s/sample-job.yml +++ b/.github/workflows/e2e/k8s/sample-job.yml @@ -38,6 +38,8 @@ spec: value: "tracecontext,baggage" - name: OTEL_GO_AUTO_INCLUDE_DB_STATEMENT value: "true" + - name: OTEL_GO_AUTO_SHOW_VERIFIER_LOG + value: "true" resources: {} securityContext: runAsUser: 0 From 18dd13cc7a3b6f015bb4a580e22f6c7c4daeaa02 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 23:10:33 +0200 Subject: [PATCH 27/33] larger verifier log --- internal/pkg/instrumentation/utils/ebpf.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/instrumentation/utils/ebpf.go b/internal/pkg/instrumentation/utils/ebpf.go index 3cad8d98d..d120479b1 100644 --- a/internal/pkg/instrumentation/utils/ebpf.go +++ b/internal/pkg/instrumentation/utils/ebpf.go @@ -33,7 +33,7 @@ func LoadEBPFObjects(spec *ebpf.CollectionSpec, to interface{}, opts *ebpf.Colle // Getting full verifier log is expensive, so we only do it if the user explicitly asks for it. showVerifierLogs := shouldShowVerifierLogs() if showVerifierLogs { - opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 100 + opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 10000 opts.Programs.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelBranch | ebpf.LogLevelStats } From 9c83561e9611bd50a8a7f38a97447f0157079c9c Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 23:39:12 +0200 Subject: [PATCH 28/33] ... --- internal/include/otel_types.h | 27 +++++++++++----------- internal/pkg/instrumentation/utils/ebpf.go | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index b7e680a4e..5fd0401dd 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -18,24 +18,23 @@ #include "go_types.h" #include "common.h" -/* Defintions should mimic structs defined in go.opentelemetry.io/otel/attribute */ -typedef u64 attr_val_type_t; - // Injected in init -volatile const attr_val_type_t attr_type_invalid; +volatile const u64 attr_type_invalid; -volatile const attr_val_type_t attr_type_bool; -volatile const attr_val_type_t attr_type_int64; -volatile const attr_val_type_t attr_type_float64; -volatile const attr_val_type_t attr_type_string; +volatile const u64 attr_type_bool; +volatile const u64 attr_type_int64; +volatile const u64 attr_type_float64; +volatile const u64 attr_type_string; -volatile const attr_val_type_t attr_type_boolslice; -volatile const attr_val_type_t attr_type_int64slice; -volatile const attr_val_type_t attr_type_float64slice; -volatile const attr_val_type_t attr_type_stringslice; +volatile const u64 attr_type_boolslice; +volatile const u64 attr_type_int64slice; +volatile const u64 attr_type_float64slice; +volatile const u64 attr_type_stringslice; + +/* Defintions should mimic structs defined in go.opentelemetry.io/otel/attribute */ typedef struct go_otel_attr_value { - attr_val_type_t vtype; + u64 vtype; u64 numeric; struct go_string string; struct go_iface slice; @@ -69,7 +68,7 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ return false; } - attr_val_type_t vtype = go_attr_value->vtype; + u64 vtype = go_attr_value->vtype; if (vtype == attr_type_invalid) { bpf_printk("Invalid attribute value type\n"); diff --git a/internal/pkg/instrumentation/utils/ebpf.go b/internal/pkg/instrumentation/utils/ebpf.go index d120479b1..12e5f189d 100644 --- a/internal/pkg/instrumentation/utils/ebpf.go +++ b/internal/pkg/instrumentation/utils/ebpf.go @@ -34,7 +34,7 @@ func LoadEBPFObjects(spec *ebpf.CollectionSpec, to interface{}, opts *ebpf.Colle showVerifierLogs := shouldShowVerifierLogs() if showVerifierLogs { opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 10000 - opts.Programs.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelBranch | ebpf.LogLevelStats + opts.Programs.LogLevel = ebpf.LogLevelStats } err := spec.LoadAndAssign(to, opts) From f4690cf60e6b71a5a0a3e011ace209799e8acd5c Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Wed, 29 Nov 2023 23:53:43 +0200 Subject: [PATCH 29/33] simplify eBPF code --- internal/include/otel_types.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index 5fd0401dd..d3534d306 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -64,10 +64,6 @@ typedef struct otel_attributes { static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_value_t *go_attr_value) { - if (attr == NULL || go_attr_value == NULL){ - return false; - } - u64 vtype = go_attr_value->vtype; if (vtype == attr_type_invalid) { @@ -85,9 +81,6 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ // String values if (vtype == attr_type_string) { - if (go_attr_value->string.len <= 0){ - return false; - } if (go_attr_value->string.len >= OTEL_ATTRIBUTE_VALUE_MAX_LEN) { bpf_printk("Aattribute string value is too long\n"); return false; @@ -126,9 +119,6 @@ static __always_inline void convert_go_otel_attributes(void *attrs_buf, s64 slic // Read the key string bpf_probe_read(&go_str, sizeof(struct go_string), &go_attr[go_attr_index].key); - if (go_str.len <= 0){ - continue; - } if (go_str.len >= OTEL_ATTRIBUTE_KEY_MAX_LEN) { // key string is too large bpf_printk("Attribute key string is too long\n"); From 12feb5857aca663211f70aff1a73ba84bc0ab584 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Thu, 30 Nov 2023 09:07:00 +0200 Subject: [PATCH 30/33] Clean-up and updating changelog --- .github/workflows/e2e/k8s/sample-job.yml | 2 -- CHANGELOG.md | 2 +- examples/rolldice/docker-compose.yaml | 1 - examples/rolldice/main.go | 29 +--------------------- internal/pkg/instrumentation/utils/ebpf.go | 4 +-- 5 files changed, 4 insertions(+), 34 deletions(-) diff --git a/.github/workflows/e2e/k8s/sample-job.yml b/.github/workflows/e2e/k8s/sample-job.yml index c1538d1ee..f241f5bb2 100644 --- a/.github/workflows/e2e/k8s/sample-job.yml +++ b/.github/workflows/e2e/k8s/sample-job.yml @@ -38,8 +38,6 @@ spec: value: "tracecontext,baggage" - name: OTEL_GO_AUTO_INCLUDE_DB_STATEMENT value: "true" - - name: OTEL_GO_AUTO_SHOW_VERIFIER_LOG - value: "true" resources: {} securityContext: runAsUser: 0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f03fd41c..fe6eaa4b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http ### Added -- Manual instrumentation support - Phase 1 ([#523]https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/523) +- Manual instrumentation support - Phase 1. Adding cli flag named `global-impl` that allows to record telemetry from the OpenTelemetry default global implementation. ([#523]https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/523) - Add `WithResourceAttributes` `InstrumentationOption` to configure `Instrumentation` to add additional resource attributes. ([#522](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/522)) ### Changed diff --git a/examples/rolldice/docker-compose.yaml b/examples/rolldice/docker-compose.yaml index 870d6ebc2..448032e2a 100644 --- a/examples/rolldice/docker-compose.yaml +++ b/examples/rolldice/docker-compose.yaml @@ -32,7 +32,6 @@ services: - OTEL_PROPAGATORS=tracecontext,baggage volumes: - /proc:/host/proc - command: ["/otel-go-instrumentation", "-global-impl"] jaeger: image: jaegertracing/all-in-one:latest diff --git a/examples/rolldice/main.go b/examples/rolldice/main.go index bd6866940..f9342c05b 100644 --- a/examples/rolldice/main.go +++ b/examples/rolldice/main.go @@ -15,17 +15,12 @@ package main import ( - "context" "fmt" - "math" "math/rand" "net/http" "time" "go.uber.org/zap" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" ) // Server is Http server that exposes multiple endpoints. @@ -33,8 +28,6 @@ type Server struct { rand *rand.Rand } -var tracer = otel.Tracer("rolldice") - // NewServer creates a server struct after initialing rand. func NewServer() *Server { rd := rand.New(rand.NewSource(time.Now().Unix())) @@ -43,27 +36,8 @@ func NewServer() *Server { } } -func (s *Server) innerFunction(ctx context.Context) { - _, span := tracer.Start(ctx, "innerFunction") - defer span.End() - - span.SetAttributes(attribute.String("inner.key", "inner.value")) -} - -func (s *Server) rolldice(w http.ResponseWriter, r *http.Request) { - ctx, span := tracer.Start(r.Context(), "roll") - defer span.End() +func (s *Server) rolldice(w http.ResponseWriter, _ *http.Request) { n := s.rand.Intn(6) + 1 - - rollValueAttr := attribute.Int("roll.value", n) - piAttr := attribute.Float64("pi", math.Pi) - - s.innerFunction(ctx) - - strAttr := attribute.String("nice.key", "string value!") - strAttr2 := attribute.String("nice.key2", "string value 2!") - span.SetAttributes(rollValueAttr, piAttr, strAttr, strAttr2) - logger.Info("rolldice called", zap.Int("dice", n)) fmt.Fprintf(w, "%v", n) } @@ -83,7 +57,6 @@ func main() { fmt.Printf("error creating zap logger, error:%v", err) return } - port := fmt.Sprintf(":%d", 8080) logger.Info("starting http server", zap.String("port", port)) diff --git a/internal/pkg/instrumentation/utils/ebpf.go b/internal/pkg/instrumentation/utils/ebpf.go index 12e5f189d..3cad8d98d 100644 --- a/internal/pkg/instrumentation/utils/ebpf.go +++ b/internal/pkg/instrumentation/utils/ebpf.go @@ -33,8 +33,8 @@ func LoadEBPFObjects(spec *ebpf.CollectionSpec, to interface{}, opts *ebpf.Colle // Getting full verifier log is expensive, so we only do it if the user explicitly asks for it. showVerifierLogs := shouldShowVerifierLogs() if showVerifierLogs { - opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 10000 - opts.Programs.LogLevel = ebpf.LogLevelStats + opts.Programs.LogSize = ebpf.DefaultVerifierLogSize * 100 + opts.Programs.LogLevel = ebpf.LogLevelInstruction | ebpf.LogLevelBranch | ebpf.LogLevelStats } err := spec.LoadAndAssign(to, opts) From fcd527c0cd685ee58b2b6fbbce1b317f183d5149 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Thu, 30 Nov 2023 09:14:51 +0200 Subject: [PATCH 31/33] run make precommit --- examples/go.mod | 6 +----- examples/go.sum | 13 +------------ 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index 76a0440c4..57de53a43 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -4,14 +4,10 @@ go 1.20 require ( github.com/mattn/go-sqlite3 v1.14.18 - go.opentelemetry.io/otel v1.19.0 go.uber.org/zap v1.26.0 ) require ( - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect go.uber.org/multierr v1.10.0 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index f4c2f9865..59f735271 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -1,20 +1,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= From 42b36916807a142ffe6fe23105907e40a7940703 Mon Sep 17 00:00:00 2001 From: Ron Federman Date: Thu, 30 Nov 2023 15:13:34 +0200 Subject: [PATCH 32/33] Fix doc --- instrumentation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation.go b/instrumentation.go index 18c9f944a..3cffa07c8 100644 --- a/instrumentation.go +++ b/instrumentation.go @@ -434,7 +434,7 @@ func WithSampler(sampler trace.Sampler) InstrumentationOption { // The OpenTelemetry default global implementation is left unchanged (i.e. it // remains a no-op implementation) if this options is not used. // -// [default global OpenTelemetry implementation]: https://pkg.go.dev/go.opentelemetry.io/otel +// [OpenTelemetry default global implementation]: https://pkg.go.dev/go.opentelemetry.io/otel func WithGlobal() InstrumentationOption { return fnOpt(func(_ context.Context, c instConfig) (instConfig, error) { c.globalImpl = true From 6205df51d38313bd01a17a2801aad739c69aa266 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Thu, 7 Dec 2023 08:32:58 -0800 Subject: [PATCH 33/33] Apply suggestions from code review --- CHANGELOG.md | 4 +++- internal/include/otel_types.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0656c3bb..15769d13c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http ### Added -- Manual instrumentation support - Phase 1. Adding cli flag named `global-impl` that allows to record telemetry from the OpenTelemetry default global implementation. ([#523]https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/523) +- The CLI flag `global-impl` is added. + This flag, when used, enables the instrumentation of the OpenTelemetry default global implementation (https://pkg.go.dev/go.opentelemetry.io/otel). + This means that all trace telemetry from this implementation that would normally be dropped will instead be recorded with the auto-instrumentation pipeline. ([#523]https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/523) - Add `WithResourceAttributes` `InstrumentationOption` to configure `Instrumentation` to add additional resource attributes. ([#522](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/522)) ### Changed diff --git a/internal/include/otel_types.h b/internal/include/otel_types.h index d3534d306..cc7eb82c2 100644 --- a/internal/include/otel_types.h +++ b/internal/include/otel_types.h @@ -88,7 +88,7 @@ static __always_inline bool set_attr_value(otel_attirbute_t *attr, go_otel_attr_ return get_go_string_from_user_ptr(&go_attr_value->string, attr->value, OTEL_ATTRIBUTE_VALUE_MAX_LEN); } - // TODO: handle slices + // TODO (#525): handle slices return false; }