diff --git a/Dockerfile b/Dockerfile index a84c2f27cc5..9e5ec0c2177 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ GOOS="$TARGETOS" GOARCH="$TARGETARCH" GOARM=${TARGETVARIANT#v} \ RELEASE_BUILD=${RELEASE_BUILD} VERSION=${VERSION} \ - GO_TAGS="netgo builtinassets promtail_journal_enabled" \ + GO_TAGS="netgo builtinassets promtail_journal_enabled pyroscope_ebpf" \ GOEXPERIMENT=${GOEXPERIMENT} \ make alloy diff --git a/docs/sources/reference/components/pyroscope/pyroscope.ebpf.md b/docs/sources/reference/components/pyroscope/pyroscope.ebpf.md index cc0f290b8bc..cd29f3f83b3 100644 --- a/docs/sources/reference/components/pyroscope/pyroscope.ebpf.md +++ b/docs/sources/reference/components/pyroscope/pyroscope.ebpf.md @@ -15,19 +15,25 @@ title: pyroscope.ebpf `pyroscope.ebpf` configures an eBPF profiling job for the current host. The collected performance profiles are forwarded to the list of receivers passed in `forward_to`. +The `pyroscope.ebpf` component embeds the [`grafana/opentelemetry-ebpf-profiler`][] which is a fork of [`open-telemetry/opentelemetry-ebpf-profiler`][]. + + [`grafana/opentelemetry-ebpf-profiler`]: https://github.com/grafana/opentelemetry-ebpf-profiler + [`open-telemetry/opentelemetry-ebpf-profiler`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler + {{< admonition type="note" >}} To use the `pyroscope.ebpf` component you must run {{< param "PRODUCT_NAME" >}} as root and inside the host PID namespace. {{< /admonition >}} +{{< admonition type="note" >}} +The profiler requires file system storage at `/tmp/symb-cache` to store symbol cache data. Ensure this directory is accessible and has sufficient storage space. +{{< /admonition >}} + You can specify multiple `pyroscope.ebpf` components by giving them different labels, however it's not recommended as it can lead to additional memory and CPU usage. ## Supported languages -This eBPF profiler only collects CPU profiles. Generally, natively compiled languages like C/C++, Go, and Rust are supported. Refer to [Troubleshooting unknown symbols][troubleshooting] for additional requirements. - -Python is the only supported high-level language, as long as `python_enabled=true`. -Other high-level languages like Java, Ruby, PHP, and JavaScript require additional work to show stack traces of methods in these languages correctly. -Currently, the CPU usage for these languages is reported as belonging to the runtime's methods. +- Native code (C/C++, Rust, Zig, Go, etc. without debug symbols on host) +- Broad set of HLLs (Hotspot JVM, Python, Ruby, PHP, Node.JS, V8, Perl). ## Usage @@ -44,27 +50,36 @@ The component configures and starts a new eBPF profiling job to collect performa You can use the following arguments with `pyroscope.ebpf`: -| Name | Type | Description | Default | Required | -| ------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- | -| `forward_to` | `list(ProfilesReceiver)` | List of receivers to send collected profiles to. | | yes | -| `targets` | `list(map(string))` | List of targets to group profiles by container id | | yes | -| `build_id_cache_size` | `int` | The size of the elf file build id -> symbols table LRU cache | `64` | no | -| `collect_interval` | `duration` | How frequently to collect profiles | `"15s"` | no | -| `collect_kernel_profile` | `bool` | A flag to enable/disable collection of kernelspace profiles | `true` | no | -| `collect_user_profile` | `bool` | A flag to enable/disable collection of userspace profiles | `true` | no | -| `container_id_cache_size` | `int` | The size of the PID -> container ID table LRU cache | `1024` | no | -| `demangle` | `string` | C++ demangle mode. Available options are: `none`, `simplified`, `templates`, or `full` | `"none"` | no | -| `go_table_fallback` | `bool` | A flag to enable symbol lookup in `.sym` / `.dynsym` sections when `.gopclntab` lookup failed. May be useful for `cgo` binaries. | `false` | no | -| `pid_cache_size` | `int` | The size of the PID -> proc symbols table LRU cache | `32` | no | -| `pid_map_size` | `int` | The size of eBPF PID map | `2048` | no | -| `python_enabled` | `bool` | A flag to enable/disable python profiling | `true` | no | -| `same_file_cache_size` | `int` | The size of the elf file -> symbols table LRU cache | `8` | no | -| `sample_rate` | `int` | How many times per second to collect profile samples | `97` | no | -| `symbols_map_size` | `int` | The size of eBPF symbols map | `16384` | no | +| Name | Type | Description | Default | Required | +|---------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------|----------|----------| +| `forward_to` | `list(ProfilesReceiver)` | List of receivers to send collected profiles to. | | yes | +| `targets` | `list(map(string))` | List of targets to group profiles by container ID. | | yes | +| `build_id_cache_size` | `int` | Deprecated (no-op), previously controlled the size of the elf file build id -> symbols table LRU cache. | `64` | no | +| `cache_rounds` | `int` | Deprecated (no-op), previously controlled the number of cache rounds. | | no | +| `collect_interval` | `duration` | How frequently to collect profiles. | `"15s"` | no | +| `collect_kernel_profile` | `bool` | Deprecated (no-op), previously enabled collection of kernelspace profiles. | `true` | no | +| `collect_user_profile` | `bool` | Deprecated (no-op), previously enabled collection of userspace profiles. | `true` | no | +| `container_id_cache_size` | `int` | The size of the PID -> container ID table LRU cache. | `1024` | no | +| `demangle` | `string` | C++ demangle mode. Available options are: `none`, `simplified`, `templates`, or `full`. | `"none"` | no | +| `dotnet_enabled` | `bool` | A flag to enable or disable .NET profiling. | `true` | no | +| `go_table_fallback` | `bool` | Deprecated (no-op), previously enabled symbol lookup in `.sym` / `.dynsym` sections when `.gopclntab` lookup failed. | `false` | no | +| `hotspot_enabled` | `bool` | A flag to enable ordisable hotspot profiling. | `true` | no | +| `perl_enabled` | `bool` | A flag to enable or disable Perl profiling. | `true` | no | +| `php_enabled` | `bool` | A flag to enable or disable PHP profiling. | `true` | no | +| `pid_cache_size` | `int` | Deprecated (no-op), previously controlled the size of the PID -> proc symbols table LRU cache. | `32` | no | +| `pid_map_size` | `int` | Deprecated (no-op), previously controlled the size of eBPF PID map. | `2048` | no | +| `python_enabled` | `bool` | A flag to enable or disable python profiling. | `true` | no | +| `ruby_enabled` | `bool` | A flag to enable or disable Ruby profiling. | `true` | no | +| `same_file_cache_size` | `int` | Deprecated (no-op), previously controlled the size of the elf file -> symbols table LRU cache. | `8` | no | +| `sample_rate` | `int` | How many times per second to collect profile samples. | `19` | no | +| `symbols_map_size` | `int` | Deprecated (no-op), previously controlled the size of eBPF symbols map . | `16384` | no | +| `v8_enabled` | `bool` | A flag to enable/disable V8 profiling. | `true` | no | Only the `forward_to` and `targets` fields are required. Omitted fields take their default values. +Several arguments are marked as "Deprecated (no-op)". These arguments were previously used for configuring various cache sizes and behaviors, but they no longer have any effect. They are kept for backward compatibility but will be removed in a future release. It is recommended to remove these arguments from your configuration. + ## Blocks The `pyroscope.ebpf` component doesn't support any blocks. You can configure this component with arguments. diff --git a/go.mod b/go.mod index ad812728e43..10ea132bf69 100644 --- a/go.mod +++ b/go.mod @@ -19,10 +19,10 @@ require ( github.com/PuerkitoBio/rehttp v1.4.0 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b - github.com/aws/aws-sdk-go-v2 v1.36.3 - github.com/aws/aws-sdk-go-v2/config v1.29.14 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 - github.com/aws/aws-sdk-go-v2/service/s3 v1.80.0 + github.com/aws/aws-sdk-go-v2 v1.36.4 + github.com/aws/aws-sdk-go-v2/config v1.29.16 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 + github.com/aws/aws-sdk-go-v2/service/s3 v1.80.2 github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.4 github.com/blang/semver/v4 v4.0.0 github.com/bmatcuk/doublestar v1.3.4 @@ -269,18 +269,18 @@ require ( go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.38.0 + golang.org/x/crypto v0.39.0 golang.org/x/crypto/x509roots/fallback v0.0.0-20240208163226-62c9f1799c91 - golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b - golang.org/x/net v0.40.0 + golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 + golang.org/x/net v0.41.0 golang.org/x/oauth2 v0.30.0 - golang.org/x/sync v0.14.0 + golang.org/x/sync v0.15.0 golang.org/x/sys v0.33.0 - golang.org/x/text v0.25.0 + golang.org/x/text v0.26.0 golang.org/x/time v0.11.0 - golang.org/x/tools v0.33.0 + golang.org/x/tools v0.34.0 google.golang.org/api v0.230.0 - google.golang.org/grpc v1.72.2 + google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 @@ -458,16 +458,15 @@ require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270 // indirect github.com/aws/aws-msk-iam-sasl-signer-go v1.0.4 // indirect github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.69 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.77 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.35 // indirect github.com/aws/aws-sdk-go-v2/service/amp v1.26.1 // indirect github.com/aws/aws-sdk-go-v2/service/apigateway v1.24.1 // indirect github.com/aws/aws-sdk-go-v2/service/apigatewayv2 v1.21.1 // indirect @@ -478,16 +477,16 @@ require ( github.com/aws/aws-sdk-go-v2/service/ec2 v1.224.0 // indirect github.com/aws/aws-sdk-go-v2/service/iam v1.33.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.16 // indirect github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.22.1 // indirect github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.0 // indirect github.com/aws/aws-sdk-go-v2/service/shield v1.26.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 // indirect github.com/aws/aws-sdk-go-v2/service/storagegateway v1.30.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 // indirect github.com/aws/smithy-go v1.22.3 // indirect github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // indirect @@ -510,7 +509,7 @@ require ( github.com/cilium/ebpf v0.18.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect - github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect github.com/compose-spec/compose-go/v2 v2.6.2 // indirect github.com/containerd/cgroups/v3 v3.0.5 // indirect github.com/containerd/console v1.0.4 // indirect @@ -696,7 +695,7 @@ require ( github.com/josharian/native v1.1.0 // indirect github.com/joyent/triton-go v1.8.5 // indirect github.com/jpillora/backoff v1.0.0 // indirect - github.com/jsimonetti/rtnetlink v1.3.5 // indirect + github.com/jsimonetti/rtnetlink v1.4.2 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/karrick/godirwalk v1.17.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect @@ -874,7 +873,6 @@ require ( github.com/twmb/murmur3 v1.1.8 // indirect github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6 // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect - github.com/ulikunitz/xz v0.5.12 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/vertica/vertica-sql-go v1.3.3 // indirect github.com/vishvananda/netlink v1.3.1 // indirect @@ -934,7 +932,7 @@ require ( go.opentelemetry.io/contrib/detectors/aws/ec2 v1.28.0 // indirect go.opentelemetry.io/contrib/detectors/aws/eks v1.28.0 // indirect go.opentelemetry.io/contrib/detectors/azure/azurevm v0.0.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect @@ -954,8 +952,8 @@ require ( go.uber.org/mock v0.5.2 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.design/x/chann v0.1.2 // indirect - golang.org/x/arch v0.7.0 // indirect - golang.org/x/mod v0.24.0 // indirect + golang.org/x/arch v0.18.0 // indirect + golang.org/x/mod v0.25.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect @@ -998,17 +996,21 @@ require ( github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/containerd/containerd/v2 v2.0.5 // indirect + github.com/elastic/go-perf v0.0.0-20241029065020-30bec95324b8 // indirect github.com/foxboron/go-tpm-keyfiles v0.0.0-20250323135004-b31fac66206e // indirect github.com/google/go-tpm v0.9.5 // indirect github.com/grafana/faro/pkg/go v0.0.0-20250314155512-06a06da3b8bc // indirect github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect github.com/mariomac/guara v0.0.0-20250408105519-1e4dbdfb7136 // indirect + github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/moby/go-archive v0.1.0 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/extension/ackextension v0.128.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/splunk v0.128.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/faro v0.128.0 // indirect + github.com/peterbourgon/ff/v3 v3.4.0 // indirect github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect github.com/twmb/franz-go v1.18.1 // indirect @@ -1099,3 +1101,10 @@ replace github.com/prometheus/procfs => github.com/prometheus/procfs v0.12.0 // Use v0.62.0 of prometheus/common for all dependencies until mongodb_exporter is updated to support 0.63.0 replace github.com/prometheus/common => github.com/prometheus/common v0.62.0 + +require ( + github.com/elastic/go-freelru v0.16.0 + go.opentelemetry.io/ebpf-profiler v0.0.0-00010101000000-000000000000 +) + +replace go.opentelemetry.io/ebpf-profiler => github.com/grafana/opentelemetry-ebpf-profiler v0.0.0-20250624035245-5fc775dac6dc diff --git a/go.sum b/go.sum index b60cbd682b8..d50a7a44aa4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= -cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss= +cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -568,8 +568,6 @@ github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270 h1:JIxGEMs4E5Zb6R7z2C5IgecI0mkqS97WAEF31wUbYTM= -github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270/go.mod h1:2XtVRGCw/HthOLxU0Qw6o6jSJrcEoOb2OCCl8gQYvGw= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-msk-iam-sasl-signer-go v1.0.4 h1:2jAwFwA0Xgcx94dUId+K24yFabsKYDtAhCgyMit6OqE= github.com/aws/aws-msk-iam-sasl-signer-go v1.0.4/go.mod h1:MVYeeOhILFFemC/XlYTClvBjYZrg/EPd3ts885KrNTI= @@ -582,30 +580,30 @@ github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= -github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E= +github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= -github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= -github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= +github.com/aws/aws-sdk-go-v2/config v1.29.16 h1:XkruGnXX1nEZ+Nyo9v84TzsX+nj86icbFAeust6uo8A= +github.com/aws/aws-sdk-go-v2/config v1.29.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= -github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178= +github.com/aws/aws-sdk-go-v2/credentials v1.17.69/go.mod h1:gPME6I8grR1jCqBFEGthULiolzf/Sexq/Wy42ibKK9c= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.77 h1:xaRN9fags7iJznsMEjtcEuON1hGfCZ0y5MVfEMKtrx8= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.77/go.mod h1:lolsiGkT47AZ3DWqtxgEQM/wVMpayi7YWNjl3wHSRx8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35/go.mod h1:rZUQNYMNG+8uZxz9FOerQJ+FceCiodXvixpeRtdESrU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35/go.mod h1:FuA+nmgMRfkzVKYDNEqQadvEMxtxl9+RLT9ribCwEMs= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.35 h1:th/m+Q18CkajTw1iqx2cKkLCij/uz8NMwJFPK91p2ug= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.35/go.mod h1:dkJuf0a1Bc8HAA0Zm2MoTGm/WDC18Td9vSbrQ1+VqE8= github.com/aws/aws-sdk-go-v2/service/amp v1.26.1 h1:svGkgUKZDc5SNSiP6BgcOe/6sPmwBniltU6uHmxrjqo= github.com/aws/aws-sdk-go-v2/service/amp v1.26.1/go.mod h1:mBtHxQRTrzQB0G5oap7IcgP9Ny5p9BJSGhWnuQ+35EY= github.com/aws/aws-sdk-go-v2/service/apigateway v1.24.1 h1:20jy3+l7bQA6tJ1nJxg8+l+1Xm9KrDslNVvc8HvLoEI= @@ -627,17 +625,17 @@ github.com/aws/aws-sdk-go-v2/service/iam v1.33.1 h1:0dcMo3330L9LIckl+4iujMoq0AdR github.com/aws/aws-sdk-go-v2/service/iam v1.33.1/go.mod h1:sX/naR5tYtlGFN0Bjg9VPNgYNg/rqiDUuKTW9peFnZk= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 h1:BCG7DCXEXpNCcpwCxg1oi9pkJWH2+eZzTn9MY56MbVw= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.3 h1:VHPZakq2L7w+RLzV54LmQavbvheFaR2u1NomJRSEfcU= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.3/go.mod h1:DX1e/lkbsAt0MkY3NgLYuH4jQvRfw8MYxTe9feR7aXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 h1:/ldKrPPXTC421bTNWrUIpq3CxwHwRI/kpc+jPUTJocM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.16 h1:2HuI7vWKhFWsBhIr2Zq8KfFZT6xqaId2XXnXZjkbEuc= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.16/go.mod h1:BrwWnsfbFtFeRjdx0iM1ymvlqDX1Oz68JsQaibX/wG8= github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.22.1 h1:73im9DnuBD4+G8hHsbqb0NSA+n6QJ5ApFk6/YeOz8k8= github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.22.1/go.mod h1:p5FuKT8Rj4fnlT84Pzy7itV11NZ39Fwm/Y52S8Lg1Oc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.80.0 h1:fV4XIU5sn/x8gjRouoJpDVHj+ExJaUk4prYF+eb6qTs= -github.com/aws/aws-sdk-go-v2/service/s3 v1.80.0/go.mod h1:qbn305Je/IofWBJ4bJz/Q7pDEtnnoInw/dGt71v6rHE= +github.com/aws/aws-sdk-go-v2/service/s3 v1.80.2 h1:T6Wu+8E2LeTUqzqQ/Bh1EoFNj1u4jUyveMgmTlu9fDU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.80.2/go.mod h1:chSY8zfqmS0OnhZoO/hpPx/BHfAIL80m77HwhRLYScY= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.0 h1:64jRTsqBcIqlA4N7ZFYy+ysGPE7Rz/nJgU2fwv2cymk= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.0/go.mod h1:JsJDZFHwLGZu6dxhV9EV1gJrMnCeE4GEXubSZA59xdA= github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.4 h1:zZvziql5vgfDs2hTfF8fRF4pySG7A28/qNJQihJvpwc= @@ -645,15 +643,15 @@ github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.4/go.mod h1:IbC8X3WZ github.com/aws/aws-sdk-go-v2/service/shield v1.26.1 h1:vlqoPRFrhs/djRKnrPNJvzzVLIsMWITGgP4gHIzprSU= github.com/aws/aws-sdk-go-v2/service/shield v1.26.1/go.mod h1:1aUTOI7FTFp3ng7NH3C0UqDkbofoLb7NLcd/ufvlHdY= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 h1:EU58LP8ozQDVroOEyAfcq0cGc5R/FTZjVoYJ6tvby3w= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 h1:XB4z0hbQtpmBnb1FQYvKaCM7UsS6Y/u8jVBwIUGeCTk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg= github.com/aws/aws-sdk-go-v2/service/storagegateway v1.30.1 h1:/teUr5AA4/AUaw8A1wF6wcki4oc//lxonloUq1bl1VU= github.com/aws/aws-sdk-go-v2/service/storagegateway v1.30.1/go.mod h1:LigoGatDhnWionzCxyHIQ96cQhwmLgTEkQDOzZg1Q3E= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 h1:nyLjs8sYJShFYj6aiyjCBI3EcLn1udWrQTjEF+SOXB0= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.21/go.mod h1:EhdxtZ+g84MSGrSrHzZiUm9PYiZkrADNja15wtRJSJo= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= @@ -772,8 +770,8 @@ github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -976,8 +974,12 @@ github.com/efficientgo/core v1.0.0-rc.3 h1:X6CdgycYWDcbYiJr1H1+lQGzx13o7bq3EUkbB github.com/efficientgo/core v1.0.0-rc.3/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= +github.com/elastic/go-freelru v0.16.0 h1:gG2HJ1WXN2tNl5/p40JS/l59HjvjRhjyAa+oFTRArYs= +github.com/elastic/go-freelru v0.16.0/go.mod h1:bSdWT4M0lW79K8QbX6XY2heQYSCqD7THoYf82pT/H3I= github.com/elastic/go-grok v0.3.1 h1:WEhUxe2KrwycMnlvMimJXvzRa7DoByJB4PVUIE1ZD/U= github.com/elastic/go-grok v0.3.1/go.mod h1:n38ls8ZgOboZRgKcjMY8eFeZFMmcL9n2lP0iHhIDk64= +github.com/elastic/go-perf v0.0.0-20241029065020-30bec95324b8 h1:FD01NjsTes0RxZVQ22ebNYJA4KDdInVnR9cn1hmaMwA= +github.com/elastic/go-perf v0.0.0-20241029065020-30bec95324b8/go.mod h1:Nt+pnRYvf0POC+7pXsrv8ubsEOSsaipJP0zlz1Ms1RM= github.com/elastic/go-sysinfo v1.8.1 h1:4Yhj+HdV6WjbCRgGdZpPJ8lZQlXZLKDAeIkmQ/VRvi4= github.com/elastic/go-sysinfo v1.8.1/go.mod h1:JfllUnzoQV/JRYymbH3dO1yggI3mV2oTKSXsDHM+uIM= github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= @@ -1427,6 +1429,8 @@ github.com/grafana/node_exporter v0.18.1-grafana-r01.0.20250218170810-6300fab821 github.com/grafana/node_exporter v0.18.1-grafana-r01.0.20250218170810-6300fab82195/go.mod h1:onpUlSx7BWY8aO+tr3DChzBJB06pZfZ4gltVtsRXLFA= github.com/grafana/opentelemetry-collector/featuregate v0.0.0-20240325174506-2fd1623b2ca0 h1:i/Ne0XwoRokYj52ZcSmnvuyID3h/uA91n0Ycg/grHU8= github.com/grafana/opentelemetry-collector/featuregate v0.0.0-20240325174506-2fd1623b2ca0/go.mod h1:mm8+xyQfgDmqhyegZRNIQmoKsNnDTwWKFLsdMoXAb7A= +github.com/grafana/opentelemetry-ebpf-profiler v0.0.0-20250624035245-5fc775dac6dc h1:Qv8+wIvDB5c1liVKAcTNxgKTf/gEQSyPEAiVZXrSLR8= +github.com/grafana/opentelemetry-ebpf-profiler v0.0.0-20250624035245-5fc775dac6dc/go.mod h1:W40JSh7omwYanKBorFhknIBujk//sNY65rDfXqG++/4= github.com/grafana/postgres_exporter v0.15.1-0.20250312140329-3046b223bba0 h1:UdXc6g+G/JsDGvh5TpJ5siGskJt9P7IdUiG5pvcw15g= github.com/grafana/postgres_exporter v0.15.1-0.20250312140329-3046b223bba0/go.mod h1:ws2UAbpWWvs87mYgclemWvosRQjIp6fuOxDjLzfo7L0= github.com/grafana/prometheus v1.8.2-0.20250410112059-2c3eb5bff000 h1:zxMLTFyjhG0YyP2TZCm68MOCER/PX6uuI7e29i63Vcg= @@ -1808,10 +1812,10 @@ github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2E github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA= -github.com/jsimonetti/rtnetlink v1.3.5/go.mod h1:0LFedyiTkebnd43tE4YAkWGIq9jQphow4CcwxaT2Y00= -github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= -github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= +github.com/jsimonetti/rtnetlink v1.4.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5GTEn90= +github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM= +github.com/jsimonetti/rtnetlink/v2 v2.0.3 h1:Jcp7GTnTPepoUAJ9+LhTa7ZiebvNS56T1GtlEUaPNFE= +github.com/jsimonetti/rtnetlink/v2 v2.0.3/go.mod h1:atIkksp/9fqtf6rpAw45JnttnP2gtuH9X88WPfWfS9A= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -1980,6 +1984,8 @@ github.com/mdlayher/ethtool v0.1.0/go.mod h1:fBMLn2UhfRGtcH5ZFjr+6GUiHEjZsItFD7f github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= +github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d h1:JmrZTpS0GAyMV4ZQVVH/AS0Y6r2PbnYNSRUuRX+HOLA= +github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d/go.mod h1:+SexPO1ZvdbbWUdUnyXEWv3+4NwHZjKhxOmQqHY4Pqc= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= @@ -2014,6 +2020,8 @@ github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8D github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o= github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -2435,6 +2443,8 @@ github.com/percona/percona-backup-mongodb v1.8.1-0.20241022111827-8d3ad8a6eb7a h github.com/percona/percona-backup-mongodb v1.8.1-0.20241022111827-8d3ad8a6eb7a/go.mod h1:dX4OBSAS5kw1VAwm/uYy5IavVrkbh9ncwUVLVKrKT98= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= @@ -2851,8 +2861,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= -github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= @@ -3147,8 +3155,8 @@ go.opentelemetry.io/contrib/detectors/aws/eks v1.28.0 h1:446exEE2fdXqNttY+oHB54N go.opentelemetry.io/contrib/detectors/aws/eks v1.28.0/go.mod h1:L2UOyaTFvypCAHW69TwGnce7YgKhSI9n8kAzQjyNbJk= go.opentelemetry.io/contrib/detectors/azure/azurevm v0.0.1 h1:hgKJ88bL5cTomIlP7VYxsG4HfYeRp3U2tfcpZr0B0yY= go.opentelemetry.io/contrib/detectors/azure/azurevm v0.0.1/go.mod h1:nmQyKaRlFYOboU/JGyRb1/9eLpuKYmTFd+rDjrIZTKk= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.45.0 h1:CaagQrotQLgtDlHU6u9pE/Mf4mAwiLD8wrReIVt06lY= go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.45.0/go.mod h1:LOjFy00/ZMyMYfKFPta6kZe2cDUc1sNo/qtv1pSORWA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= @@ -3249,8 +3257,8 @@ go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fR go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= golang.design/x/chann v0.1.2 h1:eHF9wjuQnpp+j4ryWhyxC/pFuYzbvMAkudA/I5ALovY= golang.design/x/chann v0.1.2/go.mod h1:Rh5KhCAp+0qu9+FfKPymHpu8onmjl89sFwMeiw3SK14= -golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= -golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc= +golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -3292,8 +3300,8 @@ golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto/x509roots/fallback v0.0.0-20240208163226-62c9f1799c91 h1:Lyizcy9jX02jYR0ceBkL6S+jRys8Uepf7wt1vrz6Ras= golang.org/x/crypto/x509roots/fallback v0.0.0-20240208163226-62c9f1799c91/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -3309,8 +3317,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA= -golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -3342,8 +3350,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -3409,8 +3417,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -3437,8 +3445,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3588,8 +3596,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3658,8 +3666,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3763,8 +3771,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= -google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/internal/component/pyroscope/ebpf/args.go b/internal/component/pyroscope/ebpf/args.go index 2b885c34f0a..cc3de92895e 100644 --- a/internal/component/pyroscope/ebpf/args.go +++ b/internal/component/pyroscope/ebpf/args.go @@ -1,7 +1,6 @@ package ebpf import ( - "errors" "time" "github.com/grafana/alloy/internal/component/discovery" @@ -13,28 +12,41 @@ type Arguments struct { Targets []discovery.Target `alloy:"targets,attr,optional"` CollectInterval time.Duration `alloy:"collect_interval,attr,optional"` SampleRate int `alloy:"sample_rate,attr,optional"` - PidCacheSize int `alloy:"pid_cache_size,attr,optional"` - BuildIDCacheSize int `alloy:"build_id_cache_size,attr,optional"` - SameFileCacheSize int `alloy:"same_file_cache_size,attr,optional"` - ContainerIDCacheSize int `alloy:"container_id_cache_size,attr,optional"` - CacheRounds int `alloy:"cache_rounds,attr,optional"` - CollectUserProfile bool `alloy:"collect_user_profile,attr,optional"` - CollectKernelProfile bool `alloy:"collect_kernel_profile,attr,optional"` - Demangle string `alloy:"demangle,attr,optional"` - GoTableFallback bool `alloy:"go_table_fallback,attr,optional"` PythonEnabled bool `alloy:"python_enabled,attr,optional"` - SymbolsMapSize int `alloy:"symbols_map_size,attr,optional"` - PIDMapSize int `alloy:"pid_map_size,attr,optional"` + PerlEnabled bool `alloy:"perl_enabled,attr,optional"` + PHPEnabled bool `alloy:"php_enabled,attr,optional"` + HotspotEnabled bool `alloy:"hotspot_enabled,attr,optional"` + RubyEnabled bool `alloy:"ruby_enabled,attr,optional"` + V8Enabled bool `alloy:"v8_enabled,attr,optional"` + DotNetEnabled bool `alloy:"dotnet_enabled,attr,optional"` + GoEnabled bool `alloy:"go_enabled,attr,optional"` + Demangle string `alloy:"demangle,attr,optional"` + ContainerIDCacheSize uint32 `alloy:"container_id_cache_size,attr,optional"` + DeprecatedArguments DeprecatedArguments `alloy:",squash"` +} + +type DeprecatedArguments struct { + // deprecated + PidCacheSize int `alloy:"pid_cache_size,attr,optional"` + // deprecated + BuildIDCacheSize int `alloy:"build_id_cache_size,attr,optional"` + // deprecated + SameFileCacheSize int `alloy:"same_file_cache_size,attr,optional"` + // deprecated + CacheRounds int `alloy:"cache_rounds,attr,optional"` + // deprecated + GoTableFallback bool `alloy:"go_table_fallback,attr,optional"` + // deprecated + SymbolsMapSize int `alloy:"symbols_map_size,attr,optional"` + // deprecated + PIDMapSize int `alloy:"pid_map_size,attr,optional"` + // deprecated + CollectUserProfile bool `alloy:"collect_user_profile,attr,optional"` + // deprecated + CollectKernelProfile bool `alloy:"collect_kernel_profile,attr,optional"` } // Validate implements syntax.Validator. -func (arg *Arguments) Validate() error { - var errs []error - if arg.SymbolsMapSize <= 0 { - errs = append(errs, errors.New("symbols_map_size must be greater than 0")) - } - if arg.PIDMapSize <= 0 { - errs = append(errs, errors.New("pid_map_size must be greater than 0")) - } - return errors.Join(errs...) +func (args *Arguments) Validate() error { + return nil } diff --git a/internal/component/pyroscope/ebpf/ebpf_linux.go b/internal/component/pyroscope/ebpf/ebpf_linux.go index 9224c21b1c9..86b8c95b5b2 100644 --- a/internal/component/pyroscope/ebpf/ebpf_linux.go +++ b/internal/component/pyroscope/ebpf/ebpf_linux.go @@ -1,24 +1,30 @@ -//go:build (linux && arm64) || (linux && amd64) +//go:build linux && (arm64 || amd64) && pyroscope_ebpf package ebpf import ( "context" - "fmt" "os" + "path/filepath" + "strings" "sync" + "syscall" "time" - ebpfspy "github.com/grafana/pyroscope/ebpf" - demangle2 "github.com/grafana/pyroscope/ebpf/cpp/demangle" - "github.com/grafana/pyroscope/ebpf/pprof" - "github.com/grafana/pyroscope/ebpf/sd" - "github.com/grafana/pyroscope/ebpf/symtab" - + "github.com/go-kit/log/level" "github.com/grafana/alloy/internal/component" "github.com/grafana/alloy/internal/component/pyroscope" + "github.com/grafana/alloy/internal/component/pyroscope/ebpf/reporter" "github.com/grafana/alloy/internal/featuregate" - "github.com/grafana/alloy/internal/runtime/logging/level" + "github.com/oklog/run" + "github.com/sirupsen/logrus" + "go.opentelemetry.io/ebpf-profiler/interpreter/python" + discovery2 "go.opentelemetry.io/ebpf-profiler/pyroscope/discovery" + "go.opentelemetry.io/ebpf-profiler/pyroscope/dynamicprofiling" + "go.opentelemetry.io/ebpf-profiler/pyroscope/internalshim/controller" + "go.opentelemetry.io/ebpf-profiler/pyroscope/symb/irsymcache" + "go.opentelemetry.io/ebpf-profiler/pyroscope/symb/table" + "go.opentelemetry.io/ebpf-profiler/reporter/samples" ) func init() { @@ -32,168 +38,143 @@ func init() { return New(opts, arguments) }, }) + python.NoContinueWithNextUnwinder.Store(true) } func New(opts component.Options, args Arguments) (component.Component, error) { - targetFinder, err := sd.NewTargetFinder(os.DirFS("/"), opts.Logger, targetsOptionFromArgs(args)) + cfg, err := args.Convert() + if err != nil { + return nil, err + } + cgroups, err := reporter.NewContainerIDCache(args.ContainerIDCacheSize) if err != nil { - return nil, fmt.Errorf("ebpf target finder create: %w", err) + return nil, err } + dynamicProfilingPolicy := cfg.PyroscopeDynamicProfilingPolicy + discovery := discovery2.NewTargetProducer(cgroups, args.targetsOptions(dynamicProfilingPolicy)) ms := newMetrics(opts.Registerer) - session, err := ebpfspy.NewSession( - opts.Logger, - targetFinder, - convertSessionOptions(args, ms), - ) - if err != nil { - return nil, fmt.Errorf("ebpf session create: %w", err) + appendable := pyroscope.NewFanout(args.ForwardTo, opts.ID, opts.Registerer) + + var nfs samples.NativeSymbolResolver + if cfg.SymbolizeNativeFrames { + tf := irsymcache.TableTableFactory{ + Options: []table.Option{ + table.WithFiles(), + table.WithLines(), + }, + } + nfs, err = irsymcache.NewFSCache(tf, irsymcache.Options{ + SizeEntries: uint32(cfg.SymbCacheSizeEntries), + Path: cfg.SymbCachePath, + }) + if err != nil { + return nil, err + } } + cfg.FileObserver = nfs - alloyAppendable := pyroscope.NewFanout(args.ForwardTo, opts.ID, opts.Registerer) + if dynamicProfilingPolicy { + cfg.Policy = &dynamicprofiling.ServiceDiscoveryTargetsOnlyPolicy{Discovery: discovery} + } else { + cfg.Policy = dynamicprofiling.AlwaysOnPolicy{} + } res := &Component{ - options: opts, - metrics: ms, - appendable: alloyAppendable, - args: args, - targetFinder: targetFinder, - session: session, - argsUpdate: make(chan Arguments, 4), + cfg: cfg, + options: opts, + metrics: ms, + appendable: appendable, + args: args, + targetFinder: discovery, + dynamicProfilingPolicy: dynamicProfilingPolicy, + argsUpdate: make(chan Arguments, 4), } - res.metrics.targetsActive.Set(float64(len(res.targetFinder.DebugInfo()))) - return res, nil -} - -var DefaultArguments = NewDefaultArguments() -// NewDefaultArguments create the default settings for a scrape job. -func NewDefaultArguments() Arguments { - return Arguments{ - CollectInterval: 15 * time.Second, - SampleRate: 97, - PidCacheSize: 32, - ContainerIDCacheSize: 1024, - BuildIDCacheSize: 64, - SameFileCacheSize: 8, - CacheRounds: 3, - CollectUserProfile: true, - CollectKernelProfile: true, - Demangle: "none", - PythonEnabled: true, - SymbolsMapSize: 2048, - PIDMapSize: 16384, + cfg.Reporter, err = reporter.New(opts.Logger, cgroups, cfg, discovery, nfs, reporter.PPROFConsumerFunc(func(ctx context.Context, ps []reporter.PPROF) { + res.sendProfiles(ctx, ps) + })) + if err != nil { + return nil, err + } + if cfg.VerboseMode { + logrus.SetLevel(logrus.DebugLevel) } -} -// SetToDefault implements syntax.Defaulter. -func (arg *Arguments) SetToDefault() { - *arg = NewDefaultArguments() + return res, nil } type Component struct { - options component.Options - args Arguments - argsUpdate chan Arguments - appendable *pyroscope.Fanout - targetFinder sd.TargetFinder - session ebpfspy.Session + options component.Options + args Arguments + dynamicProfilingPolicy bool + argsUpdate chan Arguments + appendable *pyroscope.Fanout + targetFinder discovery2.TargetProducer - debugInfo DebugInfo - debugInfoLock sync.Mutex - metrics *metrics + metrics *metrics + cfg *controller.Config healthMut sync.RWMutex health component.Health } func (c *Component) Run(ctx context.Context) error { - var ( - sessionStarted = false - sessionErrors = 0 - sessionMaxErrors = 3 - ) - - collectInterval := c.args.CollectInterval - t := time.NewTicker(collectInterval) - defer t.Stop() - for { - select { - case <-ctx.Done(): - return nil - case newArgs := <-c.argsUpdate: - // ensure there are no other updates queued. this might happen if the collection takes a very long time - newArgs = getLatestArgsFromChannel(c.argsUpdate, newArgs) - - // update targets - c.args = newArgs - c.session.UpdateTargets(targetsOptionFromArgs(c.args)) - c.metrics.targetsActive.Set(float64(len(c.targetFinder.DebugInfo()))) - err := c.session.Update(convertSessionOptions(c.args, c.metrics)) - if err != nil { - level.Error(c.options.Logger).Log("msg", "failed to update profiling session", "err", err) - c.reportUnhealthy(err) - continue - } - c.appendable.UpdateChildren(newArgs.ForwardTo) - if c.args.CollectInterval != collectInterval { - t.Reset(c.args.CollectInterval) - collectInterval = c.args.CollectInterval - } - case <-t.C: - if !sessionStarted { - err := c.session.Start() - if err != nil { - sessionErrors++ - if sessionErrors > sessionMaxErrors { - level.Error(c.options.Logger).Log("msg", "too many errors starting profiling session, giving up", "tries", sessionErrors, "last_error", err) - t.Stop() - continue - } - level.Error(c.options.Logger).Log("msg", "failed to start profiling session", "err", err) - c.reportUnhealthy(err) - continue - } - sessionErrors = 0 - defer func() { - c.session.Stop() - level.Info(c.options.Logger).Log("msg", "ebpf profiling session stopped") - }() - sessionStarted = true - level.Info(c.options.Logger).Log("msg", "ebpf profiling session started") - } - err := c.collectProfiles(ctx) - if err != nil { - level.Error(c.options.Logger).Log("msg", "failed to collect profiles", "err", err) - c.reportUnhealthy(err) - c.metrics.profilingSessionsFailingTotal.Inc() - continue - } - c.reportHealthy() - c.updateDebugInfo() + c.checkTraceFS() + ctlr := controller.New(c.cfg) + const sessionMaxErrors = 3 + var err error + for i := 0; i < sessionMaxErrors; i++ { + err = ctlr.Start(ctx) + if err != nil { + c.reportUnhealthy(err) + time.Sleep(c.cfg.ReporterInterval) + continue } + break } -} - -func getLatestArgsFromChannel[A any](ch chan A, current A) A { - for { - select { - case x := <-ch: - current = x - default: - return current - } + if err != nil { + return err } + c.reportHealthy() + defer func() { + ctlr.Shutdown() + if c.cfg.FileObserver != nil { + c.cfg.FileObserver.Cleanup() + } + }() + + var g run.Group + g.Add(func() error { + for { + select { + case <-ctx.Done(): + return nil + case newArgs := <-c.argsUpdate: + c.args = newArgs + c.targetFinder.Update(c.args.targetsOptions(c.dynamicProfilingPolicy)) + c.appendable.UpdateChildren(newArgs.ForwardTo) + } + } + }, func(error) {}) + return g.Run() } func (c *Component) Update(args component.Arguments) error { newArgs := args.(Arguments) - c.argsUpdate <- newArgs + select { + case c.argsUpdate <- newArgs: + default: + _ = level.Debug(c.options.Logger).Log("msg", "dropped args update") + } return nil } func (c *Component) reportUnhealthy(err error) { + _ = level.Error(c.options.Logger). + Log("msg", "unhealthy", "err", err) + c.healthMut.Lock() defer c.healthMut.Unlock() c.health = component.Health{ @@ -218,85 +199,108 @@ func (c *Component) CurrentHealth() component.Health { return c.health } -func (c *Component) DebugInfo() interface{} { - c.debugInfoLock.Lock() - defer c.debugInfoLock.Unlock() - return c.debugInfo +func (c *Component) checkTraceFS() { + candidates := []string{ + "/sys/kernel/tracing", + "/sys/kernel/debug/tracing", + } + for _, p := range candidates { + _, err := os.Stat(filepath.Join(p, "events")) + if err != nil { + continue + } + level.Debug(c.options.Logger).Log("msg", "found tracefs at "+p) + return + } + mountPath := candidates[0] + err := syscall.Mount("tracefs", mountPath, "tracefs", 0, "") + if err != nil { + level.Error(c.options.Logger).Log("msg", "failed to mount tracefs at "+mountPath, "err", err) + } else { + level.Debug(c.options.Logger).Log("msg", "mounted tracefs at "+mountPath) + } } -func (c *Component) collectProfiles(ctx context.Context) error { - c.metrics.profilingSessionsTotal.Inc() - level.Debug(c.options.Logger).Log("msg", "ebpf collectProfiles") - args := c.args - builders := pprof.NewProfileBuilders(pprof.BuildersOptions{ - SampleRate: int64(args.SampleRate), - PerPIDProfile: true, - }) - err := pprof.Collect(builders, c.session) - - if err != nil { - return fmt.Errorf("ebpf session collectProfiles %w", err) +// NewDefaultArguments create the default settings for a scrape job. +func NewDefaultArguments() Arguments { + return Arguments{ + CollectInterval: 15 * time.Second, + SampleRate: 19, + ContainerIDCacheSize: 1024, + Demangle: "none", + PythonEnabled: true, + PerlEnabled: true, + PHPEnabled: true, + HotspotEnabled: true, + RubyEnabled: true, + V8Enabled: true, + DotNetEnabled: true, + GoEnabled: false, } - level.Debug(c.options.Logger).Log("msg", "ebpf collectProfiles done", "profiles", len(builders.Builders)) - c.sendProfiles(ctx, builders) - return nil } -type DebugInfo struct { - Targets interface{} `alloy:"targets,attr,optional"` - Session interface{} `alloy:"session,attr,optional"` +// SetToDefault implements syntax.Defaulter. +func (args *Arguments) SetToDefault() { + *args = NewDefaultArguments() } -func (c *Component) updateDebugInfo() { - c.debugInfoLock.Lock() - defer c.debugInfoLock.Unlock() +func (args *Arguments) Convert() (*controller.Config, error) { + cfgProtoType, err := controller.ParseArgs() + if err != nil { + return nil, err + } - c.debugInfo = DebugInfo{ - Targets: c.targetFinder.DebugInfo(), - Session: c.session.DebugInfo(), + if err = cfgProtoType.Validate(); err != nil { + return nil, err } + + cfg := new(controller.Config) + *cfg = *cfgProtoType + cfg.ReporterInterval = args.CollectInterval + cfg.SamplesPerSecond = args.SampleRate + cfg.Tracers = args.tracers() + return cfg, nil } -func targetsOptionFromArgs(args Arguments) sd.TargetsOptions { - targets := make([]sd.DiscoveryTarget, 0, len(args.Targets)) - for _, t := range args.Targets { - targets = append(targets, t.AsMap()) +func (args *Arguments) tracers() string { + var tracers []string + if args.PythonEnabled { + tracers = append(tracers, "python") + } + if args.PerlEnabled { + tracers = append(tracers, "perl") + } + if args.PHPEnabled { + tracers = append(tracers, "php") + } + if args.HotspotEnabled { + tracers = append(tracers, "hotspot") + } + if args.V8Enabled { + tracers = append(tracers, "v8") + } + if args.RubyEnabled { + tracers = append(tracers, "ruby") } - return sd.TargetsOptions{ - Targets: targets, - TargetsOnly: true, - ContainerCacheSize: args.ContainerIDCacheSize, + if args.DotNetEnabled { + tracers = append(tracers, "dotnet") } + if args.GoEnabled { + tracers = append(tracers, "go") + } + return strings.Join(tracers, ",") } -func convertSessionOptions(args Arguments, ms *metrics) ebpfspy.SessionOptions { - return ebpfspy.SessionOptions{ - CollectUser: args.CollectUserProfile, - CollectKernel: args.CollectKernelProfile, - SampleRate: args.SampleRate, - PythonEnabled: args.PythonEnabled, - Metrics: ms.ebpfMetrics, - SymbolOptions: symtab.SymbolOptions{ - GoTableFallback: args.GoTableFallback, - DemangleOptions: demangle2.ConvertDemangleOptions(args.Demangle), - }, - CacheOptions: symtab.CacheOptions{ - PidCacheOptions: symtab.GCacheOptions{ - Size: args.PidCacheSize, - KeepRounds: args.CacheRounds, - }, - BuildIDCacheOptions: symtab.GCacheOptions{ - Size: args.BuildIDCacheSize, - KeepRounds: args.CacheRounds, - }, - SameFileCacheOptions: symtab.GCacheOptions{ - Size: args.SameFileCacheSize, - KeepRounds: args.CacheRounds, - }, - }, - BPFMapsOptions: ebpfspy.BPFMapsOptions{ - SymbolsMapSize: uint32(args.SymbolsMapSize), - PIDMapSize: uint32(args.PIDMapSize), +func (args *Arguments) targetsOptions(dynamicProfilingPolicy bool) discovery2.TargetsOptions { + targets := make([]discovery2.DiscoveredTarget, 0, len(args.Targets)) + for _, t := range args.Targets { + targets = append(targets, t.AsMap()) + } + return discovery2.TargetsOptions{ + Targets: targets, + TargetsOnly: dynamicProfilingPolicy, + DefaultTarget: discovery2.DiscoveredTarget{ + "service_name": "ebpf/unspecified", }, } } diff --git a/internal/component/pyroscope/ebpf/ebpf_linux_test.go b/internal/component/pyroscope/ebpf/ebpf_linux_test.go index 70dba51c957..17f0d551d9f 100644 --- a/internal/component/pyroscope/ebpf/ebpf_linux_test.go +++ b/internal/component/pyroscope/ebpf/ebpf_linux_test.go @@ -1,275 +1,18 @@ -//go:build (linux && arm64) || (linux && amd64) +//go:build ((linux && arm64) || (linux && amd64)) && pyroscope_ebpf package ebpf import ( - "context" - "fmt" - "os" - "sync" "testing" "time" - ebpfspy "github.com/grafana/pyroscope/ebpf" - "github.com/grafana/pyroscope/ebpf/pprof" - "github.com/grafana/pyroscope/ebpf/sd" - "github.com/grafana/pyroscope/ebpf/symtab" - "github.com/grafana/pyroscope/ebpf/symtab/elf" - "github.com/oklog/run" - "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - "github.com/grafana/alloy/internal/component" "github.com/grafana/alloy/internal/component/discovery" "github.com/grafana/alloy/internal/component/pyroscope" - "github.com/grafana/alloy/internal/util" "github.com/grafana/alloy/syntax" ) -type mockSession struct { - options ebpfspy.SessionOptions - collectCallback func() error - collected int - data [][]string - dataTarget *sd.Target - mtx sync.Mutex -} - -func (m *mockSession) Start() error { - return nil -} - -func (m *mockSession) Stop() { - -} - -func (m *mockSession) Update(options ebpfspy.SessionOptions) error { - m.mtx.Lock() - defer m.mtx.Unlock() - m.options = options - return nil -} - -func (m *mockSession) UpdateTargets(_ sd.TargetsOptions) { - -} - -func (m *mockSession) CollectProfiles(f pprof.CollectProfilesCallback) error { - m.collected++ - if m.collectCallback != nil { - return m.collectCallback() - } - for _, stack := range m.data { - f( - pprof.ProfileSample{ - Target: m.dataTarget, - Pid: 0, - SampleType: pprof.SampleTypeCpu, - Aggregation: pprof.SampleAggregation(false), - Stack: stack, - Value: 1, - Value2: 0, - }) - } - return nil -} - -func (m *mockSession) DebugInfo() interface{} { - return ebpfspy.SessionDebugInfo{ - ElfCache: symtab.ElfCacheDebugInfo{ - BuildIDCache: symtab.GCacheDebugInfo[elf.SymTabDebugInfo]{}, - SameFileCache: symtab.GCacheDebugInfo[elf.SymTabDebugInfo]{ - LRUSize: 10, - RoundSize: 10, - CurrentRound: 1, - LRUDump: []elf.SymTabDebugInfo{ - { - Name: "X", - Size: 123, - MiniDebugInfo: false, - LastUsedRound: 1, - }, - }, - }, - }, - PidCache: symtab.GCacheDebugInfo[symtab.ProcTableDebugInfo]{ - LRUSize: 10, - RoundSize: 10, - CurrentRound: 1, - LRUDump: []symtab.ProcTableDebugInfo{ - { - Pid: 666, - Size: 123, - }, - }, - }, - Arch: "my-arch", - Kernel: "my-kernel", - } -} - -func TestTargetUpdatesWithLongCollection(t *testing.T) { - logger := util.TestAlloyLogger(t) - ms := newMetrics(nil) - targetFinder, err := sd.NewTargetFinder(os.DirFS("/foo"), logger, sd.TargetsOptions{ - ContainerCacheSize: 1024, - }) - require.NoError(t, err) - session := &mockSession{} - arguments := NewDefaultArguments() - arguments.CollectInterval = time.Millisecond * 100 - c := newTestComponent( - component.Options{ - Logger: logger, - Registerer: prometheus.NewRegistry(), - OnStateChange: func(e component.Exports) {}, - }, - arguments, - session, - targetFinder, - ms, - ) - - collection := make(chan struct{}) - - // simulate a long collection - session.collectCallback = func() error { - <-collection - return nil - } - - var wg sync.WaitGroup - wg.Add(1) - ctx, cancel := context.WithCancel(t.Context()) - go func() { - err = c.Run(ctx) - require.NoError(t, err) - wg.Done() - }() - - // now schedule 3 updates - c.Update(arguments) - c.Update(arguments) - c.Update(arguments) - argX := NewDefaultArguments() - argX.SampleRate = 1234 - c.Update(argX) - - // unblock the collection - close(collection) - - // wait for the session to be updated - require.Eventually(t, func() bool { - session.mtx.Lock() - defer session.mtx.Unlock() - return session.options.SampleRate == 1234 - }, time.Second*1, time.Millisecond*10) - - cancel() - wg.Wait() -} - -func TestReportingCollectError(t *testing.T) { - logger := util.TestAlloyLogger(t) - ms := newMetrics(nil) - targetFinder, err := sd.NewTargetFinder(os.DirFS("/foo"), logger, sd.TargetsOptions{ - ContainerCacheSize: 1024, - }) - require.NoError(t, err) - session := &mockSession{} - arguments := NewDefaultArguments() - arguments.CollectInterval = time.Millisecond * 100 - c := newTestComponent( - component.Options{ - Logger: logger, - Registerer: prometheus.NewRegistry(), - OnStateChange: func(e component.Exports) {}, - }, - arguments, - session, - targetFinder, - ms, - ) - - session.collectCallback = func() error { return fmt.Errorf("mocked error collecting profiles") } - var wg sync.WaitGroup - wg.Add(1) - ctx, cancel := context.WithCancel(t.Context()) - go func() { - err = c.Run(ctx) - require.NoError(t, err) - wg.Done() - }() - - // expect the component to be unhealthy - require.Eventually(t, func() bool { - if c.CurrentHealth().Health == component.HealthTypeUnhealthy { - require.Equal(t, c.CurrentHealth().Message, "ebpf session collectProfiles mocked error collecting profiles") - return true - } - return false - }, time.Second*1, time.Millisecond*10) - - // the component should still be handling update requests - err = c.Update(arguments) - require.NoError(t, err) - - cancel() - wg.Wait() -} - -func TestContextShutdown(t *testing.T) { - logger := util.TestAlloyLogger(t) - ms := newMetrics(nil) - targetFinder, err := sd.NewTargetFinder(os.DirFS("/foo"), logger, sd.TargetsOptions{ - ContainerCacheSize: 1024, - }) - require.NoError(t, err) - session := &mockSession{} - arguments := NewDefaultArguments() - arguments.CollectInterval = time.Millisecond * 100 - c := newTestComponent( - component.Options{ - Logger: logger, - Registerer: prometheus.NewRegistry(), - OnStateChange: func(e component.Exports) {}, - }, - arguments, - session, - targetFinder, - ms, - ) - - session.data = [][]string{ - {"a", "b", "c"}, - {"q", "w", "e"}, - } - session.dataTarget = sd.NewTarget("cid", 0, map[string]string{"service_name": "foo"}) - var g run.Group - ctx, cancel := context.WithDeadline(t.Context(), time.Now().Add(time.Second*1)) - defer cancel() - g.Add(func() error { - err = c.Run(ctx) - require.NoError(t, err) - return nil - }, func(err error) { - - }) - g.Add(func() error { - time.Sleep(time.Millisecond * 300) - arguments.SampleRate = 4242 - err := c.Update(arguments) - require.NoError(t, err) - return nil - }, func(err error) { - - }) - err = g.Run() - require.NoError(t, err) - require.Greater(t, session.collected, 5) - require.Equal(t, session.options.SampleRate, 4242) -} - func TestUnmarshalConfig(t *testing.T) { for _, tt := range []struct { name string @@ -320,13 +63,13 @@ collect_kernel_profile = false`, x.ForwardTo = []pyroscope.Appendable{} x.CollectInterval = time.Second * 3 x.SampleRate = 239 - x.PidCacheSize = 1000 - x.BuildIDCacheSize = 2000 - x.SameFileCacheSize = 3000 - x.ContainerIDCacheSize = 4000 - x.CacheRounds = 4 x.CollectUserProfile = true x.CollectKernelProfile = false + x.ContainerIDCacheSize = 4000 + x.DeprecatedArguments.PidCacheSize = 1000 + x.DeprecatedArguments.SameFileCacheSize = 3000 + x.DeprecatedArguments.BuildIDCacheSize = 2000 + x.DeprecatedArguments.CacheRounds = 4 return x }, }, @@ -339,16 +82,6 @@ collect_interval = 3s" `, expectedErr: "4:21: expected TERMINATOR, got IDENT (and 1 more diagnostics)", }, - { - name: "incorrect-map-sizes", - in: ` -targets = [{"service_name" = "foo", "container_id"= "cid"}] -forward_to = [] -symbols_map_size = -1 -pid_map_size = 0 -`, - expectedErr: "symbols_map_size must be greater than 0\npid_map_size must be greater than 0", - }, } { t.Run(tt.name, func(t *testing.T) { arg := Arguments{} @@ -363,90 +96,3 @@ pid_map_size = 0 }) } } - -type mockTargetFinder struct { - sd.TargetFinder -} - -func (m *mockTargetFinder) DebugInfo() []map[string]string { - return []map[string]string{ - {"__container_id__": "foo", "__name__": "process_cpu", "container": "kube-proxy"}, - {"__container_id__": "baz", "__name__": "process_cpu", "container": "kube-proxy"}, - } -} - -func TestDebugInfo(t *testing.T) { - c := &Component{ - session: &mockSession{}, - targetFinder: &mockTargetFinder{}, - } - - c.updateDebugInfo() - di := c.DebugInfo() - - v, err := syntax.Marshal(di) - require.NoError(t, err) - - require.Equal(t, `targets = [{ - __container_id__ = "foo", - __name__ = "process_cpu", - container = "kube-proxy", -}, { - __container_id__ = "baz", - __name__ = "process_cpu", - container = "kube-proxy", -}] -session = { - elf_cache = { - build_id_cache = { - lru_size = 0, - round_size = 0, - current_round = 0, - lru_dump = [], - round_dump = [], - }, - same_file_cache = { - lru_size = 10, - round_size = 10, - current_round = 1, - lru_dump = [{ - name = "X", - symbol_count = 123, - file = "", - mini_debug_info = false, - last_used_round = 1, - }], - round_dump = [], - }, - }, - pid_cache = { - lru_size = 10, - round_size = 10, - current_round = 1, - lru_dump = [{ - elfs = {}, - size = 123, - pid = 666, - last_used_round = 0, - }], - round_dump = [], - }, - arch = "my-arch", - kernel = "my-kernel", -}`, string(v)) -} - -func newTestComponent(opts component.Options, args Arguments, session *mockSession, targetFinder sd.TargetFinder, ms *metrics) *Component { - alloyAppendable := pyroscope.NewFanout(args.ForwardTo, opts.ID, opts.Registerer) - res := &Component{ - options: opts, - metrics: ms, - appendable: alloyAppendable, - args: args, - targetFinder: targetFinder, - session: session, - argsUpdate: make(chan Arguments), - } - res.metrics.targetsActive.Set(float64(len(res.targetFinder.DebugInfo()))) - return res -} diff --git a/internal/component/pyroscope/ebpf/ebpf_placeholder.go b/internal/component/pyroscope/ebpf/ebpf_placeholder.go index 822a9446604..914d1c2b0a4 100644 --- a/internal/component/pyroscope/ebpf/ebpf_placeholder.go +++ b/internal/component/pyroscope/ebpf/ebpf_placeholder.go @@ -1,4 +1,4 @@ -//go:build !(linux && (arm64 || amd64)) +//go:build !(linux && (arm64 || amd64)) || !pyroscope_ebpf package ebpf diff --git a/internal/component/pyroscope/ebpf/metrics.go b/internal/component/pyroscope/ebpf/metrics.go index 6107fb777f0..26e12243717 100644 --- a/internal/component/pyroscope/ebpf/metrics.go +++ b/internal/component/pyroscope/ebpf/metrics.go @@ -1,4 +1,4 @@ -//go:build linux +//go:build linux && (arm64 || amd64) && pyroscope_ebpf // the build tag is to avoid unnecessary compilation of symtab diff --git a/internal/component/pyroscope/ebpf/pool.go b/internal/component/pyroscope/ebpf/pool.go index f464fac4073..f1734313865 100644 --- a/internal/component/pyroscope/ebpf/pool.go +++ b/internal/component/pyroscope/ebpf/pool.go @@ -1,3 +1,5 @@ +//go:build linux && (arm64 || amd64) && pyroscope_ebpf + package ebpf import "sync" diff --git a/internal/component/pyroscope/ebpf/reporter/builder.go b/internal/component/pyroscope/ebpf/reporter/builder.go new file mode 100644 index 00000000000..3a13a934f37 --- /dev/null +++ b/internal/component/pyroscope/ebpf/reporter/builder.go @@ -0,0 +1,209 @@ +//go:build linux && (arm64 || amd64) && pyroscope_ebpf + +package reporter + +import ( + "fmt" + "io" + "sync" + "time" + + "github.com/google/pprof/profile" + "github.com/klauspost/compress/gzip" + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/pyroscope/discovery" + "go.opentelemetry.io/ebpf-profiler/support" +) + +var ( + gzipWriterPool = sync.Pool{ + New: func() any { + res, err := gzip.NewWriterLevel(io.Discard, gzip.BestSpeed) + if err != nil { + panic(err) + } + return res + }, + } +) + +type BuildersOptions struct { + SampleRate int64 + PerPIDProfile bool + Origin libpf.Origin +} + +type builderHashKey struct { + labelsHash uint64 + pid uint32 +} + +type ProfileBuilders struct { + Builders map[builderHashKey]*ProfileBuilder + opt BuildersOptions + + samples batch[profile.Sample] + functions batch[profile.Function] + locations batch[profile.Location] +} + +func NewProfileBuilders(options BuildersOptions) *ProfileBuilders { + return &ProfileBuilders{Builders: make(map[builderHashKey]*ProfileBuilder), opt: options} +} + +func (b *ProfileBuilders) BuilderForSample( + target *discovery.Target, + pid uint32, +) *ProfileBuilder { + labelsHash, _ := target.Labels() + + k := builderHashKey{labelsHash: labelsHash} + if b.opt.PerPIDProfile { + k.pid = pid + } + res := b.Builders[k] + if res != nil { + return res + } + + var sampleType []*profile.ValueType + var periodType *profile.ValueType + var period int64 + if b.opt.Origin == support.TraceOriginSampling { + sampleType = []*profile.ValueType{{Type: "cpu", Unit: "nanoseconds"}} + periodType = &profile.ValueType{Type: "cpu", Unit: "nanoseconds"} + period = time.Second.Nanoseconds() / b.opt.SampleRate + } else if b.opt.Origin == support.TraceOriginOffCPU { + sampleType = []*profile.ValueType{{Type: "offcpu", Unit: "nanoseconds"}} + period = 1 + } else { + panic(fmt.Sprintf("unknown sample type %v", sampleType)) + } + dummyMapping := &profile.Mapping{ + ID: 1, + } + builder := &ProfileBuilder{ + p: b, + locations: make(map[libpf.FrameID]*profile.Location), + functions: make(map[functionsKey]*profile.Function), + Target: target, + Profile: &profile.Profile{ + Mapping: []*profile.Mapping{ + dummyMapping, + }, + SampleType: sampleType, + Period: period, + PeriodType: periodType, + TimeNanos: time.Now().UnixNano(), + }, + dummyMapping: dummyMapping, + fileIDtoMapping: make(map[libpf.FileID]*profile.Mapping), + } + res = builder + b.Builders[k] = res + return res +} + +type functionsKey struct { + name string + file string +} +type ProfileBuilder struct { + p *ProfileBuilders + locations map[libpf.FrameID]*profile.Location + + functions map[functionsKey]*profile.Function + + Profile *profile.Profile + Target *discovery.Target + + dummyMapping *profile.Mapping + fileIDtoMapping map[libpf.FileID]*profile.Mapping +} + +func (p *ProfileBuilder) Mapping(fid libpf.FileID) (*profile.Mapping, bool) { + if tmpMappingIndex, exists := p.fileIDtoMapping[fid]; exists { + return tmpMappingIndex, false + } + mid := uint64(len(p.Profile.Mapping) + 1) + mapping := &profile.Mapping{ + ID: mid, + } + p.fileIDtoMapping[fid] = mapping + p.Profile.Mapping = append(p.Profile.Mapping, mapping) + return mapping, true +} + +func (p *ProfileBuilder) Function(function, file string) *profile.Function { + k := functionsKey{name: function, file: file} + f, ok := p.functions[k] + if ok { + return f + } + + id := uint64(len(p.Profile.Function) + 1) + f = p.p.functions.pop() + f.ID = id + f.Name = function + f.Filename = file + + p.Profile.Function = append(p.Profile.Function, f) + p.functions[k] = f + return f +} + +func (p *ProfileBuilder) Write(dst io.Writer) (int64, error) { + gzipWriter := gzipWriterPool.Get().(*gzip.Writer) + gzipWriter.Reset(dst) + defer func() { + gzipWriter.Reset(io.Discard) + gzipWriterPool.Put(gzipWriter) + }() + err := p.Profile.WriteUncompressed(gzipWriter) + if err != nil { + return 0, fmt.Errorf("ebpf profile encode %w", err) + } + err = gzipWriter.Close() + if err != nil { + return 0, fmt.Errorf("ebpf profile encode %w", err) + } + return 0, nil +} + +func (p *ProfileBuilder) NewSample(locSize int) *profile.Sample { + sample := p.p.samples.pop() + sample.Value = []int64{0} + sample.Location = make([]*profile.Location, 0, locSize) + p.Profile.Sample = append(p.Profile.Sample, sample) + return sample +} + +func (p *ProfileBuilder) AddValue(v int64, sample *profile.Sample) { + sample.Value[0] += v * p.Profile.Period +} + +func (p *ProfileBuilder) Location(frameID libpf.FrameID) (*profile.Location, bool) { + loc, ok := p.locations[frameID] + if ok { + return loc, false + } + loc = p.p.locations.pop() + loc.ID = uint64(len(p.Profile.Location) + 1) + loc.Mapping = p.dummyMapping + p.locations[frameID] = loc + p.Profile.Location = append(p.Profile.Location, loc) + return loc, true +} + +type batch[T any] struct { + items []T +} + +func (b *batch[T]) pop() *T { + if len(b.items) == 0 { + b.items = make([]T, 128) + } + res := &b.items[0] + b.items = b.items[1:] + return res +} diff --git a/internal/component/pyroscope/ebpf/reporter/pprof.go b/internal/component/pyroscope/ebpf/reporter/pprof.go new file mode 100644 index 00000000000..d5bf4bce5a2 --- /dev/null +++ b/internal/component/pyroscope/ebpf/reporter/pprof.go @@ -0,0 +1,433 @@ +//go:build linux && (arm64 || amd64) && pyroscope_ebpf + +package reporter + +import ( + "bytes" + "context" + "maps" + "sync" + "time" + + "github.com/grafana/alloy/internal/runtime/logging/level" + + "github.com/elastic/go-freelru" + lru "github.com/elastic/go-freelru" + "github.com/go-kit/log" + "github.com/google/pprof/profile" + "github.com/prometheus/prometheus/model/labels" + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/libpf/xsync" + "go.opentelemetry.io/ebpf-profiler/process" + "go.opentelemetry.io/ebpf-profiler/pyroscope/discovery" + "go.opentelemetry.io/ebpf-profiler/pyroscope/symb/irsymcache" + reporter2 "go.opentelemetry.io/ebpf-profiler/reporter" + "go.opentelemetry.io/ebpf-profiler/reporter/samples" + "go.opentelemetry.io/ebpf-profiler/support" +) + +type PPROF struct { + Raw []byte + Labels labels.Labels + Origin libpf.Origin +} +type PPROFConsumer interface { + ConsumePprofProfiles(ctx context.Context, p []PPROF) +} + +type PPROFConsumerFunc func(ctx context.Context, p []PPROF) + +func (f PPROFConsumerFunc) ConsumePprofProfiles(ctx context.Context, p []PPROF) { + f(ctx, p) +} + +type Config struct { + CGroupCacheElements uint32 + ReportInterval time.Duration + SamplesPerSecond int64 + ExecutablesCacheElements uint32 + FramesCacheElements uint32 + + ExtraNativeSymbolResolver samples.NativeSymbolResolver + Consumer PPROFConsumer +} +type PPROFReporter struct { + cfg *Config + log log.Logger + cgroups freelru.Cache[libpf.PID, string] + traceEvents xsync.RWMutex[map[libpf.Origin]samples.KeyToEventMapping] + Executables *freelru.SyncedLRU[libpf.FileID, samples.ExecInfo] + Frames *lru.SyncedLRU[libpf.FrameID, samples.SourceInfo] + + sd discovery.TargetProducer + wg sync.WaitGroup + cancelReporting context.CancelFunc +} + +func NewPPROF( + log log.Logger, + cgroups freelru.Cache[libpf.PID, string], + cfg *Config, + sd discovery.TargetProducer, +) (*PPROFReporter, error) { + // Set a lifetime to reduce risk of invalid data in case of PID reuse. + cgroups.SetLifetime(90 * time.Second) + + originsMap := make(map[libpf.Origin]samples.KeyToEventMapping, 2) + for _, origin := range []libpf.Origin{support.TraceOriginSampling, + support.TraceOriginOffCPU} { + originsMap[origin] = make(samples.KeyToEventMapping) + } + executables, err := + freelru.NewSynced[libpf.FileID, samples.ExecInfo]( + cfg.ExecutablesCacheElements, + libpf.FileID.Hash32, + ) + if err != nil { + return nil, err + } + executables.SetLifetime(ExecutableCacheLifetime) + executables.SetOnEvict(func(f libpf.FileID, ei samples.ExecInfo) { + log.Log("msg", "evicting executable", "f", f.StringNoQuotes(), "n", ei.FileName, "id", ei.GnuBuildID) + }) + frames, err := freelru.NewSynced[ + libpf.FrameID, + samples.SourceInfo, + ]( + cfg.FramesCacheElements, libpf.FrameID.Hash32) + if err != nil { + return nil, err + } + frames.SetLifetime(FramesCacheLifetime) + + return &PPROFReporter{ + cfg: cfg, + log: log, + cgroups: cgroups, + traceEvents: xsync.NewRWMutex(originsMap), + Executables: executables, + Frames: frames, + sd: sd, + }, nil +} + +func (p *PPROFReporter) ReportTraceEvent(trace *libpf.Trace, meta *samples.TraceEventMeta) error { + if meta.Origin != support.TraceOriginSampling && meta.Origin != support.TraceOriginOffCPU { + return nil + } + + key := samples.TraceAndMetaKey{ + Hash: trace.Hash, + Comm: "", + ProcessName: "", + ExecutablePath: "", + ApmServiceName: "", + ContainerID: "", + Pid: int64(meta.PID), + ExtraMeta: nil, + } + + traceEventsMap := p.traceEvents.WLock() + defer p.traceEvents.WUnlock(&traceEventsMap) + + if events, exists := (*traceEventsMap)[meta.Origin][key]; exists { + events.Timestamps = append(events.Timestamps, uint64(meta.Timestamp)) + events.OffTimes = append(events.OffTimes, meta.OffTime) + (*traceEventsMap)[meta.Origin][key] = events + return nil + } + + (*traceEventsMap)[meta.Origin][key] = &samples.TraceEvents{ + Files: trace.Files, + Linenos: trace.Linenos, + FrameTypes: trace.FrameTypes, + MappingStarts: trace.MappingStart, + MappingEnds: trace.MappingEnd, + MappingFileOffsets: trace.MappingFileOffsets, + Timestamps: []uint64{uint64(meta.Timestamp)}, + OffTimes: []int64{meta.OffTime}, + } + return nil +} + +func (p *PPROFReporter) SupportsReportTraceEvent() bool { + return true +} + +func (p *PPROFReporter) ExecutableKnown(fileID libpf.FileID) bool { + _, known := p.Executables.GetAndRefresh(fileID, ExecutableCacheLifetime) + return known +} + +func (p *PPROFReporter) ExecutableMetadata(args *reporter2.ExecutableMetadataArgs) { + lt := ExecutableCacheLifetime + p.Executables.AddWithLifetime(args.FileID, samples.ExecInfo{ + FileName: args.FileName, + GnuBuildID: args.GnuBuildID, + }, lt) +} + +func (p *PPROFReporter) FrameKnown(frameID libpf.FrameID) bool { + _, ok := p.Frames.GetAndRefresh(frameID, FramesCacheLifetime) + return ok +} + +func (p *PPROFReporter) FrameMetadata(args *reporter2.FrameMetadataArgs) { + si := samples.SourceInfo{ + Frames: []samples.SourceInfoFrame{ + { + LineNumber: args.SourceLine, + FilePath: args.SourceFile, + FunctionName: args.FunctionName, + }, + }, + } + p.Frames.Add(args.FrameID, si) +} + +func (p *PPROFReporter) ReportHostMetadata(_ map[string]string) { +} + +func (p *PPROFReporter) ReportHostMetadataBlocking( + _ context.Context, + _ map[string]string, + _ int, + _ time.Duration, +) error { + return nil +} + +func (p *PPROFReporter) ReportMetrics( + _ uint32, + _ []uint32, + _ []int64, +) { + +} + +func (p *PPROFReporter) Start(ctx context.Context) error { + ctx, cancelReporting := context.WithCancel(ctx) + p.cancelReporting = cancelReporting + p.wg.Add(1) + + go func() { + defer p.wg.Done() + tick := time.NewTicker(p.cfg.ReportInterval) + defer tick.Stop() + purgeTick := time.NewTicker(5 * time.Minute) + defer purgeTick.Stop() + purge := false + for { + select { + case <-ctx.Done(): + return + case <-tick.C: + p.reportProfile(ctx) + if purge { + p.Executables.PurgeExpired() + p.Frames.PurgeExpired() + p.cgroups.PurgeExpired() + purge = false + } + case <-purgeTick.C: + purge = true + } + } + }() + + return nil +} + +func (p *PPROFReporter) Stop() { + if p.cancelReporting != nil { + p.cancelReporting() + } + p.wg.Wait() +} + +func (p *PPROFReporter) reportProfile(ctx context.Context) { + traceEvents := p.traceEvents.WLock() + events := make(map[libpf.Origin]samples.KeyToEventMapping, 2) + for _, origin := range []libpf.Origin{support.TraceOriginSampling, + support.TraceOriginOffCPU} { + events[origin] = maps.Clone((*traceEvents)[origin]) + clear((*traceEvents)[origin]) + } + p.traceEvents.WUnlock(&traceEvents) + + var profiles []PPROF + for _, origin := range []libpf.Origin{support.TraceOriginSampling, + support.TraceOriginOffCPU} { + originEvents := events[origin] + if len(originEvents) == 0 { + continue + } + pp := p.createProfile(origin, originEvents) + profiles = append(profiles, pp...) + } + + p.cfg.Consumer.ConsumePprofProfiles(ctx, profiles) + sz := 0 + for _, it := range profiles { + sz += len(it.Raw) + } + _ = level.Debug(p.log).Log("msg", "pprof report successful", "count", len(profiles), "total-size", sz) +} + +func (p *PPROFReporter) createProfile( + origin libpf.Origin, + events map[samples.TraceAndMetaKey]*samples.TraceEvents, +) []PPROF { + defer func() { + if p.cfg.ExtraNativeSymbolResolver != nil { + p.cfg.ExtraNativeSymbolResolver.Cleanup() + } + }() + + bs := NewProfileBuilders(BuildersOptions{ + SampleRate: p.cfg.SamplesPerSecond, + PerPIDProfile: true, + Origin: origin, + }) + + for traceKey, traceInfo := range events { + target := p.sd.FindTarget(uint32(traceKey.Pid)) + if target == nil { + continue + } + b := bs.BuilderForSample(target, uint32(traceKey.Pid)) + + s := b.NewSample(len(traceInfo.FrameTypes)) + + switch origin { + case support.TraceOriginSampling: + b.AddValue(int64(len(traceInfo.Timestamps)), s) + case support.TraceOriginOffCPU: + sum := int64(0) + for _, t := range traceInfo.OffTimes { + sum += t + } + b.AddValue(sum, s) + } + + // Walk every frame of the trace. + for i := range traceInfo.FrameTypes { + fileID := traceInfo.Files[i] + addrOrLineNo := traceInfo.Linenos[i] + frameID := libpf.NewFrameID(fileID, addrOrLineNo) + location, locationFresh := b.Location(frameID) + if locationFresh { + location.Address = uint64(addrOrLineNo) + switch frameKind := traceInfo.FrameTypes[i]; frameKind { + case libpf.NativeFrame: + mapping, mappingFresh := b.Mapping(traceInfo.Files[i]) + if mappingFresh { + ei, exists := p.Executables.GetAndRefresh(traceInfo.Files[i], + ExecutableCacheLifetime) + + var fileName = "UNKNOWN" + if exists { + fileName = ei.FileName + } + mapping.Start = uint64(traceInfo.MappingStarts[i]) + mapping.Limit = uint64(traceInfo.MappingEnds[i]) + mapping.Offset = traceInfo.MappingFileOffsets[i] + mapping.File = fileName + mapping.BuildID = ei.GnuBuildID + } + location.Mapping = mapping + p.symbolizeNativeFrame(b, location, traceInfo, i) + case libpf.AbortFrame: + // Next step: Figure out how the OTLP protocol + // could handle artificial frames, like AbortFrame, + // that are not originated from a native or interpreted + // program. + default: + var funcName string + var filePath string + var lineNo int64 + if si, exists := p.Frames.GetAndRefresh(frameID, FramesCacheLifetime); exists { + if len(si.Frames) == 1 { + fr := si.Frames[0] + funcName = fr.FunctionName + filePath = fr.FilePath + lineNo = int64(fr.LineNumber) + } else { + funcName = "UNRESOLVED2" + } + } else { + funcName = "UNRESOLVED" + } + location.Line = []profile.Line{{ + Line: lineNo, + Function: b.Function(funcName, filePath)}, + } + location.Mapping.HasFunctions = true + } + } + if traceInfo.FrameTypes[i] == libpf.PythonFrame && len(location.Line) == 1 && location.Line[0].Function.Name == "" { + continue + } + s.Location = append(s.Location, location) + } + } + res := make([]PPROF, 0, len(bs.Builders)) + for _, b := range bs.Builders { + buf := bytes.NewBuffer(nil) + _, err := b.Write(buf) + if err != nil { + _ = p.log.Log("err", err) + continue + } + _, ls := b.Target.Labels() + metric := discovery.MetricValueProcessCPU + if origin == support.TraceOriginOffCPU { + metric = discovery.MetricValueOffCPU + } + labelsWithMetric := make([]labels.Label, 0, len(ls)+1) + labelsWithMetric = append(labelsWithMetric, ls...) + labelsWithMetric = append(labelsWithMetric, labels.Label{ + Name: labels.MetricName, + Value: metric, + }) + res = append(res, PPROF{ + Raw: buf.Bytes(), + Labels: labelsWithMetric, + Origin: origin, + }) + } + return res +} + +func (p *PPROFReporter) symbolizeNativeFrame( + b *ProfileBuilder, + loc *profile.Location, + traceInfo *samples.TraceEvents, + i int, +) { + if loc.Mapping.File == process.VdsoPathName { + return + } + if p.cfg.ExtraNativeSymbolResolver == nil { + return + } + fileID := traceInfo.Files[i] + addr := traceInfo.Linenos[i] + frameID := libpf.NewFrameID(fileID, addr) + + irsymcache.SymbolizeNativeFrame(p.cfg.ExtraNativeSymbolResolver, p.Frames, loc.Mapping.File, frameID, func(si samples.SourceInfo) { + if len(si.Frames) > 0 { + loc.Mapping.HasFunctions = true + } + for _, fn := range si.Frames { + line := profile.Line{Function: b.Function(fn.FunctionName, fn.FilePath)} + line.Line = int64(fn.LineNumber) + loc.Line = append(loc.Line, line) + } + }) +} + +const ( + ExecutableCacheLifetime = 1 * time.Hour + FramesCacheLifetime = 1 * time.Hour +) diff --git a/internal/component/pyroscope/ebpf/reporter/reporter.go b/internal/component/pyroscope/ebpf/reporter/reporter.go new file mode 100644 index 00000000000..09f70185809 --- /dev/null +++ b/internal/component/pyroscope/ebpf/reporter/reporter.go @@ -0,0 +1,48 @@ +//go:build linux && (arm64 || amd64) && pyroscope_ebpf + +package reporter + +import ( + "time" + + "github.com/elastic/go-freelru" + "github.com/go-kit/log" + "go.opentelemetry.io/ebpf-profiler/libpf" + pyrosd "go.opentelemetry.io/ebpf-profiler/pyroscope/discovery" + "go.opentelemetry.io/ebpf-profiler/pyroscope/internalshim/controller" + "go.opentelemetry.io/ebpf-profiler/reporter" + samples2 "go.opentelemetry.io/ebpf-profiler/reporter/samples" +) + +func New( + log log.Logger, + cgroups freelru.Cache[libpf.PID, string], + cfg *controller.Config, + sd pyrosd.TargetProducer, + nfs samples2.NativeSymbolResolver, + consumer PPROFConsumer, +) (reporter.Reporter, error) { + + return NewPPROF(log, cgroups, &Config{ + ExtraNativeSymbolResolver: nfs, + CGroupCacheElements: 1024, + ReportInterval: cfg.ReporterInterval, + SamplesPerSecond: int64(cfg.SamplesPerSecond), + ExecutablesCacheElements: 16384, + FramesCacheElements: 65536, + Consumer: consumer, + }, sd) + +} + +func NewContainerIDCache(size uint32) (freelru.Cache[libpf.PID, string], error) { + var cgroups freelru.Cache[libpf.PID, string] + var err error + h := func(pid libpf.PID) uint32 { return uint32(pid) } + cgroups, err = freelru.New[libpf.PID, string](size, h) + if err != nil { + return nil, err + } + cgroups.SetLifetime(5 * time.Minute) + return cgroups, nil +} diff --git a/internal/component/pyroscope/ebpf/send.go b/internal/component/pyroscope/ebpf/send.go index 0af739573c7..52a7f78d107 100644 --- a/internal/component/pyroscope/ebpf/send.go +++ b/internal/component/pyroscope/ebpf/send.go @@ -1,24 +1,24 @@ -//go:build (linux && arm64) || (linux && amd64) +//go:build linux && (arm64 || amd64) && pyroscope_ebpf package ebpf import ( - "bytes" "context" - "fmt" "time" "github.com/go-kit/log/level" "github.com/grafana/alloy/internal/component/pyroscope" - "github.com/grafana/pyroscope/ebpf/pprof" + "github.com/grafana/alloy/internal/component/pyroscope/ebpf/reporter" ) const maxSendConcurrency = 32 -func (c *Component) sendProfiles(ctx context.Context, builders *pprof.ProfileBuilders) { +func (c *Component) sendProfiles(ctx context.Context, ps []reporter.PPROF) { + var err error start := time.Now() pool := workerPool{} - pool.run(min(maxSendConcurrency, len(builders.Builders))) + n := len(ps) + pool.run(min(maxSendConcurrency, n)) queued := 0 ctx, cancel := context.WithTimeout(ctx, c.args.CollectInterval) defer func() { @@ -27,25 +27,19 @@ func (c *Component) sendProfiles(ctx context.Context, builders *pprof.ProfileBui level.Debug(c.options.Logger).Log("msg", "sent profiles", "duration", time.Since(start), "queued", queued) }() j := 0 - for _, builder := range builders.Builders { - serviceName := builder.Labels.Get("service_name") + for _, p := range ps { + serviceName := p.Labels.Get("service_name") c.metrics.pprofsTotal.WithLabelValues(serviceName).Inc() - c.metrics.pprofSamplesTotal.WithLabelValues(serviceName).Add(float64(len(builder.Profile.Sample))) + c.metrics.pprofSamplesTotal.WithLabelValues(serviceName).Add(float64(len(p.Raw))) - buf := bytes.NewBuffer(nil) - _, err := builder.Write(buf) - if err != nil { - level.Error(c.options.Logger).Log("err", fmt.Errorf("ebpf profile encode %w", err)) - continue - } - rawProfile := buf.Bytes() + rawProfile := p.Raw appender := c.appendable.Appender() c.metrics.pprofBytesTotal.WithLabelValues(serviceName).Add(float64(len(rawProfile))) job := func() { samples := []*pyroscope.RawSample{{RawProfile: rawProfile}} - err = appender.Append(ctx, builder.Labels, samples) + err = appender.Append(ctx, p.Labels, samples) if err != nil { level.Error(c.options.Logger).Log("msg", "ebpf pprof write", "err", err) } @@ -54,7 +48,7 @@ func (c *Component) sendProfiles(ctx context.Context, builders *pprof.ProfileBui case pool.jobs <- job: queued++ case <-ctx.Done(): - dropped := len(builders.Builders) - j + dropped := n - j c.metrics.pprofsDroppedTotal.Add(float64(dropped)) level.Debug(c.options.Logger).Log("msg", "dropped profiles", "count", dropped) return diff --git a/internal/component/pyroscope/ebpf/send_test.go b/internal/component/pyroscope/ebpf/send_test.go index 5df1e444fdd..15db43738b6 100644 --- a/internal/component/pyroscope/ebpf/send_test.go +++ b/internal/component/pyroscope/ebpf/send_test.go @@ -1,4 +1,4 @@ -//go:build (linux && arm64) || (linux && amd64) +//go:build ((linux && arm64) || (linux && amd64)) && pyroscope_ebpf package ebpf @@ -11,13 +11,13 @@ import ( "go.uber.org/atomic" "github.com/grafana/alloy/internal/component/pyroscope" + "github.com/grafana/alloy/internal/component/pyroscope/ebpf/reporter" "github.com/grafana/alloy/internal/util" - "github.com/grafana/pyroscope/ebpf/pprof" - "github.com/grafana/pyroscope/ebpf/sd" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/prometheus/model/labels" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/ebpf-profiler/support" ) func TestSendProfilesConcurrently(t *testing.T) { @@ -77,25 +77,16 @@ func TestSendProfilesConcurrently(t *testing.T) { }), }, "", reg) - profiles := pprof.NewProfileBuilders(pprof.BuildersOptions{ - SampleRate: 97, - }) + profiles := []reporter.PPROF{} for i := 0; i < td.profilesCount; i++ { - cid := fmt.Sprintf("cid_%d", i) - pid := uint32(239 + i) - target := sd.DiscoveryTarget(map[string]string{ + target := labels.FromMap(map[string]string{ "service_name": fmt.Sprintf("service_%d", i), }) - profiles.AddSample(&pprof.ProfileSample{ - Target: sd.NewTargetForTesting(cid, pid, target), - Pid: pid, - SampleType: pprof.SampleTypeCpu, - Aggregation: pprof.SampleAggregated, - Stack: []string{ - "func1", "func2", - }, - Value: 42, + profiles = append(profiles, reporter.PPROF{ + Raw: []byte(fmt.Sprintf("profile_%d", i)), + Labels: target, + Origin: support.TraceOriginSampling, }) } diff --git a/tools/make/packaging.mk b/tools/make/packaging.mk index efa518d0951..6ee3dd6ff8e 100644 --- a/tools/make/packaging.mk +++ b/tools/make/packaging.mk @@ -32,13 +32,13 @@ dist-alloy-binaries: dist/alloy-linux-amd64 \ dist/alloy-windows-amd64.exe \ dist/alloy-freebsd-amd64 -dist/alloy-linux-amd64: GO_TAGS += netgo builtinassets promtail_journal_enabled +dist/alloy-linux-amd64: GO_TAGS += netgo builtinassets promtail_journal_enabled pyroscope_ebpf dist/alloy-linux-amd64: GOOS := linux dist/alloy-linux-amd64: GOARCH := amd64 dist/alloy-linux-amd64: generate-ui $(PACKAGING_VARS) ALLOY_BINARY=$@ "$(MAKE)" -f $(PARENT_MAKEFILE) alloy -dist/alloy-linux-arm64: GO_TAGS += netgo builtinassets promtail_journal_enabled +dist/alloy-linux-arm64: GO_TAGS += netgo builtinassets promtail_journal_enabled pyroscope_ebpf dist/alloy-linux-arm64: GOOS := linux dist/alloy-linux-arm64: GOARCH := arm64 dist/alloy-linux-arm64: generate-ui @@ -97,14 +97,14 @@ dist/alloy-freebsd-amd64: generate-ui dist-alloy-boringcrypto-binaries: dist/alloy-boringcrypto-linux-amd64 \ dist/alloy-boringcrypto-linux-arm64 -dist/alloy-boringcrypto-linux-amd64: GO_TAGS += netgo builtinassets promtail_journal_enabled +dist/alloy-boringcrypto-linux-amd64: GO_TAGS += netgo builtinassets promtail_journal_enabled pyroscope_ebpf dist/alloy-boringcrypto-linux-amd64: GOEXPERIMENT := boringcrypto dist/alloy-boringcrypto-linux-amd64: GOOS := linux dist/alloy-boringcrypto-linux-amd64: GOARCH := amd64 dist/alloy-boringcrypto-linux-amd64: generate-ui $(PACKAGING_VARS) ALLOY_BINARY=$@ "$(MAKE)" -f $(PARENT_MAKEFILE) alloy -dist/alloy-boringcrypto-linux-arm64: GO_TAGS += netgo builtinassets promtail_journal_enabled +dist/alloy-boringcrypto-linux-arm64: GO_TAGS += netgo builtinassets promtail_journal_enabled pyroscope_ebpf dist/alloy-boringcrypto-linux-arm64: GOEXPERIMENT := boringcrypto dist/alloy-boringcrypto-linux-arm64: GOOS := linux dist/alloy-boringcrypto-linux-arm64: GOARCH := arm64