From 0456fc217c70924578faa0be756b767fa5d86a04 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:55:24 +0200 Subject: [PATCH 01/24] chore(deps): update rust dependencies (#672) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 517cf8d4d..017a3bba8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,9 +316,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "object" -version = "0.37.1" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a" +checksum = "b3e3d0a7419f081f4a808147e845310313a39f322d7ae1f996b7f001d6cbed04" dependencies = [ "memchr", ] @@ -507,9 +507,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", From 74e8ef2ac10b1bd48b9247e4a5e1b44f6e90ce50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:30:05 +0300 Subject: [PATCH 02/24] fix(deps): update go dependencies (#679) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 28 ++++++++++++++-------------- go.sum | 56 ++++++++++++++++++++++++++++---------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index abbcc823c..98e7a0663 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module go.opentelemetry.io/ebpf-profiler go 1.23.6 require ( - github.com/aws/aws-sdk-go-v2 v1.37.1 - github.com/aws/aws-sdk-go-v2/config v1.30.2 - github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1 + github.com/aws/aws-sdk-go-v2 v1.37.2 + github.com/aws/aws-sdk-go-v2/config v1.30.3 + github.com/aws/aws-sdk-go-v2/service/s3 v1.86.0 github.com/cespare/xxhash/v2 v2.3.0 github.com/cilium/ebpf v0.19.0 github.com/docker/go-connections v0.5.0 @@ -45,19 +45,19 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.18.2 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.1 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.3 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.2 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.26.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.35.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.27.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.32.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.36.0 // indirect github.com/aws/smithy-go v1.22.5 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/containerd/containerd v1.7.18 // indirect diff --git a/go.sum b/go.sum index a7060da7d..87f39055c 100644 --- a/go.sum +++ b/go.sum @@ -6,40 +6,40 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/aws/aws-sdk-go-v2 v1.37.1 h1:SMUxeNz3Z6nqGsXv0JuJXc8w5YMtrQMuIBmDx//bBDY= -github.com/aws/aws-sdk-go-v2 v1.37.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= +github.com/aws/aws-sdk-go-v2 v1.37.2 h1:xkW1iMYawzcmYFYEV0UCMxc8gSsjCGEhBXQkdQywVbo= +github.com/aws/aws-sdk-go-v2 v1.37.2/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 h1:6GMWV6CNpA/6fbFHnoAjrv4+LGfyTqZz2LtCHnspgDg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0/go.mod h1:/mXlTIVG9jbxkqDnr5UQNQxW1HRYxeGklkM9vAFeabg= -github.com/aws/aws-sdk-go-v2/config v1.30.2 h1:YE1BmSc4fFYqFgN1mN8uzrtc7R9x+7oSWeX8ckoltAw= -github.com/aws/aws-sdk-go-v2/config v1.30.2/go.mod h1:UNrLGZ6jfAVjgVJpkIxjLufRJqTXCVYOpkeVf83kwBo= -github.com/aws/aws-sdk-go-v2/credentials v1.18.2 h1:mfm0GKY/PHLhs7KO0sUaOtFnIQ15Qqxt+wXbO/5fIfs= -github.com/aws/aws-sdk-go-v2/credentials v1.18.2/go.mod h1:v0SdJX6ayPeZFQxgXUKw5RhLpAoZUuynxWDfh8+Eknc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1 h1:owmNBboeA0kHKDcdF8KiSXmrIuXZustfMGGytv6OMkM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1/go.mod h1:Bg1miN59SGxrZqlP8vJZSmXW+1N8Y1MjQDq1OfuNod8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.1 h1:ksZXBYv80EFTcgc8OJO48aQ8XDWXIQL7gGasPeCoTzI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.1/go.mod h1:HSksQyyJETVZS7uM54cir0IgxttTD+8aEoJMPGepHBI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.1 h1:+dn/xF/05utS7tUhjIcndbuaPjfll2LhbH1cCDGLYUQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.1/go.mod h1:hyAGz30LHdm5KBZDI58MXx5lDVZ5CUfvfTZvMu4HCZo= +github.com/aws/aws-sdk-go-v2/config v1.30.3 h1:utupeVnE3bmB221W08P0Moz1lDI3OwYa2fBtUhl7TCc= +github.com/aws/aws-sdk-go-v2/config v1.30.3/go.mod h1:NDGwOEBdpyZwLPlQkpKIO7frf18BW8PaCmAM9iUxQmI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.3 h1:ptfyXmv+ooxzFwyuBth0yqABcjVIkjDL0iTYZBSbum8= +github.com/aws/aws-sdk-go-v2/credentials v1.18.3/go.mod h1:Q43Nci++Wohb0qUh4m54sNln0dbxJw8PvQWkrwOkGOI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.2 h1:nRniHAvjFJGUCl04F3WaAj7qp/rcz5Gi1OVoj5ErBkc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.2/go.mod h1:eJDFKAMHHUvv4a0Zfa7bQb//wFNUXGrbFpYRCHe2kD0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2 h1:sPiRHLVUIIQcoVZTNwqQcdtjkqkPopyYmIX0M5ElRf4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2/go.mod h1:ik86P3sgV+Bk7c1tBFCwI3VxMoSEwl4YkRB9xn1s340= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.2 h1:ZdzDAg075H6stMZtbD2o+PyB933M/f20e9WmCBC17wA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.2/go.mod h1:eE1IIzXG9sdZCB0pNNpMpsYTLl4YdOQD3njiVN1e/E4= 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.4.1 h1:4HbnOGE9491a9zYJ9VpPh1ApgEq6ZlD4Kuv1PJenFpc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.1/go.mod h1:Z6QnHC6TmpJWUxAy8FI4JzA7rTwl6EIANkyK9OR5z5w= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.2 h1:sBpc8Ph6CpfZsEdkz/8bfg8WhKlWMCms5iWj6W/AW2U= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.2/go.mod h1:Z2lDojZB+92Wo6EKiZZmJid9pPrDJW2NNIXSlaEfVlU= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1 h1:ps3nrmBWdWwakZBydGX1CxeYFK80HsQ79JLMwm7Y4/c= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1/go.mod h1:bAdfrfxENre68Hh2swNaGEVuFYE74o0SaSCAlaG9E74= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.1 h1:ky79ysLMxhwk5rxJtS+ILd3Mc8kC5fhsLBrP27r6h4I= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.1/go.mod h1:+2MmkvFvPYM1vsozBWduoLJUi5maxFk5B7KJFECujhY= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1 h1:MdVYlN5pcQu1t1OYx4Ajo3fKl1IEhzgdPQbYFCRjYS8= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1/go.mod h1:iikmNLrvHm2p4a3/4BPeix2S9P+nW8yM1IZW73x8bFA= -github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1 h1:Hsqo8+dFxSdDvv9B2PgIx1AJAnDpqgS0znVI+R+MoGY= -github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1/go.mod h1:8Q0TAPXD68Z8YqlcIGHs/UNIDHsxErV9H4dl4vJEpgw= -github.com/aws/aws-sdk-go-v2/service/sso v1.26.1 h1:uWaz3DoNK9MNhm7i6UGxqufwu3BEuJZm72WlpGwyVtY= -github.com/aws/aws-sdk-go-v2/service/sso v1.26.1/go.mod h1:ILpVNjL0BO+Z3Mm0SbEeUoYS9e0eJWV1BxNppp0fcb8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1 h1:XdG6/o1/ZDmn3wJU5SRAejHaWgKS4zHv0jBamuKuS2k= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1/go.mod h1:oiotGTKadCOCl3vg/tYh4k45JlDF81Ka8rdumNhEnIQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.35.1 h1:iF4Xxkc0H9c/K2dS0zZw3SCkj0Z7n6AMnUiiyoJND+I= -github.com/aws/aws-sdk-go-v2/service/sts v1.35.1/go.mod h1:0bxIatfN0aLq4mjoLDeBpOjOke68OsFlXPDFJ7V0MYw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.2 h1:blV3dY6WbxIVOFggfYIo2E1Q2lZoy5imS7nKgu5m6Tc= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.2/go.mod h1:cBWNeLBjHJRSmXAxdS7mwiMUEgx6zup4wQ9J+/PcsRQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.2 h1:oxmDEO14NBZJbK/M8y3brhMFEIGN4j8a6Aq8eY0sqlo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.2/go.mod h1:4hH+8QCrk1uRWDPsVfsNDUup3taAjO8Dnx63au7smAU= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.2 h1:0hBNFAPwecERLzkhhBY+lQKUMpXSKVv4Sxovikrioms= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.2/go.mod h1:Vcnh4KyR4imrrjGN7A2kP2v9y6EPudqoPKXtnmBliPU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.86.0 h1:utPhv4ECQzJIUbtx7vMN4A8uZxlQ5tSt1H1toPI41h8= +github.com/aws/aws-sdk-go-v2/service/s3 v1.86.0/go.mod h1:1/eZYtTWazDgVl96LmGdGktHFi7prAcGCrJ9JGvBITU= +github.com/aws/aws-sdk-go-v2/service/sso v1.27.0 h1:j7/jTOjWeJDolPwZ/J4yZ7dUsxsWZEsxNwH5O7F8eEA= +github.com/aws/aws-sdk-go-v2/service/sso v1.27.0/go.mod h1:M0xdEPQtgpNT7kdAX4/vOAPkFj60hSQRb7TvW9B0iug= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.32.0 h1:ywQF2N4VjqX+Psw+jLjMmUL2g1RDHlvri3NxHA08MGI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.32.0/go.mod h1:Z+qv5Q6b7sWiclvbJyPSOT1BRVU9wfSUPaqQzZ1Xg3E= +github.com/aws/aws-sdk-go-v2/service/sts v1.36.0 h1:bRP/a9llXSSgDPk7Rqn5GD/DQCGo6uk95plBFKoXt2M= +github.com/aws/aws-sdk-go-v2/service/sts v1.36.0/go.mod h1:tgBsFzxwl65BWkuJ/x2EUs59bD4SfYKgikvFDJi1S58= github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= From d45a304b409afa5f83405eade9b072fbb38c2820 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:30:35 +0300 Subject: [PATCH 03/24] chore(deps): update docker/login-action action to v3.5.0 (#680) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/push-docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push-docker-image.yml b/.github/workflows/push-docker-image.yml index 03449e27e..203291c71 100644 --- a/.github/workflows/push-docker-image.yml +++ b/.github/workflows/push-docker-image.yml @@ -17,7 +17,7 @@ jobs: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Login to Docker Hub - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} From 1453e146cf3f9697b9a330557dd6bacd1243b034 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:49:24 +0300 Subject: [PATCH 04/24] chore(deps): update actions/download-artifact action to v5 (#681) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/unit-test-on-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-test-on-pull-request.yml b/.github/workflows/unit-test-on-pull-request.yml index 99c734632..993d51cf0 100644 --- a/.github/workflows/unit-test-on-pull-request.yml +++ b/.github/workflows/unit-test-on-pull-request.yml @@ -177,7 +177,7 @@ jobs: go install github.com/florianl/bluebox@v0.0.1 sudo mv ~/go/bin/bluebox /usr/local/bin/. - name: Fetch integration test binaries - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: { name: "integration-test-binaries-${{ matrix.target_arch }}" } - name: Fetch precompiled kernel run: | From 1604ee09b9869aa6735f838e0871e1a0c8263015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Wed, 6 Aug 2025 10:49:56 +0300 Subject: [PATCH 05/24] interpreter: reduce GC stress by interning and using unsafe.String (#665) --- interpreter/apmint/apmint.go | 2 +- interpreter/dotnet/pe.go | 21 +++++---- interpreter/hotspot/instance.go | 4 +- interpreter/nodev8/v8.go | 2 +- interpreter/perl/data.go | 11 +---- interpreter/perl/instance.go | 81 +++++++++++++-------------------- interpreter/php/php.go | 2 +- 7 files changed, 48 insertions(+), 75 deletions(-) diff --git a/interpreter/apmint/apmint.go b/interpreter/apmint/apmint.go index bb4eee195..ddfe142a8 100644 --- a/interpreter/apmint/apmint.go +++ b/interpreter/apmint/apmint.go @@ -207,7 +207,7 @@ func nextString(rm remotememory.RemoteMemory, addr *libpf.Address, maxLen int) ( } *addr += libpf.Address(length) - return string(raw), nil + return unsafe.String(unsafe.SliceData(raw), len(raw)), nil } // readProcStorage reads the APM process storage from memory. diff --git a/interpreter/dotnet/pe.go b/interpreter/dotnet/pe.go index 6ebc5e531..550096b38 100644 --- a/interpreter/dotnet/pe.go +++ b/interpreter/dotnet/pe.go @@ -14,6 +14,7 @@ import ( "slices" "sync/atomic" "time" + "unsafe" "github.com/elastic/go-freelru" @@ -266,7 +267,7 @@ type peInfo struct { // strings contains the preloaded strings from dotnet string heap. // If this consumes too much memory, this could be converted to LRU and on-demand // populated by reading the strings from attached process memory. - strings map[uint32]string + strings map[uint32]libpf.String } // peParser contains the needed data when reading and parsing the dotnet data from a PE file. @@ -568,7 +569,7 @@ func (pp *peParser) parseCLI() error { break } } - switch string(name) { + switch unsafe.String(unsafe.SliceData(name), len(name)) { case "#Strings": // ECMA-335 II.24.2.3 #Strings heap pp.dotnetStrings = io.NewSectionReader(r, int64(hdr.Offset), int64(hdr.Size)) @@ -595,10 +596,10 @@ func (pp *peParser) parseCLI() error { return nil } -func (pp *peParser) readDotnetString(offs uint32) string { +func (pp *peParser) readDotnetString(offs uint32) libpf.String { // Read a string from the ECMA-335 II.24.2.3 #Strings heap if offs == 0 { - return "" + return libpf.NullString } // Zero terminated string. Assume maximum length of 1024 bytes. @@ -609,17 +610,17 @@ func (pp *peParser) readDotnetString(offs uint32) string { chunk := str[i : i+chunkSize] n, err := pp.dotnetStrings.ReadAt(chunk, int64(offs)+int64(i)) if n == 0 && err != nil { - return "" + return libpf.NullString } zeroIdx := bytes.IndexByte(chunk[:n], 0) if zeroIdx >= 0 { - return string(str[:i+zeroIdx]) + return libpf.Intern(unsafe.String(unsafe.SliceData(str[:]), i+zeroIdx)) } } // Likely broken string. - return "" + return libpf.NullString } func (pp *peParser) readDotnetGUID(offs uint32) string { @@ -697,7 +698,7 @@ func (pp *peParser) parseModuleTable() { guidIdx := pp.readDotnetIndex(indexGUID) pp.skipDotnetBytes(2 * pp.indexSizes[indexGUID]) - pp.info.simpleName = libpf.Intern(pp.readDotnetString(nameIdx)) + pp.info.simpleName = pp.readDotnetString(nameIdx) pp.info.guid = pp.readDotnetGUID(guidIdx) } } @@ -847,7 +848,7 @@ func (pp *peParser) parseTables() error { return fmt.Errorf("number of Modules (%d) is unexpected", pp.tableRows[0]) } - pp.info.strings = map[uint32]string{} + pp.info.strings = map[uint32]libpf.String{} // Precalculate the column sizes we need to know pp.indexSizes[indexString] = getHeapSize(tablesHeader.HeapSizes&0x1 != 0) @@ -1153,7 +1154,7 @@ func (pi *peInfo) resolveMethodName(methodIdx uint32) libpf.String { } typeSpec := &pi.typeSpecs[idx] - typeName := pi.strings[typeSpec.typeNameIdx] + typeName := pi.strings[typeSpec.typeNameIdx].String() for typeSpec.enclosingClass != 0 { enclosingSpec := &pi.typeSpecs[typeSpec.enclosingClass-1] typeName = fmt.Sprintf("%s/%s", pi.strings[enclosingSpec.typeNameIdx], typeName) diff --git a/interpreter/hotspot/instance.go b/interpreter/hotspot/instance.go index 94bb565ba..8d7deb148 100644 --- a/interpreter/hotspot/instance.go +++ b/interpreter/hotspot/instance.go @@ -204,9 +204,9 @@ func (d *hotspotInstance) getSymbol(addr libpf.Address) libpf.String { return libpf.NullString } } - s := string(tmp) + s := unsafe.String(unsafe.SliceData(tmp), len(tmp)) if !util.IsValidString(s) { - log.Debugf("Extracted Hotspot symbol is invalid at 0x%x '%v'", addr, []byte(s)) + log.Debugf("Extracted Hotspot symbol is invalid at 0x%x '%v'", addr, tmp) return libpf.NullString } value := libpf.Intern(s) diff --git a/interpreter/nodev8/v8.go b/interpreter/nodev8/v8.go index da4b0fd3c..457ef45b4 100644 --- a/interpreter/nodev8/v8.go +++ b/interpreter/nodev8/v8.go @@ -855,7 +855,7 @@ func (i *v8Instance) extractString(ptr libpf.Address, tag uint16, cb func(string if err != nil { return err } - if err = cb(string(buf)); err != nil { + if err = cb(unsafe.String(unsafe.SliceData(buf), len(buf))); err != nil { return err } } diff --git a/interpreter/perl/data.go b/interpreter/perl/data.go index e5311a8f8..20c9fff68 100644 --- a/interpreter/perl/data.go +++ b/interpreter/perl/data.go @@ -5,7 +5,6 @@ package perl // import "go.opentelemetry.io/ebpf-profiler/interpreter/perl" import ( "fmt" - "sync" "github.com/elastic/go-freelru" log "github.com/sirupsen/logrus" @@ -122,7 +121,7 @@ func (d *perlData) String() string { func (d *perlData) Attach(_ interpreter.EbpfHandler, _ libpf.PID, bias libpf.Address, rm remotememory.RemoteMemory) (interpreter.Instance, error) { - addrToHEK, err := freelru.New[libpf.Address, string](interpreter.LruFunctionCacheSize, + addrToHEK, err := freelru.New[libpf.Address, libpf.String](interpreter.LruFunctionCacheSize, libpf.Address.Hash32) if err != nil { return nil, err @@ -147,14 +146,6 @@ func (d *perlData) Attach(_ interpreter.EbpfHandler, _ libpf.PID, bias libpf.Add addrToHEK: addrToHEK, addrToCOP: addrToCOP, addrToGV: addrToGV, - memPool: sync.Pool{ - New: func() any { - // To avoid resizing of the returned byte slize we size new - // allocations to hekLenLimit. - buf := make([]byte, hekLenLimit) - return &buf - }, - }, }, nil } diff --git a/interpreter/perl/instance.go b/interpreter/perl/instance.go index 7f8379b90..69f03cedd 100644 --- a/interpreter/perl/instance.go +++ b/interpreter/perl/instance.go @@ -6,7 +6,6 @@ package perl // import "go.opentelemetry.io/ebpf-profiler/interpreter/perl" import ( "errors" "fmt" - "sync" "sync/atomic" "unsafe" @@ -38,7 +37,7 @@ type perlInstance struct { bias libpf.Address // addrToHEK maps a PERL Hash Element Key (string with hash) to a Go string - addrToHEK *freelru.LRU[libpf.Address, string] + addrToHEK *freelru.LRU[libpf.Address, libpf.String] // addrToCOP maps a PERL Control OP (COP) structure to a perlCOP which caches data from it addrToCOP *freelru.LRU[copKey, *perlCOP] @@ -46,9 +45,6 @@ type perlInstance struct { // addrToGV maps a PERL Glob Value (GV) aka "symbol" to its name string addrToGV *freelru.LRU[libpf.Address, libpf.String] - // memPool provides pointers to byte arrays for efficient memory reuse. - memPool sync.Pool - // hekLen is the largest number we did see in the last reporting interval for hekLen // in getHEK. hekLen atomic.Uint32 @@ -202,22 +198,24 @@ func (i *perlInstance) GetAndResetMetrics() ([]metrics.Metric, error) { }, nil } -func (i *perlInstance) getHEK(addr libpf.Address) (string, error) { +func (i *perlInstance) getHEK(addr libpf.Address) (libpf.String, error) { if addr == 0 { - return "", errors.New("null hek pointer") + return libpf.NullString, errors.New("null hek pointer") } if value, ok := i.addrToHEK.Get(addr); ok { return value, nil } vms := &i.d.vmStructs + var buf [hekLenLimit]byte + // Read the Hash Element Key (HEK) length and readahead bytes in // attempt to avoid second system call to read the target string. // 128 is chosen arbitrarily as "hopefully good enough"; this value can // be increased if it turns out to be necessary. - var buf [128]byte - if err := i.rm.Read(addr, buf[:]); err != nil { - return "", err + const hekInitialRead = 128 + if err := i.rm.Read(addr, buf[:hekInitialRead]); err != nil { + return libpf.NullString, err } hekLen := npsr.Uint32(buf[:], vms.hek.hek_len) @@ -225,55 +223,38 @@ func (i *perlInstance) getHEK(addr libpf.Address) (string, error) { // hekLen and report it. util.AtomicUpdateMaxUint32(&i.hekLen, hekLen) - if hekLen > hekLenLimit { - return "", fmt.Errorf("hek too large (%d)", hekLen) - } - - syncPoolData := i.memPool.Get().(*[]byte) - if syncPoolData == nil { - return "", errors.New("failed to get memory from sync pool") - } - - defer func() { - // Reset memory and return it for reuse. - for j := range hekLen { - (*syncPoolData)[j] = 0x0 + if hekSize := uint32(vms.hek.hek_key) + hekLen; hekSize > hekInitialRead { + if hekSize > hekLenLimit { + return libpf.NullString, fmt.Errorf("hek too large (%d)", hekLen) } - i.memPool.Put(syncPoolData) - }() - - tmp := (*syncPoolData)[:hekLen] - // Always allocate the string separately so it does not hold the backing - // buffer that might be larger than needed - numCopied := copy(tmp, buf[vms.hek.hek_key:]) - if hekLen > uint32(numCopied) { - err := i.rm.Read(addr+libpf.Address(vms.hek.hek_key+uint(numCopied)), tmp[numCopied:]) - if err != nil { - return "", err + if err := i.rm.Read(addr+hekInitialRead, buf[hekInitialRead:]); err != nil { + return libpf.NullString, err } } - s := string(tmp) + + s := unsafe.String(unsafe.SliceData(buf[vms.hek.hek_key:]), hekLen) if !util.IsValidString(s) { log.Debugf("Extracted invalid hek string at 0x%x '%v'", addr, []byte(s)) - return "", fmt.Errorf("extracted invalid hek string at 0x%x", addr) + return libpf.NullString, fmt.Errorf("extracted invalid hek string at 0x%x", addr) } - i.addrToHEK.Add(addr, s) + value := libpf.Intern(s) + i.addrToHEK.Add(addr, value) - return s, nil + return value, nil } -func (i *perlInstance) getHVName(hvAddr libpf.Address) (string, error) { +func (i *perlInstance) getHVName(hvAddr libpf.Address) (libpf.String, error) { if hvAddr == 0 { - return "", nil + return libpf.NullString, nil } vms := &i.d.vmStructs hv := make([]byte, vms.sv.sizeof) if err := i.rm.Read(hvAddr, hv); err != nil { - return "", err + return libpf.NullString, err } hvFlags := npsr.Uint32(hv, vms.sv.sv_flags) if hvFlags&SVt_MASK != SVt_PVHV { - return "", errors.New("not a HV") + return libpf.NullString, errors.New("not a HV") } xpvhvAddr := npsr.Ptr(hv, vms.sv.sv_any) @@ -284,14 +265,14 @@ func (i *perlInstance) getHVName(hvAddr libpf.Address) (string, error) { end := i.rm.Uint64(xpvhvAddr + libpf.Address(vms.xpvhv.xhv_max)) xpvhvAuxAddr := arrayAddr + libpf.Address((end+1)*uint64(vms.xpvhv_aux.pointer_size)) if err := i.rm.Read(xpvhvAuxAddr, xpvhvAux); err != nil { - return "", err + return libpf.NullString, err } } else { // In Perl 5.36.x.XPVHV got replaced with xpvhv_with_aux to hold this information. // https://github.com/Perl/perl5/commit/94ee6ed79dbca73d0345b745534477e4017fb990 if err := i.rm.Read(xpvhvAddr+libpf.Address(vms.xpvhv_with_aux.xpvhv_aux), xpvhvAux); err != nil { - return "", err + return libpf.NullString, err } } @@ -332,12 +313,12 @@ func (i *perlInstance) getGV(gvAddr libpf.Address, nameOnly bool) (libpf.String, // Follow the GV's "body" pointer to get the function name xpvgvAddr := i.rm.Ptr(gvAddr + libpf.Address(vms.sv.sv_any)) hekAddr := i.rm.Ptr(xpvgvAddr + libpf.Address(vms.xpvgv.xivu_namehek)) - gvName, err := i.getHEK(hekAddr) + value, err := i.getHEK(hekAddr) if err != nil { return libpf.NullString, err } - if !nameOnly && gvName != "" { + if !nameOnly && value != libpf.NullString { stashAddr := i.rm.Ptr(xpvgvAddr + libpf.Address(vms.xpvgv.xgv_stash)) packageName, err := i.getHVName(stashAddr) if err != nil { @@ -345,14 +326,14 @@ func (i *perlInstance) getGV(gvAddr libpf.Address, nameOnly bool) (libpf.String, } // Build the qualified name - if packageName == "" { + if packageName == libpf.NullString { // per Perl_gv_fullname4 - packageName = "__ANON__" + value = libpf.Intern("__ANON__::" + value.String()) + } else { + value = libpf.Intern(packageName.String() + "::" + value.String()) } - gvName = packageName + "::" + gvName } - value := libpf.Intern(gvName) i.addrToGV.Add(gvAddr, value) return value, nil } diff --git a/interpreter/php/php.go b/interpreter/php/php.go index cae88537a..f657f3a37 100644 --- a/interpreter/php/php.go +++ b/interpreter/php/php.go @@ -180,7 +180,7 @@ func determinePHPVersion(ef *pfelf.File) (uint, error) { if zeroIdx < 0 { continue } - version, err := versionExtract(string(rodata[idx : idx+zeroIdx])) + version, err := versionExtract(unsafe.String(unsafe.SliceData(rodata[idx:]), zeroIdx)) if err != nil { continue } From ea177c46438c9a05d73564ba751ccbf7d0b79580 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Wed, 6 Aug 2025 09:52:04 +0200 Subject: [PATCH 06/24] [chore] drop debug and DebugTracer (#677) Signed-off-by: Florian Lehner --- Makefile | 10 +--------- internal/controller/controller.go | 2 +- interpreter/golabels/test/main_test.go | 2 +- testutils/helpers.go | 2 +- tracer/ebpf_integration_test.go | 4 ++-- tracer/tracer.go | 6 +++--- 6 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index b87768f0d..7364e37da 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: all all-common clean ebpf generate test test-deps \ test-junit protobuf docker-image agent legal integration-test-binaries \ - codespell lint linter-version debug debug-agent ebpf-profiler format-ebpf \ + codespell lint linter-version ebpf-profiler format-ebpf \ rust-components rust-targets rust-tests vanity-import-check vanity-import-fix SHELL := /usr/bin/env bash @@ -54,10 +54,6 @@ JUNIT_OUT_DIR ?= /tmp/testresults all: ebpf-profiler -debug: GO_TAGS := $(GO_TAGS),debugtracer -debug: EBPF_FLAGS += debug -debug: all - # Removes the go build cache and binaries in the current project clean: @go clean -cache -i @@ -156,10 +152,6 @@ agent: docker run -v "$$PWD":/agent -it --rm --user $(shell id -u):$(shell id -g) otel/opentelemetry-ebpf-profiler-dev:latest \ "make TARGET_ARCH=$(TARGET_ARCH) VERSION=$(VERSION) REVISION=$(REVISION) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP)" -debug-agent: - docker run -v "$$PWD":/agent -it --rm --user $(shell id -u):$(shell id -g) otel/opentelemetry-ebpf-profiler-dev:latest \ - "make TARGET_ARCH=$(TARGET_ARCH) VERSION=$(VERSION) REVISION=$(REVISION) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) debug" - legal: @go install github.com/google/go-licenses@latest @go-licenses save --force . --save_path=LICENSES diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 3e5a8a429..95bde52bf 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -92,7 +92,7 @@ func (c *Controller) Start(ctx context.Context) error { SamplesPerSecond: c.config.SamplesPerSecond, MapScaleFactor: int(c.config.MapScaleFactor), KernelVersionCheck: !c.config.NoKernelVersionCheck, - DebugTracer: c.config.VerboseMode, + VerboseMode: c.config.VerboseMode, BPFVerifierLogLevel: uint32(c.config.BpfVerifierLogLevel), ProbabilisticInterval: c.config.ProbabilisticInterval, ProbabilisticThreshold: c.config.ProbabilisticThreshold, diff --git a/interpreter/golabels/test/main_test.go b/interpreter/golabels/test/main_test.go index 301349b81..b62e0acbb 100644 --- a/interpreter/golabels/test/main_test.go +++ b/interpreter/golabels/test/main_test.go @@ -51,7 +51,7 @@ func TestGoLabels(t *testing.T) { ProbabilisticInterval: 100, ProbabilisticThreshold: 100, OffCPUThreshold: uint32(math.MaxUint32 / 100), - DebugTracer: true, + VerboseMode: true, }) require.NoError(t, err) diff --git a/testutils/helpers.go b/testutils/helpers.go index f4377f359..648c3f483 100644 --- a/testutils/helpers.go +++ b/testutils/helpers.go @@ -47,7 +47,7 @@ func StartTracer(ctx context.Context, t *testing.T, et tracertypes.IncludedTrace ProbabilisticInterval: 100, ProbabilisticThreshold: 100, OffCPUThreshold: uint32(math.MaxUint32 / 100), - DebugTracer: true, + VerboseMode: true, }) require.NoError(t, err) diff --git a/tracer/ebpf_integration_test.go b/tracer/ebpf_integration_test.go index 8fb8700f9..6c365fe8a 100644 --- a/tracer/ebpf_integration_test.go +++ b/tracer/ebpf_integration_test.go @@ -127,7 +127,7 @@ func TestTraceTransmissionAndParsing(t *testing.T) { ProbabilisticInterval: 100, ProbabilisticThreshold: 100, OffCPUThreshold: 1 * math.MaxUint32, - DebugTracer: true, + VerboseMode: true, }) require.NoError(t, err) @@ -247,7 +247,7 @@ func TestAllTracers(t *testing.T) { ProbabilisticInterval: 100, ProbabilisticThreshold: 100, OffCPUThreshold: uint32(math.MaxUint32 / 100), - DebugTracer: true, + VerboseMode: true, }) require.NoError(t, err) } diff --git a/tracer/tracer.go b/tracer/tracer.go index f5687d9af..31adbf87f 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -128,11 +128,11 @@ type Config struct { FilterErrorFrames bool // KernelVersionCheck indicates whether the kernel version should be checked. KernelVersionCheck bool - // DebugTracer indicates whether to load the debug version of eBPF tracers. - DebugTracer bool // CollectCustomLabels determines whether to collect custom labels in // languages that support them. CollectCustomLabels bool + // VerboseMode indicates whether to enable verbose output of eBPF tracers. + VerboseMode bool // BPFVerifierLogLevel is the log level of the eBPF verifier output. BPFVerifierLogLevel uint32 // ProbabilisticInterval is the time interval for which probabilistic profiling will be enabled. @@ -286,7 +286,7 @@ func initializeMapsAndPrograms(kmod *kallsyms.Module, cfg *Config) ( return nil, nil, fmt.Errorf("failed to load specification for tracers: %v", err) } - if cfg.DebugTracer { + if cfg.VerboseMode { if err = coll.Variables["with_debug_output"].Set(uint32(1)); err != nil { return nil, nil, fmt.Errorf("failed to set debug output: %v", err) } From ff303d487a64921840716c018314852117cace77 Mon Sep 17 00:00:00 2001 From: Tolya Korniltsev Date: Wed, 6 Aug 2025 19:53:49 +0700 Subject: [PATCH 07/24] tools/coredump: allow running tests on darwin (#621) --- .../workflows/unit-test-on-pull-request.yml | 22 ++++ go.mod | 2 +- go.sum | 4 +- libpf/pfelf/machine_amd64.go | 10 ++ libpf/pfelf/machine_arm64.go | 10 ++ process/debug_amd64.go | 4 +- process/debug_arm64.go | 4 +- process/{debug.go => debug_linux.go} | 2 + process/debug_other.go | 19 ++++ process/process.go | 2 +- process/process_test.go | 4 + processmanager/ebpf/asyncupdate.go | 1 + processmanager/ebpf/ebpf.go | 92 ++--------------- processmanager/ebpf/ebpf_test.go | 1 + processmanager/ebpfapi/ebpf.go | 95 ++++++++++++++++++ processmanager/{ebpf => ebpfapi}/types.go | 2 +- processmanager/execinfomanager/manager.go | 9 +- processmanager/manager.go | 5 +- processmanager/manager_test.go | 2 +- processmanager/types.go | 2 +- support/ebpf/luajit_tracer.ebpf.c | 6 +- support/ebpf/tracer.ebpf.amd64 | Bin 2830952 -> 2830952 bytes support/ebpf/tracer.ebpf.arm64 | Bin 2808368 -> 2808368 bytes tools/coredump/ebpfmaps.go | 2 +- 24 files changed, 190 insertions(+), 110 deletions(-) create mode 100644 libpf/pfelf/machine_amd64.go create mode 100644 libpf/pfelf/machine_arm64.go rename process/{debug.go => debug_linux.go} (99%) create mode 100644 process/debug_other.go create mode 100644 processmanager/ebpfapi/ebpf.go rename processmanager/{ebpf => ebpfapi}/types.go (81%) diff --git a/.github/workflows/unit-test-on-pull-request.yml b/.github/workflows/unit-test-on-pull-request.yml index 993d51cf0..dbb2f0265 100644 --- a/.github/workflows/unit-test-on-pull-request.yml +++ b/.github/workflows/unit-test-on-pull-request.yml @@ -137,6 +137,28 @@ jobs: name: integration-test-binaries-${{ matrix.target_arch }} path: support/*.test + coredump-test-macos: + name: Coredump tests (macOS) + runs-on: macos-latest + steps: + - name: Clone code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Set up Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version-file: go.mod + cache-dependency-path: go.sum + - name: Cache coredump modules + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: tools/coredump/modulecache + key: coredumps-arm64-${{ hashFiles('tools/coredump/testdata/*/*.json') }} + restore-keys: | + coredumps-arm64 + coredumps- + - name: Run coredump tests + run: GODEBUG=asyncpreemptoff=1 go test -v ./tools/coredump/ + integration-tests: name: Integration tests (v${{ matrix.kernel }} ${{ matrix.target_arch }}) runs-on: ubuntu-24.04 diff --git a/go.mod b/go.mod index 98e7a0663..c7ed7a11d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/klauspost/compress v1.18.0 github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d github.com/minio/sha256-simd v1.0.1 - github.com/parca-dev/oomprof v0.1.5-0.20250916114836-5d60b5355c17 + github.com/parca-dev/oomprof v0.1.5-0.20250918220314-dbb0077f3ff1 github.com/peterbourgon/ff/v3 v3.4.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.11.1 diff --git a/go.sum b/go.sum index 87f39055c..4213fe878 100644 --- a/go.sum +++ b/go.sum @@ -169,8 +169,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/parca-dev/oomprof v0.1.5-0.20250916114836-5d60b5355c17 h1:amrSTgX5V7nE0EIwgaSzIj5W8v6in2a3J8ez9tNNJKM= -github.com/parca-dev/oomprof v0.1.5-0.20250916114836-5d60b5355c17/go.mod h1:iqI6XrmiNWOa8m2vEIKo+GtQrqbWCMLFpBWuk8RuAPs= +github.com/parca-dev/oomprof v0.1.5-0.20250918220314-dbb0077f3ff1 h1:HsU7HCoVefO2W1VbiZhU6137hokWajurWvrXgIFthws= +github.com/parca-dev/oomprof v0.1.5-0.20250918220314-dbb0077f3ff1/go.mod h1:iqI6XrmiNWOa8m2vEIKo+GtQrqbWCMLFpBWuk8RuAPs= 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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/libpf/pfelf/machine_amd64.go b/libpf/pfelf/machine_amd64.go new file mode 100644 index 000000000..b0c78ed70 --- /dev/null +++ b/libpf/pfelf/machine_amd64.go @@ -0,0 +1,10 @@ +//go:build amd64 + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pfelf // import "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" + +import "debug/elf" + +const CurrentMachine = elf.EM_X86_64 diff --git a/libpf/pfelf/machine_arm64.go b/libpf/pfelf/machine_arm64.go new file mode 100644 index 000000000..84a72e40b --- /dev/null +++ b/libpf/pfelf/machine_arm64.go @@ -0,0 +1,10 @@ +//go:build arm64 + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pfelf // import "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" + +import "debug/elf" + +const CurrentMachine = elf.EM_AARCH64 diff --git a/process/debug_amd64.go b/process/debug_amd64.go index 769ce8772..bcd0ea1e5 100644 --- a/process/debug_amd64.go +++ b/process/debug_amd64.go @@ -1,4 +1,4 @@ -//go:build amd64 +//go:build linux && amd64 // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 @@ -11,8 +11,6 @@ import ( "fmt" ) -const currentMachine = elf.EM_X86_64 - func (sp *ptraceProcess) getThreadInfo(tid int) (ThreadInfo, error) { prStatus := make([]byte, 28*8) if err := ptraceGetRegset(tid, int(elf.NT_PRSTATUS), prStatus); err != nil { diff --git a/process/debug_arm64.go b/process/debug_arm64.go index 17024e252..30a91393e 100644 --- a/process/debug_arm64.go +++ b/process/debug_arm64.go @@ -1,4 +1,4 @@ -//go:build arm64 +//go:build linux && arm64 // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 @@ -11,8 +11,6 @@ import ( "fmt" ) -const currentMachine = elf.EM_AARCH64 - func (sp *ptraceProcess) GetMachineData() MachineData { pacMask := make([]byte, 16) _ = ptraceGetRegset(int(sp.pid), int(NT_ARM_PAC_MASK), pacMask) diff --git a/process/debug.go b/process/debug_linux.go similarity index 99% rename from process/debug.go rename to process/debug_linux.go index c3efa6573..cf5c498a7 100644 --- a/process/debug.go +++ b/process/debug_linux.go @@ -1,3 +1,5 @@ +//go:build linux + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 diff --git a/process/debug_other.go b/process/debug_other.go new file mode 100644 index 000000000..8234b6041 --- /dev/null +++ b/process/debug_other.go @@ -0,0 +1,19 @@ +//go:build !linux + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package process // import "go.opentelemetry.io/ebpf-profiler/process" + +import ( + "fmt" + "runtime" + + "go.opentelemetry.io/ebpf-profiler/libpf" +) + +// NewPtrace is the stub implementation, allowing to compile the process +// package on non linux systems, always failing at runtime with an error if used. +func NewPtrace(_ libpf.PID) (Process, error) { + return nil, fmt.Errorf("unsupported os %s", runtime.GOOS) +} diff --git a/process/process.go b/process/process.go index e39159a25..ac9c91351 100644 --- a/process/process.go +++ b/process/process.go @@ -72,7 +72,7 @@ func (sp *systemProcess) PID() libpf.PID { } func (sp *systemProcess) GetMachineData() MachineData { - return MachineData{Machine: currentMachine} + return MachineData{Machine: pfelf.CurrentMachine} } func trimMappingPath(path string) string { diff --git a/process/process_test.go b/process/process_test.go index 57bd87949..b6313550f 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -6,6 +6,7 @@ package process import ( "debug/elf" "os" + "runtime" "strings" "testing" @@ -113,6 +114,9 @@ func TestParseMappings(t *testing.T) { } func TestNewPIDOfSelf(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skipf("unsupported os %s", runtime.GOOS) + } pid := libpf.PID(os.Getpid()) pr := New(pid, pid) assert.NotNil(t, pr) diff --git a/processmanager/ebpf/asyncupdate.go b/processmanager/ebpf/asyncupdate.go index 37ef923d9..944200f5b 100644 --- a/processmanager/ebpf/asyncupdate.go +++ b/processmanager/ebpf/asyncupdate.go @@ -10,6 +10,7 @@ import ( cebpf "github.com/cilium/ebpf" log "github.com/sirupsen/logrus" + "go.opentelemetry.io/ebpf-profiler/host" ) diff --git a/processmanager/ebpf/ebpf.go b/processmanager/ebpf/ebpf.go index 4d5526f2f..c2baec600 100644 --- a/processmanager/ebpf/ebpf.go +++ b/processmanager/ebpf/ebpf.go @@ -15,16 +15,17 @@ import ( cebpf "github.com/cilium/ebpf" "github.com/cilium/ebpf/features" log "github.com/sirupsen/logrus" + "golang.org/x/exp/constraints" + "go.opentelemetry.io/ebpf-profiler/host" - "go.opentelemetry.io/ebpf-profiler/interpreter" "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/lpm" "go.opentelemetry.io/ebpf-profiler/metrics" sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" + "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" "go.opentelemetry.io/ebpf-profiler/rlimit" "go.opentelemetry.io/ebpf-profiler/support" "go.opentelemetry.io/ebpf-profiler/util" - "golang.org/x/exp/constraints" ) const ( @@ -35,55 +36,6 @@ const ( updatePoolQueueCap = 8 ) -// EbpfHandler provides the functionality to interact with eBPF maps. -type EbpfHandler interface { - // Embed interpreter.EbpfHandler as subset of this interface. - interpreter.EbpfHandler - - // RemoveReportedPID removes a PID from the reported_pids eBPF map. - RemoveReportedPID(pid libpf.PID) - - // UpdateUnwindInfo writes UnwindInfo to given unwind info array index - UpdateUnwindInfo(index uint16, info sdtypes.UnwindInfo) error - - // UpdateExeIDToStackDeltas defines a function that updates the eBPF map exe_id_to_stack_deltas - // for host.FileID with the elements of StackDeltaEBPF. It returns the mapID used. - UpdateExeIDToStackDeltas(fileID host.FileID, deltas []StackDeltaEBPF) (uint16, error) - - // DeleteExeIDToStackDeltas defines a function that removes the entries from the outer eBPF - // map exe_id_to_stack_deltas and its associated inner map entries. - DeleteExeIDToStackDeltas(fileID host.FileID, mapID uint16) error - - // UpdateStackDeltaPages defines a function that updates the mapping in a eBPF map from - // a FileID and page to its stack delta lookup information. - UpdateStackDeltaPages(fileID host.FileID, numDeltasPerPage []uint16, - mapID uint16, firstPageAddr uint64) error - - // DeleteStackDeltaPage defines a function that removes the element specified by fileID and page - // from the eBPF map. - DeleteStackDeltaPage(fileID host.FileID, page uint64) error - - // UpdatePidPageMappingInfo defines a function that updates the eBPF map - // pid_page_to_mapping_info with the given pidAndPage and fileIDAndOffset encoded values - // as key/value pair. - UpdatePidPageMappingInfo(pid libpf.PID, prefix lpm.Prefix, fileID, bias uint64) error - - // DeletePidPageMappingInfo removes the elements specified by prefixes from eBPF map - // pid_page_to_mapping_info and returns the number of elements removed. - DeletePidPageMappingInfo(pid libpf.PID, prefixes []lpm.Prefix) (int, error) - - // CollectMetrics returns gathered errors for changes to eBPF maps. - CollectMetrics() []metrics.Metric - - // SupportsGenericBatchOperations returns true if the kernel supports eBPF batch operations - // on hash and array maps. - SupportsGenericBatchOperations() bool - - // SupportsLPMTrieBatchOperations returns true if the kernel supports eBPF batch operations - // on LPM trie maps. - SupportsLPMTrieBatchOperations() bool -} - type ebpfMapsImpl struct { // Interpreter related eBPF maps InterpreterOffsets *cebpf.Map `name:"interpreter_offsets"` @@ -116,14 +68,14 @@ type ebpfMapsImpl struct { } // Compile time check to make sure ebpfMapsImpl satisfies the interface . -var _ EbpfHandler = &ebpfMapsImpl{} +var _ ebpfapi.EbpfHandler = &ebpfMapsImpl{} // LoadMaps checks if the needed maps for the process manager are available // and loads their references into a package-internal structure. // // It further spawns background workers for deferred map updates; the given // context can be used to terminate them on shutdown. -func LoadMaps(ctx context.Context, maps map[string]*cebpf.Map) (EbpfHandler, error) { +func LoadMaps(ctx context.Context, maps map[string]*cebpf.Map) (ebpfapi.EbpfHandler, error) { impl := &ebpfMapsImpl{} impl.errCounter = make(map[metrics.MetricID]int64) @@ -175,7 +127,7 @@ func (impl *ebpfMapsImpl) CoredumpTest() bool { // UpdateInterpreterOffsets adds the given moduleRanges to the eBPF map interpreterOffsets. func (impl *ebpfMapsImpl) UpdateInterpreterOffsets(ebpfProgIndex uint16, fileID host.FileID, offsetRanges []util.Range) error { - key, value, err := InterpreterOffsetKeyValue(ebpfProgIndex, fileID, offsetRanges) + key, value, err := ebpfapi.InterpreterOffsetKeyValue(ebpfProgIndex, fileID, offsetRanges) if err != nil { return err } @@ -187,35 +139,6 @@ func (impl *ebpfMapsImpl) UpdateInterpreterOffsets(ebpfProgIndex uint16, fileID return nil } -func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID, - offsetRanges []util.Range) (key uint64, value support.OffsetRange, err error) { - rLen := len(offsetRanges) - if rLen < 1 || rLen > 2 { - return 0, support.OffsetRange{}, fmt.Errorf("invalid ranges %v", offsetRanges) - } - // The keys of this map are executable-id-and-offset-into-text entries, and - // the offset_range associated with them gives the precise area in that page - // where the main interpreter loop is located. This is required to unwind - // nicely from native code into interpreted code. - key = uint64(fileID) - first := offsetRanges[0] - value = support.OffsetRange{ - Lower_offset1: first.Start, - Upper_offset1: first.End, - Program_index: ebpfProgIndex, - } - if len(offsetRanges) == 2 { - // Fields {lower,upper}_offset2 may be used to specify an optional second range - // of an interpreter function. This may be useful if the interpreter function - // consists of two non-contiguous memory ranges, which may happen due to Hot/Cold - // split compiler optimization - second := offsetRanges[1] - value.Lower_offset2 = second.Start - value.Upper_offset2 = second.End - } - return key, value, nil -} - // getInterpreterTypeMap returns the eBPF map for the given typ // or an error if typ is not supported. func (impl *ebpfMapsImpl) getInterpreterTypeMap(typ libpf.InterpreterType) (*cebpf.Map, error) { @@ -502,7 +425,8 @@ func (impl *ebpfMapsImpl) UpdateUnwindInfo(index uint16, info sdtypes.UnwindInfo // UpdateExeIDToStackDeltas creates a nested map for fileID in the eBPF map exeIDTostack_deltas // and inserts the elements of the deltas array in this nested map. Returns mapID or error. -func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, deltas []StackDeltaEBPF) ( +func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, + deltas []ebpfapi.StackDeltaEBPF) ( uint16, error) { numDeltas := len(deltas) mapID, err := getMapID(uint32(numDeltas)) diff --git a/processmanager/ebpf/ebpf_test.go b/processmanager/ebpf/ebpf_test.go index fb33e6022..bc355fd3b 100644 --- a/processmanager/ebpf/ebpf_test.go +++ b/processmanager/ebpf/ebpf_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/ebpf-profiler/support" ) diff --git a/processmanager/ebpfapi/ebpf.go b/processmanager/ebpfapi/ebpf.go new file mode 100644 index 000000000..48c60907b --- /dev/null +++ b/processmanager/ebpfapi/ebpf.go @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package ebpfapi // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" + +import ( + "fmt" + + "go.opentelemetry.io/ebpf-profiler/host" + "go.opentelemetry.io/ebpf-profiler/interpreter" + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/lpm" + "go.opentelemetry.io/ebpf-profiler/metrics" + "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" + "go.opentelemetry.io/ebpf-profiler/support" + "go.opentelemetry.io/ebpf-profiler/util" +) + +// EbpfHandler provides the functionality to interact with eBPF maps. +type EbpfHandler interface { + // Embed interpreter.EbpfHandler as subset of this interface. + interpreter.EbpfHandler + + // RemoveReportedPID removes a PID from the reported_pids eBPF map. + RemoveReportedPID(pid libpf.PID) + + // UpdateUnwindInfo writes UnwindInfo to given unwind info array index + UpdateUnwindInfo(index uint16, info stackdeltatypes.UnwindInfo) error + + // UpdateExeIDToStackDeltas defines a function that updates the eBPF map exe_id_to_stack_deltas + // for host.FileID with the elements of StackDeltaEBPF. It returns the mapID used. + UpdateExeIDToStackDeltas(fileID host.FileID, deltas []StackDeltaEBPF) (uint16, error) + + // DeleteExeIDToStackDeltas defines a function that removes the entries from the outer eBPF + // map exe_id_to_stack_deltas and its associated inner map entries. + DeleteExeIDToStackDeltas(fileID host.FileID, mapID uint16) error + + // UpdateStackDeltaPages defines a function that updates the mapping in a eBPF map from + // a FileID and page to its stack delta lookup information. + UpdateStackDeltaPages(fileID host.FileID, numDeltasPerPage []uint16, + mapID uint16, firstPageAddr uint64) error + + // DeleteStackDeltaPage defines a function that removes the element specified by fileID and page + // from the eBPF map. + DeleteStackDeltaPage(fileID host.FileID, page uint64) error + + // UpdatePidPageMappingInfo defines a function that updates the eBPF map + // pid_page_to_mapping_info with the given pidAndPage and fileIDAndOffset encoded values + // as key/value pair. + UpdatePidPageMappingInfo(pid libpf.PID, prefix lpm.Prefix, fileID, bias uint64) error + + // DeletePidPageMappingInfo removes the elements specified by prefixes from eBPF map + // pid_page_to_mapping_info and returns the number of elements removed. + DeletePidPageMappingInfo(pid libpf.PID, prefixes []lpm.Prefix) (int, error) + + // CollectMetrics returns gathered errors for changes to eBPF maps. + CollectMetrics() []metrics.Metric + + // SupportsGenericBatchOperations returns true if the kernel supports eBPF batch operations + // on hash and array maps. + SupportsGenericBatchOperations() bool + + // SupportsLPMTrieBatchOperations returns true if the kernel supports eBPF batch operations + // on LPM trie maps. + SupportsLPMTrieBatchOperations() bool +} + +func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID, + offsetRanges []util.Range) (key uint64, value support.OffsetRange, err error) { + rLen := len(offsetRanges) + if rLen < 1 || rLen > 2 { + return 0, support.OffsetRange{}, fmt.Errorf("invalid ranges %v", offsetRanges) + } + // The keys of this map are executable-id-and-offset-into-text entries, and + // the offset_range associated with them gives the precise area in that page + // where the main interpreter loop is located. This is required to unwind + // nicely from native code into interpreted code. + key = uint64(fileID) + first := offsetRanges[0] + value = support.OffsetRange{ + Lower_offset1: first.Start, + Upper_offset1: first.End, + Program_index: ebpfProgIndex, + } + if len(offsetRanges) == 2 { + // Fields {lower,upper}_offset2 may be used to specify an optional second range + // of an interpreter function. This may be useful if the interpreter function + // consists of two non-contiguous memory ranges, which may happen due to Hot/Cold + // split compiler optimization + second := offsetRanges[1] + value.Lower_offset2 = second.Start + value.Upper_offset2 = second.End + } + return key, value, nil +} diff --git a/processmanager/ebpf/types.go b/processmanager/ebpfapi/types.go similarity index 81% rename from processmanager/ebpf/types.go rename to processmanager/ebpfapi/types.go index bd5de07d0..2f802c852 100644 --- a/processmanager/ebpf/types.go +++ b/processmanager/ebpfapi/types.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package ebpf // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" +package ebpfapi // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" // StackDeltaEBPF represents stack deltas preprocessed by the ProcessManager which are // then loaded to the eBPF map. This is Go equivalent of 'struct StackDelta' in eBPF types.h. diff --git a/processmanager/execinfomanager/manager.go b/processmanager/execinfomanager/manager.go index f4f6de33a..4cea0ec1b 100644 --- a/processmanager/execinfomanager/manager.go +++ b/processmanager/execinfomanager/manager.go @@ -9,11 +9,8 @@ import ( "os" "time" - log "github.com/sirupsen/logrus" - "go.opentelemetry.io/ebpf-profiler/libpf" - "go.opentelemetry.io/ebpf-profiler/tracer/types" - lru "github.com/elastic/go-freelru" + log "github.com/sirupsen/logrus" "go.opentelemetry.io/ebpf-profiler/host" "go.opentelemetry.io/ebpf-profiler/interpreter" @@ -30,14 +27,16 @@ import ( "go.opentelemetry.io/ebpf-profiler/interpreter/php" "go.opentelemetry.io/ebpf-profiler/interpreter/python" "go.opentelemetry.io/ebpf-profiler/interpreter/ruby" + "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" "go.opentelemetry.io/ebpf-profiler/libpf/xsync" "go.opentelemetry.io/ebpf-profiler/metrics" "go.opentelemetry.io/ebpf-profiler/nativeunwind" sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" - pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" + pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" "go.opentelemetry.io/ebpf-profiler/support" "go.opentelemetry.io/ebpf-profiler/tpbase" + "go.opentelemetry.io/ebpf-profiler/tracer/types" "go.opentelemetry.io/ebpf-profiler/util" ) diff --git a/processmanager/manager.go b/processmanager/manager.go index df0fc13b8..edb9b37cd 100644 --- a/processmanager/manager.go +++ b/processmanager/manager.go @@ -13,8 +13,6 @@ import ( lru "github.com/elastic/go-freelru" log "github.com/sirupsen/logrus" - "go.opentelemetry.io/ebpf-profiler/tracer/types" - "go.opentelemetry.io/ebpf-profiler/host" "go.opentelemetry.io/ebpf-profiler/interpreter" "go.opentelemetry.io/ebpf-profiler/interpreter/apmint" @@ -24,10 +22,11 @@ import ( "go.opentelemetry.io/ebpf-profiler/nativeunwind" sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" "go.opentelemetry.io/ebpf-profiler/periodiccaller" - pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" + pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" eim "go.opentelemetry.io/ebpf-profiler/processmanager/execinfomanager" "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/times" + "go.opentelemetry.io/ebpf-profiler/tracer/types" "go.opentelemetry.io/ebpf-profiler/traceutil" "go.opentelemetry.io/ebpf-profiler/util" ) diff --git a/processmanager/manager_test.go b/processmanager/manager_test.go index f147af7ea..cf734cfae 100644 --- a/processmanager/manager_test.go +++ b/processmanager/manager_test.go @@ -24,7 +24,7 @@ import ( "go.opentelemetry.io/ebpf-profiler/nativeunwind" sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" "go.opentelemetry.io/ebpf-profiler/process" - pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" + pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" "go.opentelemetry.io/ebpf-profiler/remotememory" "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/support" diff --git a/processmanager/types.go b/processmanager/types.go index f70075b95..5e1975d14 100644 --- a/processmanager/types.go +++ b/processmanager/types.go @@ -14,7 +14,7 @@ import ( "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/libpf/pfelf" "go.opentelemetry.io/ebpf-profiler/metrics" - pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" + pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" eim "go.opentelemetry.io/ebpf-profiler/processmanager/execinfomanager" "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/times" diff --git a/support/ebpf/luajit_tracer.ebpf.c b/support/ebpf/luajit_tracer.ebpf.c index 5aa948f08..a082a8f06 100644 --- a/support/ebpf/luajit_tracer.ebpf.c +++ b/support/ebpf/luajit_tracer.ebpf.c @@ -41,8 +41,6 @@ bpf_map_def SEC("maps") luajit_procs = { __val; \ }) -typedef signed long long intptr_t; - #define L_PART_OFFSET 0x10 #define CFRAME_SIZE_JIT 0x60 // (gdb) p/x sizeof(GCproto) @@ -110,9 +108,9 @@ enum { LJ_CONT_TAILCALL, LJ_CONT_FFI_CALLBACK }; /* Special continuations. */ #define CFRAME_RESUME 1 #define CFRAME_UNWIND_FF 2 /* Only used in unwinder. */ -#define CFRAME_RAWMASK (~(intptr_t)(CFRAME_RESUME | CFRAME_UNWIND_FF)) +#define CFRAME_RAWMASK (~(s64)(CFRAME_RESUME | CFRAME_UNWIND_FF)) #define cframe_nres_addr(cf) (s32 *)(((char *)(cf)) + CFRAME_OFS_NRES) -#define cframe_raw(cf) ((void *)((intptr_t)(cf) & CFRAME_RAWMASK)) +#define cframe_raw(cf) ((void *)((s64)(cf) & CFRAME_RAWMASK)) #define cframe_pc_addr(cf) (void *)(((char *)(cf)) + CFRAME_OFS_PC) #define cframe_L_addr(cf) (void *)(((char *)(cf)) + CFRAME_OFS_L) #define cframe_prev(cf) deref((void **)(((char *)(cf)) + CFRAME_OFS_PREV)) diff --git a/support/ebpf/tracer.ebpf.amd64 b/support/ebpf/tracer.ebpf.amd64 index 464b385bd98b2e81cc5f2461291da8cdea7b1bc7..7ebb10ded32e55a55bc62fa3530841ccfce11173 100644 GIT binary patch delta 7861 zcmeI#?{8bx83*tuH@?32HoqL;hP2HZCruhB3yoV+5pMY%Xy!brloaa9G`u;d)?eeL*HMEpd=V(s7<8yS!?--8hSk4xw!D)1woMxxR z2{^6JEzYgZZGW||FV-D*|F>xG?`pJYoQS^t6;aEti9$UVO-MF++{5qNoueU(avYw) zFe7YHp8XoE4_j1FT&lABxA*PWlj9bJG;y>Ky)X_@5ZxyFA}~#vZCm_Lx-g zi+ERpC<>P(AArk}Etrg(<2{?~8O%3ACIuCT6)(V_;hK6scd=ma>Ar>t@_PT>J6;kv zQ0@7a5A2h5qgM+|GW@J8p6Q@W=Lkt?rV0@)NtWE9j?6hnW5iy0`WCX z8TL~&DAHc@(hmD+ihbL2@PePRigWBoy#8~3T2NeIJLn(8qg-Oo!wt)RT4i5=lMmwA zUXubtaE0AFg9)p+JnVbmw#$BsbcuqG!cA*_8ezW*54GdH$NmB~CVVuZIL7{dhLWT| z>Z8=nz=qR`*V)^XNEr7~v|ALg>2b;1&>xfRjgPZqn6E$Oqx21PAaf%i1_XAB1H6L% zTN6HtC>~*-M*qDI3X*KVQ;JjU|6=@>H&DQ)_aR^)kl{dc3I$|+lvg}gW%uOg_yV26 zuwGGT3e$|cH7Y7Dv5V+$!slpJvH@4vZO`Md2Q@NxiF}(5D9*4CV|=ptQH}B(cm@Fj z-?s(!J8)nU1+lNfR$QYJ+jk7(pVDYeaYb>gPZTtO@z#5;oub8d;!^yYm`--Q{3J!(%yyf*0M%n z#S!)d`U96V8j+mz0#Obe!+-;qHHt}YfD?*S?DO6M*HED1oZ<>wb8y~RUzh`;;C=^> zqLtMscr&o!FguP3#t9UtcwBLT{d;db)l*sVs^Y|tMFAI`Nt{6*m;6S6C<3P?6P%Ik z_2<~zU&H}V;SN+>sIoizcl^`d4KOH<`!h^y`VQyC{)Lxjxt_+^GhYAYdWuQ5;1v7K zjq!MZ+3&%68BeC-Imwz{c6VPy9anr5yo1l*%L9kl#!L9pT|=FUql(MyC%y5cQ$_K* z;`DB@e3;d-^g4IZe!A7CQ+7xc@g9;K^0|k;updtbFd!_#Pri(|V53eZ`+0av$e?z~ z-EdHHFFYaHg5&J7m~RXllvbRP{De0kzDE?$mnP~T!-APT3Ga$xfy~arcMR*4W`717 z5uBIe9Q$ABA2Hq8Wvm2Eyreih^a|GLjd)2(w%}Fv173eXqjmOUaMPH3)k||wr;)v) z+BCuj9AjVi79m(pBor^O#VzL^`qF+JTba{e+Gko5e#-0<*^0>a^MpI|6?QldzQr6K z_#M8@Fx@4FO~5;jyDMMWoqIAm1v&gjgxj(@h1qA}rkqYu_9b{M@9zE@r;yiaN(vir zf<62y?&?!IWfbSC?9P5|zq|K}pAr#K)DcW;fYXYzKN0;2^bcP1yGP6R`DD>Ybhn8A z**hE@P@H40qQ9k}Q(kftJjY%~|Mqh_71%>ay|tuMk==0|S7k}3k~DtrR!k^!prsy9 z(=whW#dMD-Q1rW3zp*cLy@CH~zXvC8hsCHwx`mbkVh_V!)g7P+=1u5F2HTVnCbtZj)m zx6HLIacxU{4C{wtZA;uQ-VL=a@u0X6wJmXNOI+I$*S5s;L)sGWeK~U7Mr%X69jzU0 sE7~@+?PwinKSJAq)`=EG>q6^B+lkhL){C|atq(1P);}M)J`mpbf2q7OqW}N^ delta 7861 zcmeI#?{8bx83*tuH@?2U%`XRXL)vDIlal7gOyhPGc~YPWYavKgv9xZ=i>)dWg&zt0mX<)>5H;@w9^c9tyc;d$RfY!i%6?FPphW9pwqr5 z=Sh2f{)EX<&Wq1;p7Y#ufBKx|tM4`~qov&@*KnI%pKH2)*Kz}{?XGiM+*Y^EZFf7| zpxf!*;oj+XopNrTX*%Zp_l&c%zssf=5q z-%_$D$$kM2Ua=^xcmU2Se$Ymsz=5|hz^YhO(p=FTZnj2bF9wZ-gC~QsaSw!+GLr3q0GRZ043OefPaMkq+$!sYMy5gJ%?v+Uw{gli<&d- z%C+k~?`%sQ!VN@FT)R0G=}iQv#4f-Or*T6S?}V$0JK!37v=it4JZ@-*theSM`zLsS zOc&Qm@f{)%RRX5#EuD7`PrikD;981#1%8FPa9MMOy;?VqdDMAf?$6F^O_Rj{Xv>S9z2ivD&7qDuwNC&Eidfcy6THmiTQ(eLviN%}>#OIpM=_Tg^`s>{sA1y!%t^&tWU>qiM}?_J3w6Mdm>t zrEdpTozc9)-k3tcgpZ~MnhSOIPW%L4T@-v zvZv7>ylT*d;*;qw=D;9<7)Tl~`bOq$RLgWWbs%c)) zoY^Xu4|67#UKcJqPj$AMG(RRM@d1kLZT0qk?mU+2K|(}^pLhvh!7h^m>=)s6Skv_= z9)LrN2jOYOHax>Vhw;|9MH$Um#gB`GnQd|cLm8rxJyGZ{e&L+$O#3OjLsl!H+KOYQqwNr+7pl%DCtFyQCjr0B_-lyb)GHvaB%CMipFzarM z@|ufv_AY+w{IkD+_169NVzKWMTja);xUnT}Y>DM3v#}*! z+cGz{#EmWS5v(7|jVxB t^=KQ=Hlp>SeGhFDS|3^ntsiXwZ8O^4XoF~5(1y^4(MA@cH%B8o{s$(2G{FD> diff --git a/support/ebpf/tracer.ebpf.arm64 b/support/ebpf/tracer.ebpf.arm64 index 27dfbd1153940dab5860d5110dc5ae09ba84da0d..bdf8175cc24f4b974dd5c93e24d8a8303abee2af 100644 GIT binary patch delta 7893 zcmeI#?{8bx83*tu*Z2Bf=SSnT+>nOSIBAl)Azjl_ri{VrKq?H2Ru3v>Si5?t121FL z47$2qHfP%fqAF7TqSN&d0)u#AK_sjRDIsENwso4Rs?E|OE!vo5P1P2mYE%<1f>g16 zPtFr^eEx*VklpvzZ8KO@yqWvLCDz^Lm^9i?SEE=LBhttmz z*&`t;vCqKP!4NHJuGZQADB)hnB|;Q6uVT4~Sadw<)FAhBNHD zF+aFuQ5kziMCs%T!(oC?ST{{iavw1= zDB~^95yj!E;=}NY;t)*s=6Yd=okqSLbts~FMDqgdHsPY;$6tNK-QRZ;5!4lbJ6{xm z!}Xs3@=^EIrm^cbt+dF6*Re1H*A(x8SJ~0$i6%^kZYUl%{q9F`6CfOEm1V@T5FBL> zbYOqSp_t}Go&9rL^Z``42_6zX+n4eK>!$r*{cE@SldlUFuXs&85 zY*DUV{2T86+)4bw5ER!giy}MH_=B-uh9Af}R8xEizM;4srgoX17r*=S4s~gcuwTOs zWKB6Vs(7mi#Fc4(u;}59#^Eq3TE$Fi8e8lEUW*#*ph#WSf(u?@#GC)j_-`sRx`z=rQaz(yd&fi>7Fn3UB#U1xtWg*VO| zmJP^rP5uaF?lGvSxy1ez=G*Z4DJ!<&B{ofA+X;hKG}knz2IYQntj}#ZW>A&`-$ua3 z`)r!+!{L(#<=HE6C~Z)I{co}UX&gxNg68OMx#2B8M*d}kVw=8mJ&@qQ<5*x<3`%Rx z)Y(7xgxk|`*`#n(*8L^Y+$sMpgmW_}&*9&Q?cfCazoPmqs%y?@US;q73G$Z>qCIk( z4Lj^3m=AwwP*`zJ1R@+biv@?T88oW61&(V@vVSi&Ts0`8IjgzC{uJw7d{5NaksRGm zp@Kncn}KyZL-IE`fhWa&1qaj|t+U^J5*HHJx6I*LkwyWkXs&9`+#$D_6&D)$)9eqB zA1WC%qj(g~D<-(0I42gAIIwjZ1?O<5Yp!UH4c8wa!M+#yZJ!vFWPeN4TMkf)eOk<4 z3s7d$xR40sIgnpZFsaBc!PbgN3!0be>`#6N7xKDE(Y^9qABZyW2>UZQaMPr?=7i=N z`}j|>p774nOka@oH|F!O_!RrZ!k3rxr2A}#X;E>XTvS4lubKY&C*2ddutm-Y=l%@; zC0i}(VrSvzDBc5#``{7918`b#2+pz>k#8Tw_pRoF;wQv{?0$KGoj=EuHjWQu_BY|d zI0~>Yzf%%x@UwH~&h7SHKa(K|gcY7-y8pR>_20JF^!v;AA z8f7LdN^m%bunlL}{}LZ1_`I0WTw%*w&Oe`aPoTB=DD7VA$l|Skm)ztM zsy&kR7r%>+#|x74MH%jW0q<@&%sv9|ob|gW-Jbn}8%n|clxRP#ce{VS91I|r@@DL*O?nM7I(T-03tlAJ$<`Qe*E z|LyO&f6P_zn!QKH-w@luQO!&2zhJ(7$)bwlHn__E81uJXwW!Aag-Nu%YSAit+e^4A zm+`@;tUs_F32Pj9EP$tJ*`n~h@<1Do)Y%{UzWb-%i$N+MlFNosw*_9Rv)}!U`_34i zr^0=5*>QfJO6)Y!>>X(5G}r3vH=lAp?k(Z-?XWEKmPmtF>+E-D+y@#ha-&5SeU)gL z8!d99MQ*gn@?U17MQ*gn1M*f7Epwwq#yg_XBFh`1(IRt)y3wOITI5EH+-Q-h(ISgZ zLbTNXPc8C+m%O}-(TQ;@Mi<6*jBbqEFm_kLwYh%RS{zCepagOpTkMZL2Lr+0KXNJS2(pChtj zK`OCN!RAPi7BpAu?0uSW&gYUriWqXWiy{p?%>E}laM7YMcGpjdS}$9aQf$E)_Fd>7 ztXPy~KM9AfS(I1Y4;K{Q6GWiQfdVF2ixyQhFKSNtPt4aJW^b5yOITggwxK z^|5$dbE3}PnTMQ1xr9xbHksyMNYg)VQ=a`Id@uT^6kBjnbBWz?68GK&z6;G&&C{Ed zYd7{oXK(HpzAyyEwaX&OwhX>7_V3{P@;22J?}t|ux5Lyf=NH6hf5E11&0+TIxPeU9 zrkLW*A~2=|OxJ5pI>$%f#6ChY?|-ll3$AHiX0PQfVINpud=BZ^`J{8cX>`G+vLoXi zKO^$Nc9--xJa8HJisBt`n0*TUR(1C5BhG%K=_BvTqfT#MBtWa%}LfM=c8X%Kq=c&Nl)yrq~B3*e{{~CfB=i*twL8 z1!y=dr+qBaj07mj{!$o^VqWtUJDx@VwEz`0m)OsuKZ-|nj{OGQvJ{{S`wwvLP%S`J zC9nrxWVfHjgyjI$*gN5_>jCQSlN){*Zd(aZn0*r7gJ(U$eh;=%eu`<1vumd*N9IvK zB{u?VPHC>P%`-@t@l&{8ZeZOJ#XabcDHh}7?C)Z}c>?Qw?VJpxz79y&*w16amb{s#jVIWf)lQAKlAbLKX=%sFwPF+b1#0P}-o zgQgV6;DTat?3q@a69Y;d*qleg8QkfbE1Kic`VA!5cVm9re+^2qe=PDX`6$IcDf+Mb zD6?T)NCXNTD6CEJQ;}VQ&6=O)G%wWIoBRnb@Q)T-=s0k3C%V3 z;a_3Ajdzx2x?Sd9?=QgOD0aid$;*Did9I_?q~abqsDvcnZ}rYT;T*|zn`963++X8Y zveTq)b{1~N884!^A0Adb0H+lP;VgR|^Q{q_Z#7RVeq0R5?v)$Z{u?}L<2WF*e*_OE zkbr$2-aTT{H2aUR6*sA*xy=3${qc}@Z+i(h^$aek=D9k1S0LydfGTGFPbUHF~2&Zj{K45}6t$vZy3P zN&Wv+BJX?IEjVahXg8sCqisd&LAx1k8`>>s+tGT_!f1VH{b)PTZbchF8${cQ7D3xJ K>lTJC-v1R_(BFyx diff --git a/tools/coredump/ebpfmaps.go b/tools/coredump/ebpfmaps.go index 212a48af9..b04a4c9e9 100644 --- a/tools/coredump/ebpfmaps.go +++ b/tools/coredump/ebpfmaps.go @@ -14,7 +14,7 @@ import ( "go.opentelemetry.io/ebpf-profiler/lpm" "go.opentelemetry.io/ebpf-profiler/metrics" sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" - pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" + pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" "go.opentelemetry.io/ebpf-profiler/support" "go.opentelemetry.io/ebpf-profiler/util" ) From f48c32a59936747541a06d2cac50214b1ef9f11f Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Wed, 6 Aug 2025 14:54:49 +0200 Subject: [PATCH 08/24] golabels: refactor integration tests (#678) Signed-off-by: Florian Lehner Co-authored-by: Christos Kalkanis --- Makefile | 21 +-- .../golabels/integrationtests/busy_nocgo.go | 10 ++ .../golabels/integrationtests/busy_other.go | 11 ++ .../golabels/integrationtests/busy_withcgo.go | 23 +++ .../integrationtests/golabels_test.go | 158 ++++++++++++++++++ interpreter/golabels/test/main-cgo.go | 62 ------- interpreter/golabels/test/main.go | 50 ------ interpreter/golabels/test/main_test.go | 121 -------------- 8 files changed, 210 insertions(+), 246 deletions(-) create mode 100644 interpreter/golabels/integrationtests/busy_nocgo.go create mode 100644 interpreter/golabels/integrationtests/busy_other.go create mode 100644 interpreter/golabels/integrationtests/busy_withcgo.go create mode 100644 interpreter/golabels/integrationtests/golabels_test.go delete mode 100644 interpreter/golabels/test/main-cgo.go delete mode 100644 interpreter/golabels/test/main.go delete mode 100644 interpreter/golabels/test/main_test.go diff --git a/Makefile b/Makefile index 7364e37da..a094517b3 100644 --- a/Makefile +++ b/Makefile @@ -110,11 +110,6 @@ test-junit: generate ebpf test-deps go install gotest.tools/gotestsum@latest CGO_ENABLED=1 gotestsum --junitfile $(JUNIT_OUT_DIR)/junit.xml -- $(GO_FLAGS) -tags $(GO_TAGS) ./... -# This target isn't called from CI, it doesn't work for cross compile (ie TARGET_ARCH=arm64 on -# amd64) and the CI kernel tests run them already. Useful for local testing. -sudo-golabels-test: integration-test-binaries - (cd support && sudo ./interpreter_golabels_test.test -test.v) - TESTDATA_DIRS:= \ nativeunwind/elfunwindinfo/testdata \ libpf/pfelf/testdata \ @@ -125,19 +120,19 @@ test-deps: ($(MAKE) -C "$(testdata_dir)") || exit ; \ ) -TEST_INTEGRATION_BINARY_DIRS := tracer processmanager/ebpf support interpreter/golabels/test +TEST_INTEGRATION_BINARY_DIRS := tracer processmanager/ebpf support # These binaries are named ".test" to get included into bluebox initramfs -support/golbls_1_23.test: ./interpreter/golabels/test/main.go - CGO_ENABLED=0 GOTOOLCHAIN=go1.23.7 go build -tags $(GO_TAGS),nocgo -o $@ $< +support/golbls_1_23.test: generate ebpf + CGO_ENABLED=0 GOTOOLCHAIN=go1.23.7 go test -C ./interpreter/golabels/integrationtests -c -trimpath -tags $(GO_TAGS),nocgo,integration -o $@ -support/golbls_1_24.test: ./interpreter/golabels/test/main.go - CGO_ENABLED=0 GOTOOLCHAIN=go1.24.1 go build -tags $(GO_TAGS),nocgo -o $@ $< +support/golbls_1_24.test: generate ebpf + CGO_ENABLED=0 GOTOOLCHAIN=go1.24.1 go test -C ./interpreter/golabels/integrationtests -c -trimpath -tags $(GO_TAGS),nocgo,integration -o $@ -support/golbls_cgo.test: ./interpreter/golabels/test/main-cgo.go - CGO_ENABLED=1 GOTOOLCHAIN=go1.24.1 go build -ldflags '-extldflags "-static"' -tags $(GO_TAGS),usecgo -o $@ $< +support/golbls_cgo.test: generate ebpf + CGO_ENABLED=1 GOTOOLCHAIN=go1.24.1 go test -C ./interpreter/golabels/integrationtests -c -ldflags '-extldflags "-static"' -trimpath -tags $(GO_TAGS),withcgo,integration -o $@ -integration-test-binaries: generate ebpf rust-components support/golbls_1_23.test support/golbls_1_24.test support/golbls_cgo.test +integration-test-binaries: support/golbls_1_23.test support/golbls_1_24.test support/golbls_cgo.test $(foreach test_name, $(TEST_INTEGRATION_BINARY_DIRS), \ (go test -ldflags='-extldflags=-static' -trimpath -c \ -tags $(GO_TAGS),static_build,integration \ diff --git a/interpreter/golabels/integrationtests/busy_nocgo.go b/interpreter/golabels/integrationtests/busy_nocgo.go new file mode 100644 index 000000000..2f84f753a --- /dev/null +++ b/interpreter/golabels/integrationtests/busy_nocgo.go @@ -0,0 +1,10 @@ +//go:build nocgo && linux + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package integrationtests // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabels/integrationtests" + +//go:noinline +func busyFunc() { +} diff --git a/interpreter/golabels/integrationtests/busy_other.go b/interpreter/golabels/integrationtests/busy_other.go new file mode 100644 index 000000000..f48e1bdd1 --- /dev/null +++ b/interpreter/golabels/integrationtests/busy_other.go @@ -0,0 +1,11 @@ +//go:build !nocgo && !withcgo + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//nolint:lll +package integrationtests // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabels/integrationtests" + +//go:noinline +func busyFunc() { +} diff --git a/interpreter/golabels/integrationtests/busy_withcgo.go b/interpreter/golabels/integrationtests/busy_withcgo.go new file mode 100644 index 000000000..cd0c466d1 --- /dev/null +++ b/interpreter/golabels/integrationtests/busy_withcgo.go @@ -0,0 +1,23 @@ +//go:build withcgo && linux + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package integrationtests // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabels/integrationtests" + +/* +#include + +void cgofunc() { + volatile int counter = 0; + while (counter < 1000000) { + counter++; + } +} +*/ +import "C" + +//go:noinline +func busyFunc() { + C.cgofunc() +} diff --git a/interpreter/golabels/integrationtests/golabels_test.go b/interpreter/golabels/integrationtests/golabels_test.go new file mode 100644 index 000000000..e8c72bd26 --- /dev/null +++ b/interpreter/golabels/integrationtests/golabels_test.go @@ -0,0 +1,158 @@ +//go:build integration + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package integrationtests + +import ( + "context" + "math" + "math/rand" + "os" + "runtime/debug" + "runtime/pprof" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/ebpf-profiler/host" + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/reporter" + "go.opentelemetry.io/ebpf-profiler/tracer" + tracertypes "go.opentelemetry.io/ebpf-profiler/tracer/types" +) + +type mockIntervals struct{} + +func (mockIntervals) MonitorInterval() time.Duration { return 1 * time.Second } +func (mockIntervals) TracePollInterval() time.Duration { return 250 * time.Millisecond } +func (mockIntervals) PIDCleanupInterval() time.Duration { return 1 * time.Second } + +type mockReporter struct{} + +func (mockReporter) ExecutableKnown(_ libpf.FileID) bool { return true } +func (mockReporter) ExecutableMetadata(_ *reporter.ExecutableMetadataArgs) {} + +func isRoot() bool { + return os.Geteuid() == 0 +} + +//nolint:gosec +func randomString(n int) string { + letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + s := make([]rune, n) + for i := range s { + s[i] = letters[rand.Intn(len(letters))] + } + return string(s) +} + +func setPprofLabels(t *testing.T, ctx context.Context, cookie string, busyFunc func()) { + t.Helper() + labels := pprof.Labels( + "l1"+cookie, "label1"+randomString(16), + "l2"+cookie, "label2"+randomString(24), + "l3"+cookie, "label3"+randomString(48)) + lastUpdate := time.Now() + pprof.Do(context.TODO(), labels, func(context.Context) { + for time.Since(lastUpdate) < 10*time.Second { + // CPU go burr on purpose. + busyFunc() + if ctx.Err() != nil { + return + } + } + }) +} + +func Test_Golabels(t *testing.T) { + if !isRoot() { + t.Skip("root privileges required") + } + + buildInfo, ok := debug.ReadBuildInfo() + if !ok { + t.Fatalf("Failed to get build info") + } + + withCGO := false + for _, setting := range buildInfo.Settings { + if setting.Key == "CGO_ENABLED" { + withCGO = true + } + } + t.Logf("CGo is enabled: %t", withCGO) + + cookie := buildInfo.GoVersion + + t.Run(cookie, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + enabledTracers, _ := tracertypes.Parse("") + enabledTracers.Enable(tracertypes.Labels) + + trc, err := tracer.NewTracer(ctx, &tracer.Config{ + Reporter: &mockReporter{}, + Intervals: &mockIntervals{}, + IncludeTracers: enabledTracers, + SamplesPerSecond: 20, + ProbabilisticInterval: 100, + ProbabilisticThreshold: 100, + OffCPUThreshold: uint32(math.MaxUint32 / 100), + VerboseMode: true, + }) + require.NoError(t, err) + + trc.StartPIDEventProcessor(ctx) + + err = trc.AttachTracer() + require.NoError(t, err) + + t.Log("Attached tracer program") + + err = trc.EnableProfiling() + require.NoError(t, err) + + err = trc.AttachSchedMonitor() + require.NoError(t, err) + + traceCh := make(chan *host.Trace) + + err = trc.StartMapMonitors(ctx, traceCh) + require.NoError(t, err) + + go setPprofLabels(t, ctx, cookie, busyFunc) + + for trace := range traceCh { + if trace == nil { + continue + } + if len(trace.CustomLabels) > 0 { + hits := 0 + for k, v := range trace.CustomLabels { + switch k { + case "l1" + cookie: + require.Len(t, v, 22) + require.True(t, strings.HasPrefix(v, "label1")) + hits |= (1 << 0) + case "l2" + cookie: + require.Len(t, v, 30) + require.True(t, strings.HasPrefix(v, "label2")) + hits |= (1 << 1) + case "l3" + cookie: + require.Len(t, v, 47) + require.True(t, strings.HasPrefix(v, "label3")) + hits |= (1 << 2) + } + } + if hits == (1<<0 | 1<<1 | 1<<2) { + cancel() + break + } + } + } + }) +} diff --git a/interpreter/golabels/test/main-cgo.go b/interpreter/golabels/test/main-cgo.go deleted file mode 100644 index 3950f58df..000000000 --- a/interpreter/golabels/test/main-cgo.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -//go:build usecgo -// +build usecgo - -package main - -/* -#include - -void cgofunc() { - volatile int counter = 0; - while (counter < 1000000) { - counter++; - } -} -*/ -import "C" - -import ( - "context" - "fmt" - "math/rand" - "os" - "runtime/pprof" - "time" -) - -func randomString2(n int) string { - letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - s := make([]rune, n) - for i := range s { - s[i] = letters[rand.Intn(len(letters))] - } - return string(s) -} - -// This is a normal main program that when go build will be statically linked, this is required -// to work with qemu/bluebox testing harness. A statically linked go test built binary doesn't -// work with the go labels extractor ebpf program, not sure yet if this is a bug. -func main() { - // If first isn't subtest then we're running via bluebox init and should just exit. - if len(os.Args) != 3 || os.Args[1] != "-subtest" { - fmt.Println("PASS") - return - } - cookie := os.Args[2] - labels := pprof.Labels( - "l1"+cookie, "label1"+randomString2(16), - "l2"+cookie, "label2"+randomString2(24), - "l3"+cookie, "label3"+randomString2(48)) - lastUpdate := time.Now() - pprof.Do(context.TODO(), labels, func(context.Context) { - //nolint:revive - for time.Since(lastUpdate) < 10*time.Second { - // CPU go burr on purpose. - C.cgofunc() - } - }) - fmt.Println("PASS") -} diff --git a/interpreter/golabels/test/main.go b/interpreter/golabels/test/main.go deleted file mode 100644 index 4683bba38..000000000 --- a/interpreter/golabels/test/main.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -//go:build nocgo -// +build nocgo - -package main - -import ( - "context" - "fmt" - "math/rand" - "os" - "runtime/pprof" - "time" -) - -//nolint:gosec -func randomString(n int) string { - letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - s := make([]rune, n) - for i := range s { - s[i] = letters[rand.Intn(len(letters))] - } - return string(s) -} - -// This is a normal main program that when go build will be statically linked, this is required -// to work with qemu/bluebox testing harness. A statically linked go test built binary doesn't -// work with the go labels extractor ebpf program, not sure yet if this is a bug. -func main() { - // If first isn't subtest then we're running via bluebox init and should just exit. - if len(os.Args) != 3 || os.Args[1] != "-subtest" { - fmt.Println("PASS") - return - } - cookie := os.Args[2] - labels := pprof.Labels( - "l1"+cookie, "label1"+randomString(16), - "l2"+cookie, "label2"+randomString(24), - "l3"+cookie, "label3"+randomString(48)) - lastUpdate := time.Now() - pprof.Do(context.TODO(), labels, func(context.Context) { - //nolint:revive - for time.Since(lastUpdate) < 10*time.Second { - // CPU go burr on purpose. - } - }) - fmt.Println("PASS") -} diff --git a/interpreter/golabels/test/main_test.go b/interpreter/golabels/test/main_test.go deleted file mode 100644 index b62e0acbb..000000000 --- a/interpreter/golabels/test/main_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -package main - -import ( - "context" - "math" - "os" - "os/exec" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/ebpf-profiler/host" - "go.opentelemetry.io/ebpf-profiler/libpf" - "go.opentelemetry.io/ebpf-profiler/reporter" - "go.opentelemetry.io/ebpf-profiler/tracer" - tracertypes "go.opentelemetry.io/ebpf-profiler/tracer/types" -) - -type mockIntervals struct{} - -func (mockIntervals) MonitorInterval() time.Duration { return 1 * time.Second } -func (mockIntervals) TracePollInterval() time.Duration { return 250 * time.Millisecond } -func (mockIntervals) PIDCleanupInterval() time.Duration { return 1 * time.Second } - -type mockReporter struct{} - -func (mockReporter) ExecutableKnown(_ libpf.FileID) bool { return true } -func (mockReporter) ExecutableMetadata(_ *reporter.ExecutableMetadataArgs) {} - -func isRoot() bool { - return os.Geteuid() == 0 -} - -func TestGoLabels(t *testing.T) { - if !isRoot() { - t.Skip("root privileges required") - } - ctx := context.Background() - - enabledTracers, _ := tracertypes.Parse("") - enabledTracers.Enable(tracertypes.Labels) - - trc, err := tracer.NewTracer(ctx, &tracer.Config{ - Reporter: &mockReporter{}, - Intervals: &mockIntervals{}, - IncludeTracers: enabledTracers, - SamplesPerSecond: 20, - ProbabilisticInterval: 100, - ProbabilisticThreshold: 100, - OffCPUThreshold: uint32(math.MaxUint32 / 100), - VerboseMode: true, - }) - require.NoError(t, err) - - trc.StartPIDEventProcessor(ctx) - - err = trc.AttachTracer() - require.NoError(t, err) - - t.Log("Attached tracer program") - - err = trc.EnableProfiling() - require.NoError(t, err) - - err = trc.AttachSchedMonitor() - require.NoError(t, err) - - traceCh := make(chan *host.Trace) - - err = trc.StartMapMonitors(ctx, traceCh) - require.NoError(t, err) - - for _, tc := range [][]string{ - {"./golbls_1_23.test", "123"}, - {"./golbls_1_24.test", "124"}, - {"./golbls_cgo.test", "cgo"}, - } { - t.Run(tc[0], func(t *testing.T) { - // Use a separate exe for getting labels as the bpf code doesn't seem to work with - // go test static binaries at the moment, not clear if that's a problem with the bpf - // code or a bug/fact of life for static go binaries and getting g from TLS. - cookie := tc[1] - cmd := exec.Command(tc[0], "-subtest", cookie) - err := cmd.Start() - require.NoError(t, err) - - for trace := range traceCh { - if trace == nil { - continue - } - if len(trace.CustomLabels) > 0 { - hits := 0 - for k, v := range trace.CustomLabels { - switch k { - case "l1" + cookie: - require.Len(t, v, 22) - require.True(t, strings.HasPrefix(v, "label1")) - hits++ - case "l2" + cookie: - require.Len(t, v, 30) - require.True(t, strings.HasPrefix(v, "label2")) - hits++ - case "l3" + cookie: - require.Len(t, v, 47) - require.True(t, strings.HasPrefix(v, "label3")) - hits++ - } - } - if hits == 3 { - break - } - } - } - _ = cmd.Process.Signal(os.Kill) - _ = cmd.Wait() - }) - } -} From e760594972f42488a1e0543fd0d189d4f70e7213 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:49:25 +0300 Subject: [PATCH 09/24] chore(deps): update actions/checkout action to v4.3.0 (#689) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/auto-tag.yml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/codespell.yml | 2 +- .github/workflows/collector-tests.yml | 2 +- .github/workflows/fossa.yml | 2 +- .github/workflows/ossf-scorecard.yml | 2 +- .github/workflows/push-docker-image.yml | 2 +- .github/workflows/unit-test-on-pull-request.yml | 16 ++++++++-------- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index a71d8fd8a..315664064 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Configure Git run: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2a044e5e1..98301d50e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -22,7 +22,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up environment uses: ./.github/workflows/env diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 4db8e6bda..9572ecde5 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -13,6 +13,6 @@ jobs: - name: Install codespell run: sudo apt-get install codespell - name: Checkout Repo - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Codespell run: make codespell diff --git a/.github/workflows/collector-tests.yml b/.github/workflows/collector-tests.yml index 9b2e8b0fb..7fd8906f2 100644 --- a/.github/workflows/collector-tests.yml +++ b/.github/workflows/collector-tests.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up environment uses: ./.github/workflows/env with: diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 783d3c130..c75e01a97 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'open-telemetry/opentelemetry-ebpf-profiler' steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0 with: diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 34af9fa08..6f6d59364 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -19,7 +19,7 @@ jobs: # Needed for GitHub OIDC token if publish_results is true id-token: write steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false diff --git a/.github/workflows/push-docker-image.yml b/.github/workflows/push-docker-image.yml index 203291c71..dfdc876bc 100644 --- a/.github/workflows/push-docker-image.yml +++ b/.github/workflows/push-docker-image.yml @@ -15,7 +15,7 @@ jobs: if: github.repository == 'open-telemetry/opentelemetry-ebpf-profiler' steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Login to Docker Hub uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 with: diff --git a/.github/workflows/unit-test-on-pull-request.yml b/.github/workflows/unit-test-on-pull-request.yml index dbb2f0265..3b7323e63 100644 --- a/.github/workflows/unit-test-on-pull-request.yml +++ b/.github/workflows/unit-test-on-pull-request.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up environment uses: ./.github/workflows/env - name: Check for changes in licenses of dependencies @@ -34,7 +34,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up environment uses: ./.github/workflows/env - name: Get linter version @@ -57,7 +57,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up environment uses: ./.github/workflows/env with: @@ -81,7 +81,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up environment uses: ./.github/workflows/env - name: Tests @@ -96,7 +96,7 @@ jobs: shell: bash --login {0} steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Hash binary blobs run: | sha256sum support/ebpf/tracer.ebpf.* > binary-blobs.hash @@ -126,7 +126,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up environment uses: ./.github/workflows/env - name: Prepare integration test binaries for qemu tests @@ -142,7 +142,7 @@ jobs: runs-on: macos-latest steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Set up Go uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: @@ -187,7 +187,7 @@ jobs: - { target_arch: arm64, kernel: 6.12.16 } steps: - name: Clone code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Install dependencies run: | sudo apt-get update -y From d179d51855deb99cd870b2d46157a9208e86d3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 12 Aug 2025 08:49:48 +0300 Subject: [PATCH 10/24] Rework VDSO synthetic deltas to be less special (#666) --- processmanager/execinfomanager/manager.go | 34 ++++-------------- .../{ => execinfomanager}/synthdeltas.go | 15 +++++--- .../{ => execinfomanager}/synthdeltas_test.go | 2 +- .../testdata/vdso.arch64.withframe | Bin processmanager/manager.go | 11 ------ processmanager/processinfo.go | 14 +------- processmanager/synthdeltas_arm64.go | 8 ----- processmanager/synthdeltas_other.go | 8 ----- 8 files changed, 20 insertions(+), 72 deletions(-) rename processmanager/{ => execinfomanager}/synthdeltas.go (86%) rename processmanager/{ => execinfomanager}/synthdeltas_test.go (92%) rename processmanager/{ => execinfomanager}/testdata/vdso.arch64.withframe (100%) delete mode 100644 processmanager/synthdeltas_arm64.go delete mode 100644 processmanager/synthdeltas_other.go diff --git a/processmanager/execinfomanager/manager.go b/processmanager/execinfomanager/manager.go index 4cea0ec1b..ac03157c0 100644 --- a/processmanager/execinfomanager/manager.go +++ b/processmanager/execinfomanager/manager.go @@ -210,6 +210,13 @@ func (mgr *ExecutableInfoManager) AddOrIncRef(fileID host.FileID, } return ExecutableInfo{}, fmt.Errorf("failed to extract interval data: %w", err) } + if len(intervalData.Deltas) == 0 { + ef, errx := elfRef.GetELF() + if errx != nil { + return ExecutableInfo{}, errx + } + intervalData = synthesizeIntervalData(ef) + } // Also gather TSD info if applicable. if tpbase.IsPotentialTSDDSO(elfRef.FileName()) { @@ -252,33 +259,6 @@ func (mgr *ExecutableInfoManager) AddOrIncRef(fileID host.FileID, return info.ExecutableInfo, nil } -// AddSynthIntervalData should only be called once for a given file ID. It will error if it or -// AddOrIncRef has been previously called for the same file ID. Interpreter detection is skipped. -func (mgr *ExecutableInfoManager) AddSynthIntervalData( - fileID host.FileID, - data sdtypes.IntervalData, -) error { - state := mgr.state.WLock() - defer mgr.state.WUnlock(&state) - - if _, exists := state.executables[fileID]; exists { - return errors.New("AddSynthIntervalData: mapping already exists") - } - - ref, _, err := state.loadDeltas(fileID, data.Deltas) - if err != nil { - return fmt.Errorf("failed to load deltas: %w", err) - } - - state.executables[fileID] = &entry{ - ExecutableInfo: ExecutableInfo{Data: nil}, - mapRef: ref, - rc: 1, - } - - return nil -} - // RemoveOrDecRef decrements the reference counter of the executable being tracked. Once the RC // reaches zero, information about the file is removed from the manager and the corresponding // BPF maps. diff --git a/processmanager/synthdeltas.go b/processmanager/execinfomanager/synthdeltas.go similarity index 86% rename from processmanager/synthdeltas.go rename to processmanager/execinfomanager/synthdeltas.go index 2d05d7476..69263d94d 100644 --- a/processmanager/synthdeltas.go +++ b/processmanager/execinfomanager/synthdeltas.go @@ -1,9 +1,10 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package processmanager // import "go.opentelemetry.io/ebpf-profiler/processmanager" +package execinfomanager // import "go.opentelemetry.io/ebpf-profiler/processmanager/execinfomanager" import ( + "debug/elf" "sort" aa "golang.org/x/arch/arm64/arm64asm" @@ -21,9 +22,15 @@ const regFP = 29 // regLR is the arm64 link register (x30) number const regLR = 30 -// createVDSOSyntheticRecordNone returns no synthetic deltas when the kernel vDSO -// is known to have valid unwind information. -func createVDSOSyntheticRecordNone(_ *pfelf.File) sdtypes.IntervalData { +// synthesizeIntervalData creates synthetic stack deltas if possible. +// Currently supported for ARM64 vDSO only. +func synthesizeIntervalData(ef *pfelf.File) sdtypes.IntervalData { + if ef.Machine == elf.EM_AARCH64 { + soname, err := ef.DynString(elf.DT_SONAME) + if err == nil && soname[0] == "linux-vdso.so.1" { + return createVDSOSyntheticRecordArm64(ef) + } + } return sdtypes.IntervalData{} } diff --git a/processmanager/synthdeltas_test.go b/processmanager/execinfomanager/synthdeltas_test.go similarity index 92% rename from processmanager/synthdeltas_test.go rename to processmanager/execinfomanager/synthdeltas_test.go index 0fa5b00cc..4a7f090d1 100644 --- a/processmanager/synthdeltas_test.go +++ b/processmanager/execinfomanager/synthdeltas_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package processmanager // import "go.opentelemetry.io/ebpf-profiler/processmanager" +package execinfomanager // import "go.opentelemetry.io/ebpf-profiler/processmanager/execinfomanager" import ( "testing" diff --git a/processmanager/testdata/vdso.arch64.withframe b/processmanager/execinfomanager/testdata/vdso.arch64.withframe similarity index 100% rename from processmanager/testdata/vdso.arch64.withframe rename to processmanager/execinfomanager/testdata/vdso.arch64.withframe diff --git a/processmanager/manager.go b/processmanager/manager.go index edb9b37cd..980dec91a 100644 --- a/processmanager/manager.go +++ b/processmanager/manager.go @@ -20,7 +20,6 @@ import ( "go.opentelemetry.io/ebpf-profiler/lpm" "go.opentelemetry.io/ebpf-profiler/metrics" "go.opentelemetry.io/ebpf-profiler/nativeunwind" - sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" "go.opentelemetry.io/ebpf-profiler/periodiccaller" pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi" eim "go.opentelemetry.io/ebpf-profiler/processmanager/execinfomanager" @@ -337,13 +336,3 @@ func (pm *ProcessManager) MaybeNotifyAPMAgent( return serviceName } - -// AddSynthIntervalData adds synthetic stack deltas to the manager. This is useful for cases where -// populating the information via the stack delta provider isn't viable, for example because the -// `.eh_frame` section for a binary is broken. If `AddSynthIntervalData` was called for a given -// file ID, the stack delta provider will not be consulted and the manually added stack deltas take -// precedence. -func (pm *ProcessManager) AddSynthIntervalData(fileID host.FileID, - data sdtypes.IntervalData) error { - return pm.eim.AddSynthIntervalData(fileID, data) -} diff --git a/processmanager/processinfo.go b/processmanager/processinfo.go index 4b74d46b5..7cb344924 100644 --- a/processmanager/processinfo.go +++ b/processmanager/processinfo.go @@ -412,19 +412,7 @@ func (pm *ProcessManager) getELFInfo(pr process.Process, mapping *process.Mappin hostFileID := host.FileIDFromLibpf(fileID) info.fileID = hostFileID info.addressMapper = ef.GetAddressMapper() - if mapping.IsVDSO() { - intervals := createVDSOSyntheticRecord(ef) - if intervals.Deltas != nil { - if err := pm.AddSynthIntervalData(hostFileID, intervals); err != nil { - info.err = fmt.Errorf("failed to add synthetic deltas: %w", err) - } - } - } - // Do not cache the entry if synthetic stack delta loading failed, - // so next encounter of the VDSO will retry loading them. - if info.err == nil { - pm.elfInfoCache.Add(key, info) - } + pm.elfInfoCache.Add(key, info) pm.FileIDMapper.Set(hostFileID, fileID) if pm.reporter.ExecutableKnown(fileID) { diff --git a/processmanager/synthdeltas_arm64.go b/processmanager/synthdeltas_arm64.go deleted file mode 100644 index bb0b7dcd0..000000000 --- a/processmanager/synthdeltas_arm64.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build arm64 - -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package processmanager // import "go.opentelemetry.io/ebpf-profiler/processmanager" - -var createVDSOSyntheticRecord = createVDSOSyntheticRecordArm64 diff --git a/processmanager/synthdeltas_other.go b/processmanager/synthdeltas_other.go deleted file mode 100644 index 68a709072..000000000 --- a/processmanager/synthdeltas_other.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !arm64 - -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package processmanager // import "go.opentelemetry.io/ebpf-profiler/processmanager" - -var createVDSOSyntheticRecord = createVDSOSyntheticRecordNone From f868ddfc20728bfc8beb1d20ca25c0d27325c8a5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:50:28 +0300 Subject: [PATCH 11/24] chore(deps): update rust crate thiserror to v2.0.14 (#688) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 017a3bba8..ac820c737 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -600,18 +600,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", From e516119d36edb2fd9a4a524b30f51f1a8de73a44 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 19:15:02 +0200 Subject: [PATCH 12/24] fix(deps): update go dependencies (#690) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 74 +++++------ go.sum | 155 ++++++++++++----------- reporter/internal/pdata/generate.go | 2 +- reporter/internal/pdata/generate_test.go | 2 +- 4 files changed, 117 insertions(+), 116 deletions(-) diff --git a/go.mod b/go.mod index c7ed7a11d..20c1b2c97 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module go.opentelemetry.io/ebpf-profiler go 1.23.6 require ( - github.com/aws/aws-sdk-go-v2 v1.37.2 - github.com/aws/aws-sdk-go-v2/config v1.30.3 - github.com/aws/aws-sdk-go-v2/service/s3 v1.86.0 + github.com/aws/aws-sdk-go-v2 v1.38.0 + github.com/aws/aws-sdk-go-v2/config v1.31.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0 github.com/cespare/xxhash/v2 v2.3.0 github.com/cilium/ebpf v0.19.0 github.com/docker/go-connections v0.5.0 @@ -22,22 +22,22 @@ require ( github.com/testcontainers/testcontainers-go v0.35.0 github.com/tklauser/numcpus v0.10.0 github.com/zeebo/xxh3 v1.0.2 - go.opentelemetry.io/collector/component v1.35.0 - go.opentelemetry.io/collector/consumer/consumertest v0.129.0 - go.opentelemetry.io/collector/consumer/xconsumer v0.129.0 - go.opentelemetry.io/collector/pdata v1.35.0 - go.opentelemetry.io/collector/pdata/pprofile v0.129.0 - go.opentelemetry.io/collector/receiver v1.35.0 - go.opentelemetry.io/collector/receiver/receivertest v0.129.0 - go.opentelemetry.io/collector/receiver/xreceiver v0.129.0 + go.opentelemetry.io/collector/component v1.38.0 + go.opentelemetry.io/collector/consumer/consumertest v0.132.0 + go.opentelemetry.io/collector/consumer/xconsumer v0.132.0 + go.opentelemetry.io/collector/pdata v1.38.0 + go.opentelemetry.io/collector/pdata/pprofile v0.132.0 + go.opentelemetry.io/collector/receiver v1.38.0 + go.opentelemetry.io/collector/receiver/receivertest v0.132.0 + go.opentelemetry.io/collector/receiver/xreceiver v0.132.0 go.opentelemetry.io/otel v1.37.0 go.opentelemetry.io/otel/metric v1.37.0 - golang.org/x/arch v0.19.0 - golang.org/x/exp v0.0.0-20250717185816-542afb5b7346 - golang.org/x/mod v0.26.0 + golang.org/x/arch v0.20.0 + golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 + golang.org/x/mod v0.27.0 golang.org/x/sync v0.16.0 golang.org/x/sys v0.35.0 - google.golang.org/grpc v1.74.1 + google.golang.org/grpc v1.74.2 ) require ( @@ -45,19 +45,19 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.18.3 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.2 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.4 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.2 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.27.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.32.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.36.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.28.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.37.0 // indirect github.com/aws/smithy-go v1.22.5 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/containerd/containerd v1.7.18 // indirect @@ -90,7 +90,7 @@ require ( github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect @@ -102,17 +102,17 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/collector/component/componenttest v0.129.0 // indirect - go.opentelemetry.io/collector/consumer v1.35.0 // indirect - go.opentelemetry.io/collector/consumer/consumererror v0.129.0 // indirect - go.opentelemetry.io/collector/featuregate v1.35.0 // indirect - go.opentelemetry.io/collector/internal/telemetry v0.129.0 // indirect - go.opentelemetry.io/collector/pipeline v0.129.0 // indirect - go.opentelemetry.io/contrib/bridges/otelzap v0.11.0 // indirect + go.opentelemetry.io/collector/component/componenttest v0.132.0 // indirect + go.opentelemetry.io/collector/consumer v1.38.0 // indirect + go.opentelemetry.io/collector/consumer/consumererror v0.132.0 // indirect + go.opentelemetry.io/collector/featuregate v1.38.0 // indirect + go.opentelemetry.io/collector/internal/telemetry v0.132.0 // indirect + go.opentelemetry.io/collector/pipeline v1.38.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.12.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel/log v0.12.2 // indirect - go.opentelemetry.io/otel/sdk v1.36.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/log v0.13.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect diff --git a/go.sum b/go.sum index 4213fe878..0f1e2549c 100644 --- a/go.sum +++ b/go.sum @@ -6,40 +6,40 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/aws/aws-sdk-go-v2 v1.37.2 h1:xkW1iMYawzcmYFYEV0UCMxc8gSsjCGEhBXQkdQywVbo= -github.com/aws/aws-sdk-go-v2 v1.37.2/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= +github.com/aws/aws-sdk-go-v2 v1.38.0 h1:UCRQ5mlqcFk9HJDIqENSLR3wiG1VTWlyUfLDEvY7RxU= +github.com/aws/aws-sdk-go-v2 v1.38.0/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 h1:6GMWV6CNpA/6fbFHnoAjrv4+LGfyTqZz2LtCHnspgDg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0/go.mod h1:/mXlTIVG9jbxkqDnr5UQNQxW1HRYxeGklkM9vAFeabg= -github.com/aws/aws-sdk-go-v2/config v1.30.3 h1:utupeVnE3bmB221W08P0Moz1lDI3OwYa2fBtUhl7TCc= -github.com/aws/aws-sdk-go-v2/config v1.30.3/go.mod h1:NDGwOEBdpyZwLPlQkpKIO7frf18BW8PaCmAM9iUxQmI= -github.com/aws/aws-sdk-go-v2/credentials v1.18.3 h1:ptfyXmv+ooxzFwyuBth0yqABcjVIkjDL0iTYZBSbum8= -github.com/aws/aws-sdk-go-v2/credentials v1.18.3/go.mod h1:Q43Nci++Wohb0qUh4m54sNln0dbxJw8PvQWkrwOkGOI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.2 h1:nRniHAvjFJGUCl04F3WaAj7qp/rcz5Gi1OVoj5ErBkc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.2/go.mod h1:eJDFKAMHHUvv4a0Zfa7bQb//wFNUXGrbFpYRCHe2kD0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2 h1:sPiRHLVUIIQcoVZTNwqQcdtjkqkPopyYmIX0M5ElRf4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.2/go.mod h1:ik86P3sgV+Bk7c1tBFCwI3VxMoSEwl4YkRB9xn1s340= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.2 h1:ZdzDAg075H6stMZtbD2o+PyB933M/f20e9WmCBC17wA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.2/go.mod h1:eE1IIzXG9sdZCB0pNNpMpsYTLl4YdOQD3njiVN1e/E4= +github.com/aws/aws-sdk-go-v2/config v1.31.0 h1:9yH0xiY5fUnVNLRWO0AtayqwU1ndriZdN78LlhruJR4= +github.com/aws/aws-sdk-go-v2/config v1.31.0/go.mod h1:VeV3K72nXnhbe4EuxxhzsDc/ByrCSlZwUnWH52Nde/I= +github.com/aws/aws-sdk-go-v2/credentials v1.18.4 h1:IPd0Algf1b+Qy9BcDp0sCUcIWdCQPSzDoMK3a8pcbUM= +github.com/aws/aws-sdk-go-v2/credentials v1.18.4/go.mod h1:nwg78FjH2qvsRM1EVZlX9WuGUJOL5od+0qvm0adEzHk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3 h1:GicIdnekoJsjq9wqnvyi2elW6CGMSYKhdozE7/Svh78= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.3/go.mod h1:R7BIi6WNC5mc1kfRM7XM/VHC3uRWkjc396sfabq4iOo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3 h1:o9RnO+YZ4X+kt5Z7Nvcishlz0nksIt2PIzDglLMP0vA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.3/go.mod h1:+6aLJzOG1fvMOyzIySYjOFjcguGvVRL68R+uoRencN4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3 h1:joyyUFhiTQQmVK6ImzNU9TQSNRNeD9kOklqTzyk5v6s= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.3/go.mod h1:+vNIyZQP3b3B1tSLI0lxvrU9cfM7gpdRXMFfm67ZcPc= 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.4.2 h1:sBpc8Ph6CpfZsEdkz/8bfg8WhKlWMCms5iWj6W/AW2U= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.2/go.mod h1:Z2lDojZB+92Wo6EKiZZmJid9pPrDJW2NNIXSlaEfVlU= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.3 h1:ZV2XK2L3HBq9sCKQiQ/MdhZJppH/rH0vddEAamsHUIs= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.3/go.mod h1:b9F9tk2HdHpbf3xbN7rUZcfmJI26N6NcJu/8OsBFI/0= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.2 h1:blV3dY6WbxIVOFggfYIo2E1Q2lZoy5imS7nKgu5m6Tc= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.2/go.mod h1:cBWNeLBjHJRSmXAxdS7mwiMUEgx6zup4wQ9J+/PcsRQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.2 h1:oxmDEO14NBZJbK/M8y3brhMFEIGN4j8a6Aq8eY0sqlo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.2/go.mod h1:4hH+8QCrk1uRWDPsVfsNDUup3taAjO8Dnx63au7smAU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.2 h1:0hBNFAPwecERLzkhhBY+lQKUMpXSKVv4Sxovikrioms= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.2/go.mod h1:Vcnh4KyR4imrrjGN7A2kP2v9y6EPudqoPKXtnmBliPU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.86.0 h1:utPhv4ECQzJIUbtx7vMN4A8uZxlQ5tSt1H1toPI41h8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.86.0/go.mod h1:1/eZYtTWazDgVl96LmGdGktHFi7prAcGCrJ9JGvBITU= -github.com/aws/aws-sdk-go-v2/service/sso v1.27.0 h1:j7/jTOjWeJDolPwZ/J4yZ7dUsxsWZEsxNwH5O7F8eEA= -github.com/aws/aws-sdk-go-v2/service/sso v1.27.0/go.mod h1:M0xdEPQtgpNT7kdAX4/vOAPkFj60hSQRb7TvW9B0iug= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.32.0 h1:ywQF2N4VjqX+Psw+jLjMmUL2g1RDHlvri3NxHA08MGI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.32.0/go.mod h1:Z+qv5Q6b7sWiclvbJyPSOT1BRVU9wfSUPaqQzZ1Xg3E= -github.com/aws/aws-sdk-go-v2/service/sts v1.36.0 h1:bRP/a9llXSSgDPk7Rqn5GD/DQCGo6uk95plBFKoXt2M= -github.com/aws/aws-sdk-go-v2/service/sts v1.36.0/go.mod h1:tgBsFzxwl65BWkuJ/x2EUs59bD4SfYKgikvFDJi1S58= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.3 h1:3ZKmesYBaFX33czDl6mbrcHb6jeheg6LqjJhQdefhsY= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.3/go.mod h1:7ryVb78GLCnjq7cw45N6oUb9REl7/vNUwjvIqC5UgdY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3 h1:ieRzyHXypu5ByllM7Sp4hC5f/1Fy5wqxqY0yB85hC7s= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.3/go.mod h1:O5ROz8jHiOAKAwx179v+7sHMhfobFVi6nZt8DEyiYoM= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.3 h1:SE/e52dq9a05RuxzLcjT+S5ZpQobj3ie3UTaSf2NnZc= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.3/go.mod h1:zkpvBTsR020VVr8TOrwK2TrUW9pOir28sH5ECHpnAfo= +github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0 h1:egoDf+Geuuntmw79Mz6mk9gGmELCPzg5PFEABOHB+6Y= +github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0/go.mod h1:t9MDi29H+HDbkolTSQtbI0HP9DemAWQzUjmWC7LGMnE= +github.com/aws/aws-sdk-go-v2/service/sso v1.28.0 h1:Mc/MKBf2m4VynyJkABoVEN+QzkfLqGj0aiJuEe7cMeM= +github.com/aws/aws-sdk-go-v2/service/sso v1.28.0/go.mod h1:iS5OmxEcN4QIPXARGhavH7S8kETNL11kym6jhoS7IUQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0 h1:6csaS/aJmqZQbKhi1EyEMM7yBW653Wy/B9hnBofW+sw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.0/go.mod h1:59qHWaY5B+Rs7HGTuVGaC32m0rdpQ68N8QCN3khYiqs= +github.com/aws/aws-sdk-go-v2/service/sts v1.37.0 h1:MG9VFW43M4A8BYeAfaJJZWrroinxeTi2r3+SnmLQfSA= +github.com/aws/aws-sdk-go-v2/service/sts v1.37.0/go.mod h1:JdeBDPgpJfuS6rU/hNglmOigKhyEZtBmbraLE4GK1J8= github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -159,8 +159,9 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -218,38 +219,38 @@ github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/collector/component v1.35.0 h1:JpvBukEcEUvJ/TInF1KYpXtWEP+C7iYkxCHKjI0o7BQ= -go.opentelemetry.io/collector/component v1.35.0/go.mod h1:hU/ieWPxWbMAacODCSqem5ZaN6QH9W5GWiZ3MtXVuwc= -go.opentelemetry.io/collector/component/componenttest v0.129.0 h1:gpKkZGCRPu3Yn0U2co09bMvhs17yLFb59oV8Gl9mmRI= -go.opentelemetry.io/collector/component/componenttest v0.129.0/go.mod h1:JR9k34Qvd/pap6sYkPr5QqdHpTn66A5lYeYwhenKBAM= -go.opentelemetry.io/collector/consumer v1.35.0 h1:mgS42yh1maXBIE65IT4//iOA89BE+7xSUzV8czyevHg= -go.opentelemetry.io/collector/consumer v1.35.0/go.mod h1:9sSPX0hDHaHqzR2uSmfLOuFK9v3e9K3HRQ+fydAjOWs= -go.opentelemetry.io/collector/consumer/consumererror v0.129.0 h1:ud92OBWwqQlHjjx9cB48XhXU/Lz5QSAnXUAErsNHHME= -go.opentelemetry.io/collector/consumer/consumererror v0.129.0/go.mod h1:wtg7mcOkncUO/oZQUfHYoTPiVgMT4yrEKeskFv9dUJg= -go.opentelemetry.io/collector/consumer/consumertest v0.129.0 h1:kRmrAgVvPxH5c/rTaOYAzyy0YrrYhQpBNkuqtDRrgeU= -go.opentelemetry.io/collector/consumer/consumertest v0.129.0/go.mod h1:JgJKms1+v/CuAjkPH+ceTnKeDgUUGTQV4snGu5wTEHY= -go.opentelemetry.io/collector/consumer/xconsumer v0.129.0 h1:bRyJ9TGWwnrUnB5oQGTjPhxpVRbkIVeugmvks22bJ4A= -go.opentelemetry.io/collector/consumer/xconsumer v0.129.0/go.mod h1:pbe5ZyPJrtzdt/RRI0LqfT1GVBiJLbtkDKx3SBRTiTY= -go.opentelemetry.io/collector/featuregate v1.35.0 h1:c/XRtA35odgxVc4VgOF/PTIk7ajw1wYdQ6QI562gzd4= -go.opentelemetry.io/collector/featuregate v1.35.0/go.mod h1:Y/KsHbvREENKvvN9RlpiWk/IGBK+CATBYzIIpU7nccc= -go.opentelemetry.io/collector/internal/telemetry v0.129.0 h1:jkzRpIyMxMGdAzVOcBe8aRNrbP7eUrMq6cxEHe0sbzA= -go.opentelemetry.io/collector/internal/telemetry v0.129.0/go.mod h1:riAPlR2LZBV7VEx4LicOKebg3N1Ja3izzkv5fl1Lhiw= -go.opentelemetry.io/collector/pdata v1.35.0 h1:ck6WO6hCNjepADY/p9sT9/rLECTLO5ukYTumKzsqB/E= -go.opentelemetry.io/collector/pdata v1.35.0/go.mod h1:pttpb089864qG1k0DMeXLgwwTFLk+o3fAW9I6MF9tzw= -go.opentelemetry.io/collector/pdata/pprofile v0.129.0 h1:DgZTvjOGmyZRx7Or80hz8XbEaGwHPkIh2SX1A5eXttQ= -go.opentelemetry.io/collector/pdata/pprofile v0.129.0/go.mod h1:uUBZxqJNOk6QIMvbx30qom//uD4hXJ1K/l3qysijMLE= -go.opentelemetry.io/collector/pdata/testdata v0.129.0 h1:n1QLnLOtrcAR57oMSVzmtPsQEpCc/nE5Avk1xfuAkjY= -go.opentelemetry.io/collector/pdata/testdata v0.129.0/go.mod h1:RfY5IKpmcvkS2IGVjl9jG9fcT7xpQEBWpg9sQOn/7mY= -go.opentelemetry.io/collector/pipeline v0.129.0 h1:Mp7RuKLizLQJ0381eJqKQ0zpgkFlhTE9cHidpJQIvMU= -go.opentelemetry.io/collector/pipeline v0.129.0/go.mod h1:TO02zju/K6E+oFIOdi372Wk0MXd+Szy72zcTsFQwXl4= -go.opentelemetry.io/collector/receiver v1.35.0 h1:JOLa0cHLi6cKU+qsBWXkAWLnd5MoHdh8GaUJ97jWguY= -go.opentelemetry.io/collector/receiver v1.35.0/go.mod h1:y1y8DNoP54RsiucXP/qeRuCErBLc1gyvFjO+GIIn91s= -go.opentelemetry.io/collector/receiver/receivertest v0.129.0 h1:abzNSUJXrtPwRqDM1R+BWs0uzYN2g7YZa7t6nyeLu3s= -go.opentelemetry.io/collector/receiver/receivertest v0.129.0/go.mod h1:hcn7bZ0gfcQYW00GKfEbhwVDsPhOAKALtxK67dywjYA= -go.opentelemetry.io/collector/receiver/xreceiver v0.129.0 h1:jQSsDPLbnX8tWDNz0a495ACoA4vVe/FlPEIftPdVtmU= -go.opentelemetry.io/collector/receiver/xreceiver v0.129.0/go.mod h1:5vzmNL4Mv2q3xlvw2ypg1d1WWWut9i5bUcphXNbQNN4= -go.opentelemetry.io/contrib/bridges/otelzap v0.11.0 h1:u2E32P7j1a/gRgZDWhIXC+Shd4rLg70mnE7QLI/Ssnw= -go.opentelemetry.io/contrib/bridges/otelzap v0.11.0/go.mod h1:pJPCLM8gzX4ASqLlyAXjHBEYxgbOQJ/9bidWxD6PEPQ= +go.opentelemetry.io/collector/component v1.38.0 h1:GeHVKtdJmf+dXXkviIs2QiwX198QpUDMeLCJzE+a3XU= +go.opentelemetry.io/collector/component v1.38.0/go.mod h1:h5JuuxJk/ZXl5EVzvSZSnRQKFocaB/pGhQQNwxJAfgk= +go.opentelemetry.io/collector/component/componenttest v0.132.0 h1:7D2e/97PZNpxqKEnboSXZM7YObwKYBFNnEdR67BQB4k= +go.opentelemetry.io/collector/component/componenttest v0.132.0/go.mod h1:3Qm91Gd54HMkPwrSkkgO9KwXKjeWzyG42wG3R5QCP3s= +go.opentelemetry.io/collector/consumer v1.38.0 h1:+lECNNGLQU76tzFoVpjX0TVllGXtrkw0NEt7ITK8BeQ= +go.opentelemetry.io/collector/consumer v1.38.0/go.mod h1:taR7SAnPrMWq45gBoWJG6FjQbCAtn+6+HDBI5VW3ENs= +go.opentelemetry.io/collector/consumer/consumererror v0.132.0 h1:ANaVTuxqvs3y+rgYlLfQGKTRC5mfClgeXEBB2sQ67Uo= +go.opentelemetry.io/collector/consumer/consumererror v0.132.0/go.mod h1:6QsXpUYfVvffJcI/fFp7jVSsEwZw94aaza6lS/AKYpI= +go.opentelemetry.io/collector/consumer/consumertest v0.132.0 h1:DR5JN6ufQE3ImWzCKHr5oUYQCIXp08blBKzl0bjK/V4= +go.opentelemetry.io/collector/consumer/consumertest v0.132.0/go.mod h1:t818ikaBxNA8nVkWSl1CCA92rrec0pLjZs43z0MQj5g= +go.opentelemetry.io/collector/consumer/xconsumer v0.132.0 h1:mD5/wwVcBfFr2UCSEVnhTZcIw28+YHUNhzfc3VNcI/c= +go.opentelemetry.io/collector/consumer/xconsumer v0.132.0/go.mod h1:ipDqsHg1OGmU7P/X3N4LWpUtWAOf5va/YvRtZ6AIefk= +go.opentelemetry.io/collector/featuregate v1.38.0 h1:+t+u3a7Zp0o0fn9+4hgbleHjcI8GT8eC9e5uy2tQnfU= +go.opentelemetry.io/collector/featuregate v1.38.0/go.mod h1:Y/KsHbvREENKvvN9RlpiWk/IGBK+CATBYzIIpU7nccc= +go.opentelemetry.io/collector/internal/telemetry v0.132.0 h1:6Y/y9JjUQbUdDi8uBdi2YREE/nh6KGzs0Wv+wJLakbw= +go.opentelemetry.io/collector/internal/telemetry v0.132.0/go.mod h1:KUo0IpZZvImIl172+//Oh2mboILCV5WU4TjdUgU8xEM= +go.opentelemetry.io/collector/pdata v1.38.0 h1:94LzVKMQM8R7RFJ8Z1+sL51IkI90TDfTc/ipH3mPUro= +go.opentelemetry.io/collector/pdata v1.38.0/go.mod h1:DSvnwj37IKyQj2hpB97cGITyauR8tvAauJ6/gsxg8mg= +go.opentelemetry.io/collector/pdata/pprofile v0.132.0 h1:eKSPlMCey2q9fVxqjNfL5d0Jm8k3T7owkJ+tADXYN2A= +go.opentelemetry.io/collector/pdata/pprofile v0.132.0/go.mod h1:F+En9zwwiGDakNhnFuGFUMols9ksZAmX84k5QKCQIIA= +go.opentelemetry.io/collector/pdata/testdata v0.132.0 h1:K1Dqi74YERnE7vfP6s66tyzrOZ7+weDiU/C8aEDDJko= +go.opentelemetry.io/collector/pdata/testdata v0.132.0/go.mod h1:piZCtRY083WhRrJvVj/OuoXm0wejMfw2jLTWDNSKKqk= +go.opentelemetry.io/collector/pipeline v1.38.0 h1:6kWfaWUW9RptGv2NSyT/EZoIkwUOBsZ220UYvOVNZ3U= +go.opentelemetry.io/collector/pipeline v1.38.0/go.mod h1:TO02zju/K6E+oFIOdi372Wk0MXd+Szy72zcTsFQwXl4= +go.opentelemetry.io/collector/receiver v1.38.0 h1:D4eGk8crniFr0FHgTq6FhqXMtUPL56iHk+FKX5A+PYA= +go.opentelemetry.io/collector/receiver v1.38.0/go.mod h1:xIzC4XarvJvq5HuG588qaWSaJMCMgZPmYDTcXUto4lI= +go.opentelemetry.io/collector/receiver/receivertest v0.132.0 h1:9it4Tb52OC9k+5zUOHztxkg9uoS/OmbeBrDK4/je1EM= +go.opentelemetry.io/collector/receiver/receivertest v0.132.0/go.mod h1:fUKFKe1N+fBG7RptBvAupIgtwidgmGfJkmMrC/Tcvgw= +go.opentelemetry.io/collector/receiver/xreceiver v0.132.0 h1:X35jYlFC0fNnfJ92H44oIugnDjbxSwkr8+tjRmW9ldA= +go.opentelemetry.io/collector/receiver/xreceiver v0.132.0/go.mod h1:3pmGNxo3oJ1tCkI6Wfc2ZQhZtSVh4SsmQ8aZ06cghyg= +go.opentelemetry.io/contrib/bridges/otelzap v0.12.0 h1:FGre0nZh5BSw7G73VpT3xs38HchsfPsa2aZtMp0NPOs= +go.opentelemetry.io/contrib/bridges/otelzap v0.12.0/go.mod h1:X2PYPViI2wTPIMIOBjG17KNybTzsrATnvPJ02kkz7LM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= @@ -258,16 +259,16 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/log v0.12.2 h1:yob9JVHn2ZY24byZeaXpTVoPS6l+UrrxmxmPKohXTwc= -go.opentelemetry.io/otel/log v0.12.2/go.mod h1:ShIItIxSYxufUMt+1H5a2wbckGli3/iCfuEbVZi/98E= -go.opentelemetry.io/otel/log/logtest v0.0.0-20250526142609-aa5bd0e64989 h1:4JF7oY9CcHrPGfBLijDcXZyCzGckVEyOjuat5ktmQRg= -go.opentelemetry.io/otel/log/logtest v0.0.0-20250526142609-aa5bd0e64989/go.mod h1:NToOxLDCS1tXDSB2dIj44H9xGPOpKr0csIN+gnuihv4= +go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= +go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= +go.opentelemetry.io/otel/log/logtest v0.13.0 h1:xxaIcgoEEtnwdgj6D6Uo9K/Dynz9jqIxSDu2YObJ69Q= +go.opentelemetry.io/otel/log/logtest v0.13.0/go.mod h1:+OrkmsAH38b+ygyag1tLjSFMYiES5UHggzrtY1IIEA8= go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= -go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= -go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= -go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= @@ -278,19 +279,19 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU= -golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= +golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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/exp v0.0.0-20250717185816-542afb5b7346 h1:vuCObX8mQzik1tfEcYxWZBuVsmQtD1IjxCyPKM18Bh4= -golang.org/x/exp v0.0.0-20250717185816-542afb5b7346/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4= +golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -345,8 +346,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1: google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.74.1 h1:1liE5AupsvQAxKhrVPU9yhMEnyjmMi+F6FUmP9EB2ts= -google.golang.org/grpc v1.74.1/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= +google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= +google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/reporter/internal/pdata/generate.go b/reporter/internal/pdata/generate.go index 40f047572..7d583c70d 100644 --- a/reporter/internal/pdata/generate.go +++ b/reporter/internal/pdata/generate.go @@ -264,7 +264,7 @@ func (p *Pdata) setProfile( log.Debugf("Reporting OTLP profile with %d samples", profile.Sample().Len()) profile.SetDuration(pcommon.Timestamp(endTS - startTS)) - profile.SetStartTime(pcommon.Timestamp(startTS)) + profile.SetTime(pcommon.Timestamp(startTS)) return nil } diff --git a/reporter/internal/pdata/generate_test.go b/reporter/internal/pdata/generate_test.go index eb519b43b..b26ceae8e 100644 --- a/reporter/internal/pdata/generate_test.go +++ b/reporter/internal/pdata/generate_test.go @@ -229,7 +229,7 @@ func TestProfileDuration(t *testing.T) { profile := res.ResourceProfiles().At(0).ScopeProfiles().At(0).Profiles().At(0) require.Equal(t, pcommon.Timestamp(7), profile.Duration()) - require.Equal(t, pcommon.Timestamp(1), profile.StartTime()) + require.Equal(t, pcommon.Timestamp(1), profile.Time()) }) } } From 24b181b25ee686bc795ca6afa789f71bebe9dc2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 19:15:28 +0200 Subject: [PATCH 13/24] chore(deps): update actions/checkout action to v5 (#691) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/auto-tag.yml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/codespell.yml | 2 +- .github/workflows/collector-tests.yml | 2 +- .github/workflows/fossa.yml | 2 +- .github/workflows/ossf-scorecard.yml | 2 +- .github/workflows/push-docker-image.yml | 2 +- .github/workflows/unit-test-on-pull-request.yml | 16 ++++++++-------- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index 315664064..0eda268c6 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Configure Git run: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 98301d50e..7d21bc2f1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -22,7 +22,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up environment uses: ./.github/workflows/env diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 9572ecde5..aa1df54fd 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -13,6 +13,6 @@ jobs: - name: Install codespell run: sudo apt-get install codespell - name: Checkout Repo - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Codespell run: make codespell diff --git a/.github/workflows/collector-tests.yml b/.github/workflows/collector-tests.yml index 7fd8906f2..d5a843516 100644 --- a/.github/workflows/collector-tests.yml +++ b/.github/workflows/collector-tests.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up environment uses: ./.github/workflows/env with: diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index c75e01a97..f9f1bc050 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'open-telemetry/opentelemetry-ebpf-profiler' steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0 with: diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 6f6d59364..e065cafbf 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -19,7 +19,7 @@ jobs: # Needed for GitHub OIDC token if publish_results is true id-token: write steps: - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false diff --git a/.github/workflows/push-docker-image.yml b/.github/workflows/push-docker-image.yml index dfdc876bc..85e66dd84 100644 --- a/.github/workflows/push-docker-image.yml +++ b/.github/workflows/push-docker-image.yml @@ -15,7 +15,7 @@ jobs: if: github.repository == 'open-telemetry/opentelemetry-ebpf-profiler' steps: - name: Checkout code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Login to Docker Hub uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 with: diff --git a/.github/workflows/unit-test-on-pull-request.yml b/.github/workflows/unit-test-on-pull-request.yml index 3b7323e63..9500a6094 100644 --- a/.github/workflows/unit-test-on-pull-request.yml +++ b/.github/workflows/unit-test-on-pull-request.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up environment uses: ./.github/workflows/env - name: Check for changes in licenses of dependencies @@ -34,7 +34,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up environment uses: ./.github/workflows/env - name: Get linter version @@ -57,7 +57,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up environment uses: ./.github/workflows/env with: @@ -81,7 +81,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up environment uses: ./.github/workflows/env - name: Tests @@ -96,7 +96,7 @@ jobs: shell: bash --login {0} steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Hash binary blobs run: | sha256sum support/ebpf/tracer.ebpf.* > binary-blobs.hash @@ -126,7 +126,7 @@ jobs: target_arch: [amd64, arm64] steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up environment uses: ./.github/workflows/env - name: Prepare integration test binaries for qemu tests @@ -142,7 +142,7 @@ jobs: runs-on: macos-latest steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up Go uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: @@ -187,7 +187,7 @@ jobs: - { target_arch: arm64, kernel: 6.12.16 } steps: - name: Clone code - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install dependencies run: | sudo apt-get update -y From f0175ccb99c577edcb2936853b72d853dfe202e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 09:26:37 +0300 Subject: [PATCH 14/24] chore(deps): update github/codeql-action action to v3.29.9 (#692) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7d21bc2f1..628e6caa2 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,7 +28,7 @@ jobs: uses: ./.github/workflows/env - name: Initialize CodeQL - uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 + uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 with: languages: go @@ -37,7 +37,7 @@ jobs: make TARGET_ARCH=${{ matrix.target_arch }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 + uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 with: category: "/language:Go" timeout-minutes: 10 diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index e065cafbf..dd336090f 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -42,6 +42,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 + uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 with: sarif_file: results.sarif From f42e723da4addfba28166b5a882bd835028f2735 Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Thu, 18 Sep 2025 13:25:11 -0400 Subject: [PATCH 15/24] Remove libpf.Trace.Hash (#673) NOTE: Changes skipped via --skip option (had conflicts) From 1540209d17eeb90976404e950fa7230155ab19d1 Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Thu, 14 Aug 2025 10:04:51 -0400 Subject: [PATCH 16/24] golabels: Remove -C flag from golabels test build commands (#699) --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a094517b3..2145ea39f 100644 --- a/Makefile +++ b/Makefile @@ -124,13 +124,13 @@ TEST_INTEGRATION_BINARY_DIRS := tracer processmanager/ebpf support # These binaries are named ".test" to get included into bluebox initramfs support/golbls_1_23.test: generate ebpf - CGO_ENABLED=0 GOTOOLCHAIN=go1.23.7 go test -C ./interpreter/golabels/integrationtests -c -trimpath -tags $(GO_TAGS),nocgo,integration -o $@ + CGO_ENABLED=0 GOTOOLCHAIN=go1.23.7 go test ./interpreter/golabels/integrationtests -c -trimpath -tags $(GO_TAGS),nocgo,integration -o $@ support/golbls_1_24.test: generate ebpf - CGO_ENABLED=0 GOTOOLCHAIN=go1.24.1 go test -C ./interpreter/golabels/integrationtests -c -trimpath -tags $(GO_TAGS),nocgo,integration -o $@ + CGO_ENABLED=0 GOTOOLCHAIN=go1.24.1 go test ./interpreter/golabels/integrationtests -c -trimpath -tags $(GO_TAGS),nocgo,integration -o $@ support/golbls_cgo.test: generate ebpf - CGO_ENABLED=1 GOTOOLCHAIN=go1.24.1 go test -C ./interpreter/golabels/integrationtests -c -ldflags '-extldflags "-static"' -trimpath -tags $(GO_TAGS),withcgo,integration -o $@ + CGO_ENABLED=1 GOTOOLCHAIN=go1.24.1 go test ./interpreter/golabels/integrationtests -c -ldflags '-extldflags "-static"' -trimpath -tags $(GO_TAGS),withcgo,integration -o $@ integration-test-binaries: support/golbls_1_23.test support/golbls_1_24.test support/golbls_cgo.test $(foreach test_name, $(TEST_INTEGRATION_BINARY_DIRS), \ From 3abe1d6cb3c21ead0952a1a7a7c6aeb486ac3246 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 16:32:20 +0200 Subject: [PATCH 17/24] chore(deps): update rust crate object to v0.37.3 (#695) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac820c737..eb4e6c5c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,9 +316,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "object" -version = "0.37.2" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e3d0a7419f081f4a808147e845310313a39f322d7ae1f996b7f001d6cbed04" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] From e4c4f6536c46d193743535a27ff508232dac15ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 13:49:13 +0200 Subject: [PATCH 18/24] chore(deps): update rust crate thiserror to v2.0.15 (#705) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb4e6c5c6..9fcdc88f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -600,18 +600,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.14" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.14" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", From 9920c3746de527c67db7195d55c14a25c597cd8f Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Mon, 18 Aug 2025 17:01:12 +0200 Subject: [PATCH 19/24] doc: refactor README.md (#703) Signed-off-by: Florian Lehner Co-authored-by: Christos Kalkanis --- README.md | 389 +---------------------------------------------- doc/internals.md | 385 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 392 insertions(+), 382 deletions(-) create mode 100644 doc/internals.md diff --git a/README.md b/README.md index 3c3e689fd..3b93edab3 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,10 @@ eBPF. ## Building +We are working towards integrating the profiling functionality into the [OTel Collector](https://opentelemetry.io/docs/collector/) as a receiver, +which will be the supported configuration going forward. In the meantime, we also offer a standalone profiling agent binary named `ebpf-profiler`, +to aid with development and debugging. The expectation is that this will go away once the integration with the [OTel Collector](https://opentelemetry.io/docs/collector/) is complete. + ## Platform Requirements The agent can be built with the provided make targets. Docker is required for containerized builds, and both amd64 and arm64 architectures are supported. @@ -45,7 +49,7 @@ The agent can be built with the provided make targets. Docker is required for co ```sh make agent TARGET_ARCH=arm64 ``` -The resulting binary will be named in the current directory. +The resulting binary will be named `ebpf-profiler` in the current directory. ## Other OSes Since the profiler is Linux-only, macOS and Windows users need to set up a Linux VM to build and run the agent. Ensure the appropriate architecture is specified if using cross-compilation. Use the same make targets as above after the Linux environment is configured in the VM. @@ -79,389 +83,10 @@ of the recently released OTel profiling [signal](https://github.com/open-telemet The agent loads the eBPF program and its maps, starts unwinding and reports captured traces to the backend. -## Agent internals - -The host agent is a Go application that is deployed to all machines customers -wish to profile. It collects, processes and pushes observed stack traces and -related meta-information to a backend collector. - -### Concepts - -#### File IDs - -A file ID uniquely identifies an executable, kernel or script language source -file. - -File IDs for native applications are created by taking the SHA256 checksum of a -file's head, tail, and size, then truncating the hash digest to 16 bytes (128 -bits): - -``` -Input ← Concat(File[:4096], File[-4096:], BigEndianUInt64(Len(File))) -Digest ← SHA256(Input) -FileID ← Digest[:16] -``` - -File IDs for script and JIT languages are created in an interpreter-specific -fashion. - -File IDs for Linux kernels are calculated by taking the FNV128 hash of their GNU -build ID. - -#### Stack unwinding - -Stack unwinding is the process of recovering the list of function calls that -lead execution to the point in the program at which the profiler interrupted it. - -How stacks are unwound varies depending on whether a thread is running native, -JITed or interpreted code, but the basic idea is always the same: every language -that supports arbitrarily nested function calls needs a way to keep track of -which function it needs to return to after the current function completes. Our -unwinder uses that same information to repeatedly determine the caller until we -reach the thread's entry point. - -In simplified pseudo-code: - -``` -pc ← interrupted_process.cpu.pc -sp ← interrupted_process.cpu.sp - -while !is_entry_point(pc): - file_id, start_addr, interp_type ← file_id_at_pc(pc) - push_frame(interp_type, file_id, pc - start_addr) - unwinder ← unwinder_for_interp(interp_type) - pc, sp ← unwinder.next_frame(pc, sp) -``` - -#### Symbolization - -Symbolization is the process of assigning source line information to the raw -addresses extracted during stack unwinding. - -For script and JIT languages that always have symbol information available on -the customer machines, the host agent is responsible for symbolizing frames. - -For native code the symbolization occurs in the backend. Stack frames are sent -as file IDs and the offset within the file and the symbolization service is then -responsible for assigning the correct function name, source file and lines in -the background. Symbols for open-source software installed from OS package repos -are pulled in from our global symbolization infrastructure and symbols for -private executables can be manually uploaded by the customer. - -The primary reason for doing native symbolization in the backend is that native -executables in production will often be stripped. Asking the customer to deploy -symbols to production would be both wasteful in terms of disk usage and also a -major friction point in initial adoption. - -#### Stack trace representation - -We have two major representations for our stack traces. - -The raw trace format produced by our BPF unwinders: - -https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/host/host.go#L60-L66 - -The final format produced after additional processing in user-land: - -https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/libpf/libpf.go#L458-L463 - -The two might look rather similar at first glance, but there are some important differences: - -- the BPF variant uses truncated 64-bit file IDs to save precious kernel memory -- for interpreter frames the BPF variant uses the file ID and line number fields to store - more or less arbitrary interpreter-specific data that is needed by the user-mode code to - conduct symbolization - -A third trace representation exists within our network protocol, but it essentially -just a deduplicated, compressed representation of the user-land trace format. - -#### Trace hashing - -In profiling it is common to see the same trace many times. Traces can be up to -128 entries long, and repeatedly symbolizing and sending the same traces over the -network would be very wasteful. We use trace hashing to avoid this. Different -hashing schemes are used for the BPF and user-mode trace representations. Multiple -64 bit hashes can end up being mapped to the same 128 bit hash, but *not* vice-versa. - -**BPF trace hash (64 bit):** - -``` -H(kernel_stack_id, frames_user, PID) -``` - -**User-land trace hash (128 bit)** - -``` -H(frames_user_kernel) -``` - -### User-land sub-components - -#### Tracer - -The tracer is a central user-land component that loads and attaches our BPF -programs to their corresponding BPF probes during startup and then continues to -serve as the primary event pump for BPF <-> user-land communication. It further -instantiates and owns other important subcomponents like the process manager. - -#### Trace handler - -The trace handler is responsible for converting traces from the BPF format to -the user-space format. It receives raw traces [tracer](#tracer), converts them -to the user-space format and then sends them on to the [reporter](#reporter). -The majority of the conversion logic happens via a call into the process -manager's [`ConvertTrace`] function. - -Since converting and enriching BPF-format traces is not a cheap operation, the -trace handler is also responsible for keeping a cache (mapping) of trace hashes: -from 64bit BPF hash to the user-space 128bit hash. - -[`ConvertTrace`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/processmanager/manager.go#L208 - -#### Reporter - -The reporter receives traces and trace counts in the user-mode format from the -[trace handler](#trace-handler), converts them to the gRPC representation and -then sends them out to a backend collector. - -It also receives additional meta-information (such as [metrics](metrics/metrics.json) and [host metadata](hostmetadata/hostmetadata.json)) -which it also converts and sends out to a backend collector over gRPC. +## Development -The reporter does not offer strong guarantees regarding reliability of -network operations and may drop data at any point, an "eventual consistency" -model. +To understand how this project works and learn more about profiling, check out [Profiling internals](doc/internals.md) -#### Process manager - -The process manager receives process creation/termination events from -[tracer](#tracer) and is responsible for making available any information to the -BPF code that it needs to conduct unwinding. It maintains a map of the -executables mapped into each process, loads stack unwinding deltas for native -modules and creates interpreter handlers for each memory mapping that belongs to -a supported language interpreter. - -During trace conversion the process manager is further responsible for routing -symbolization requests to the correct interpreter handlers. - -#### Interpreter handlers - -Each interpreted or JITed language that we support has a corresponding type that -implements the interpreter handler interface. It is responsible for: - -- detecting the interpreter's version and structure layouts -- placing information that the corresponding BPF interpreter unwinder needs into BPF maps -- translating interpreter frames from the BPF format to the user-land format by symbolizing them - -#### Stack delta provider - -Unwinding the stack of native executables compiled without frame pointers -requires stack deltas. These deltas are essentially a mapping from each PC in an -executable to instructions describing how to find the caller and how to adjust -the unwinder machine state in preparation of locating the next frame. Typically -these instructions consist of a register that is used as a base address and an -offset (delta) that needs to be added to it -- hence the name. The stack delta -provider is responsible for analyzing executables and creating stack deltas for -them. - -For most native executables, we rely on the information present in `.eh_frame`. -`.eh_frame` was originally meant only for C++ exception unwinding, but it has -since been repurposed for stack unwinding in general. Even applications written -in many other native languages like C, Zig or Rust will typically come with -`.eh_frame`. - -One important exception to this general pattern is Go. As of writing, Go -executables do not come with `.eh_frame` sections unless they are built with CGo -enabled. Even with CGo the `.eh_frame` section will only contain information for -a small subset of functions that are either written in C/C++ or part of the CGo -runtime. For Go executables we extract the stack delta information from the -Go-specific section called `.gopclntab`. In-depth documentation on the format is -available in [a separate document](doc/gopclntab.md)). - -### BPF components - -The BPF portion of the host agent implements the actual stack unwinding. It uses -the eBPF virtual machine to execute our code directly in the Linux kernel. The -components are implemented in BPF C and live in the -[`opentelemetry-ebpf-profiler/support/ebpf`](./support/ebpf) directory. - -#### Limitations - -BPF programs must adhere to various restrictions imposed by the verifier. Many -of these limitations are significantly relaxed in newer kernel versions, but we -still have to stick to the old limits because we wish to continue supporting -older kernels. - -The minimum supported Linux kernel versions are -- 4.19 for amd64/x86_64 -- 5.5 for arm64/aarch64 - -The most notable limitations are the following two: - -- **4096 instructions per program**\ - A single BPF program can consist of a maximum of 4096 instructions, otherwise - older kernels will refuse to load it. Since BPF does not allow for loops, they - instead need to be unrolled. -- **32 tail-calls**\ - Linux allows BPF programs to do a tail-call to another BPF program. A tail - call is essentially a `jmp` into another BPF program, ending execution of the - current handler and starting a new one. This allows us to circumvent the 4096 - instruction limit a bit by doing a tail-call before we run into the limit. - There's a maximum of 32 tail calls that a BPF program can do. - -These limitations mean that we generally try to prepare as much work as possible -in user-land and then only do the minimal work necessary within BPF. We can only -use $O(\log{n})$ algorithms at worst and try to stick with $O(1)$ for most things. -All processing that cannot be implemented like this must be delegated to -user-land. As a general rule of thumb, anything that needs more than 32 -iterations in a loop is out of the question for BPF. - -#### Unwinders - -Unwinding always begins in [`native_tracer_entry`]. This entry point for our -tracer starts by reading the register state of the thread that we just -interrupted and initializes the [`PerCPURecord`] structure. The per-CPU record -persists data between tail-calls of the same unwinder invocation. The unwinder's -current `PC`, `SP` etc. values are initialized from register values. - -After the initial setup the entry point consults a BPF map that is maintained -by the user-land portion of the agent to determine which interpreter unwinder -is responsible for unwinding the code at `PC`. If a record for the memory -region is found, we then tail-call to the corresponding interpreter unwinder. - -Each interpreter unwinder has their own BPF program. The interpreter unwinders -typically have an unrolled main loop where they try to unwind as many frames for -that interpreter as they can without going over the instruction limit. After -each iteration the unwinders will typically check whether the current PC value -still belongs to the current unwinder and tail-call to the right unwinder -otherwise. - -When an unwinder detects that we've reached the last frame in the trace, -unwinding is terminated with a tail call to [`unwind_stop`]. For most traces -this call will happen in the native unwinder, since even JITed languages -usually call through a few layers of native C/C++ code before entering the VM. -We detect the end of a trace by heuristically marking certain functions with -`PROG_UNWIND_STOP` in the BPF maps prepared by user-land. `unwind_stop` then -sends the completed BPF trace to user-land. - -If any frame in the trace requires symbolization in user-mode, we additionally -send a BPF event to request an expedited read from user-land. For all other -traces user-land will simply read and then clear this map on a timer. - -[`native_tracer_entry`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/support/ebpf/native_stack_trace.ebpf.c#L875 -[`PerCPURecord`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/support/ebpf/types.h#L576 -[`unwind_stop`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/support/ebpf/interpreter_dispatcher.ebpf.c#L125 - -#### PID events - -The BPF components are responsible for notifying user-land about new and exiting -processes. An event about a new process is produced when we first interrupt it -with the unwinders. Events about exiting processes are created with a -`sched_process_free` tracepoint. In both cases the BPF code sends a perf event to -notify user-land. We also re-report a PID if we detect execution in previously -unknown memory region to prompt re-scan of the mappings. Finally, the profiler -can also profile processes whose main thread exits, leaving other threads running. - -### Network protocol - -All collected information is reported to a backend collector via a push-based, -stateless, one-way gRPC [protocol](https://github.com/open-telemetry/opentelemetry-proto/pull/534). - -All data to be transmitted is stored in bounded FIFO queues (ring buffers). Old -data is overwritten when the queues fill up (e.g. due to a lagging or offline -backend collector). There is no explicit reliability or redundancy (besides -retries internal to gRPC) and the assumption is that data will be resent -(eventually consistent). - -### Trace processing pipeline - -The host agent contains an internal pipeline that incrementally processes the -raw traces that are produced by the BPF unwinders, enriches them with additional -information (e.g. symbols for interpreter frames and container info), deduplicates -known traces and combines trace counts that occurred in the same update period. - -The traces produced in BPF start out with the information shown in the following -diagram. - -
-Note: please read this if you wish to update the diagrams - -The diagrams in this section were created via draw.io. The SVGs can be loaded -into draw.io for editing. When you're done, make sure to export via -File -> Export As -> SVG and then select -a zoom level of 200%. If you simply save the diagram via CTRL+S, -it won't fill the whole width of the documentation page. Also make sure that -"Include a copy of my diagram" remains ticked to keep the diagram editable. - -
- -![bpf-trace-diagram](doc/bpf-trace.drawio.svg) - -Our backend collector expects to receive trace information in a normalized and -enriched format. This diagram below is relatively close to the data-structures -that are actually sent over the network, minus the batching and domain-specific -deduplication that we apply prior to sending it out. - -![net-trace-diagram](doc/network-trace.drawio.svg) - -The diagram below provides a detailed overview on how the various components of -the host agent interact to transform raw traces into the network format. It -is focused around our data structures and how data flows through them. Dotted -lines represent indirect interaction with data structures, solid ones correspond -to code flow. "UM" is short for "user mode". - -![trace-pipe-diagram](doc/trace-pipe.drawio.svg) - -### Testing strategy - -The host agent code is tested with three test suites: - -- **Go unit tests**\ - Functionality of individual functions and types is tested with regular Go unit - tests. This works great for the user-land portion of the agent, but is unable - to test any of the unwinding logic and BPF interaction. -- **coredump test suite**\ - The coredump test suite (`tools/coredump`) we compile the whole BPF unwinder - code into a user-mode executable, then use the information from a coredump to - simulate a realistic environment to test the unwinder code in. The coredump - suite essentially implements all required BPF helper functions in user-space, - reading memory and thread contexts from the coredump. The resulting traces are - then compared to a frame list in a JSON file, serving as regression tests. -- **BPF integration tests**\ - A special build of the host agent with the `integration` tag is created that - enables specialized test cases that actually load BPF tracers into the kernel. - These test cases require root privileges and thus cannot be part of the - regular unit test suite. The test cases focus on covering the interaction and - communication of BPF with user-mode code, as well as testing that our BPF code - passes the BPF verifier. Our CI builds the integration test executable once and - then executes it on a wide range of different Linux kernel versions via qemu. - -### Probabilistic profiling - -Probabilistic profiling allows you to reduce storage costs by collecting a representative -sample of profiling data. This method decreases storage costs with a visibility trade-off, -as not all Profiling Host Agents will have profile collection enabled at all times. - -Profiling Events linearly correlate with the probabilistic profiling value. The lower the value, -the fewer events are collected. - -#### Configure probabilistic profiling - -To configure probabilistic profiling, set the `-probabilistic-threshold` and `-probabilistic-interval` options. - -Set the `-probabilistic-threshold` option to a unsigned integer between 1 and 99 to enable - probabilistic profiling. At every probabilistic interval, a random number between 0 and 99 is chosen. - If the probabilistic threshold that you've set is greater than this random number, the agent collects - profiles from this system for the duration of the interval. The default value is 100. - -Set the `-probabilistic-interval` option to a time duration to define the time interval for which -probabilistic profiling is either enabled or disabled. The default value is 1 minute. - -#### Example - -The following example shows how to configure the profiling agent with a threshold of 50 and an interval of 2 minutes and 30 seconds: -```bash -sudo ./ebpf-profiler -probabilistic-threshold=50 -probabilistic-interval=2m30s -``` # Legal diff --git a/doc/internals.md b/doc/internals.md new file mode 100644 index 000000000..32995e541 --- /dev/null +++ b/doc/internals.md @@ -0,0 +1,385 @@ +# Profiling internals + +> **Note:** This project is still under development. Components, APIs, and behaviors may or already have changed. + +This a Go application that is deployed to all machines customers +wish to profile. It collects, processes and pushes observed stack traces and +related meta-information to a backend collector. + +## Concepts + +### File IDs + +A file ID uniquely identifies an executable, kernel or script language source +file. + +File IDs for native applications are created by taking the SHA256 checksum of a +file's head, tail, and size, then truncating the hash digest to 16 bytes (128 +bits): + +``` +Input ← Concat(File[:4096], File[-4096:], BigEndianUInt64(Len(File))) +Digest ← SHA256(Input) +FileID ← Digest[:16] +``` + +File IDs for script and JIT languages are created in an interpreter-specific +fashion. + +File IDs for Linux kernels are calculated by taking the FNV128 hash of their GNU +build ID. + +### Stack unwinding + +Stack unwinding is the process of recovering the list of function calls that +lead execution to the point in the program at which the profiler interrupted it. + +How stacks are unwound varies depending on whether a thread is running native, +JITed or interpreted code, but the basic idea is always the same: every language +that supports arbitrarily nested function calls needs a way to keep track of +which function it needs to return to after the current function completes. Our +unwinder uses that same information to repeatedly determine the caller until we +reach the thread's entry point. + +In simplified pseudo-code: + +``` +pc ← interrupted_process.cpu.pc +sp ← interrupted_process.cpu.sp + +while !is_entry_point(pc): + file_id, start_addr, interp_type ← file_id_at_pc(pc) + push_frame(interp_type, file_id, pc - start_addr) + unwinder ← unwinder_for_interp(interp_type) + pc, sp ← unwinder.next_frame(pc, sp) +``` + +### Symbolization + +Symbolization is the process of assigning source line information to the raw +addresses extracted during stack unwinding. + +For script and JIT languages that always have symbol information available on +the customer machines, the host agent is responsible for symbolizing frames. + +For native code the symbolization occurs in the backend. Stack frames are sent +as file IDs and the offset within the file and the symbolization service is then +responsible for assigning the correct function name, source file and lines in +the background. Symbols for open-source software installed from OS package repos +are pulled in from our global symbolization infrastructure and symbols for +private executables can be manually uploaded by the customer. + +The primary reason for doing native symbolization in the backend is that native +executables in production will often be stripped. Asking the customer to deploy +symbols to production would be both wasteful in terms of disk usage and also a +major friction point in initial adoption. + +### Stack trace representation + +We have two major representations for our stack traces. + +The raw trace format produced by our BPF unwinders: + +https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/host/host.go#L60-L66 + +The final format produced after additional processing in user-land: + +https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/libpf/libpf.go#L458-L463 + +The two might look rather similar at first glance, but there are some important differences: + +- the BPF variant uses truncated 64-bit file IDs to save precious kernel memory +- for interpreter frames the BPF variant uses the file ID and line number fields to store + more or less arbitrary interpreter-specific data that is needed by the user-mode code to + conduct symbolization + +A third trace representation exists within our network protocol, but it essentially +just a deduplicated, compressed representation of the user-land trace format. + +### Trace hashing + +In profiling it is common to see the same trace many times. Traces can be up to +128 entries long, and repeatedly symbolizing and sending the same traces over the +network would be very wasteful. We use trace hashing to avoid this. Different +hashing schemes are used for the BPF and user-mode trace representations. Multiple +64 bit hashes can end up being mapped to the same 128 bit hash, but *not* vice-versa. + +**BPF trace hash (64 bit):** + +``` +H(kernel_stack_id, frames_user, PID) +``` + +**User-land trace hash (128 bit)** + +``` +H(frames_user_kernel) +``` + +## User-land sub-components + +### Tracer + +The tracer is a central user-land component that loads and attaches our BPF +programs to their corresponding BPF probes during startup and then continues to +serve as the primary event pump for BPF <-> user-land communication. It further +instantiates and owns other important subcomponents like the process manager. + +### Trace handler + +The trace handler is responsible for converting traces from the BPF format to +the user-space format. It receives raw traces [tracer](#tracer), converts them +to the user-space format and then sends them on to the [reporter](#reporter). +The majority of the conversion logic happens via a call into the process +manager's [`ConvertTrace`] function. + +Since converting and enriching BPF-format traces is not a cheap operation, the +trace handler is also responsible for keeping a cache (mapping) of trace hashes: +from 64bit BPF hash to the user-space 128bit hash. + +[`ConvertTrace`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/processmanager/manager.go#L208 + +### Reporter + +The reporter receives traces and trace counts in the user-mode format from the +[trace handler](#trace-handler), converts them to the gRPC representation and +then sends them out to a backend collector. + +It also receives additional meta-information (such as [metrics](./../metrics/metrics.json) and [host metadata](./..hostmetadata/hostmetadata.json)) +which it also converts and sends out to a backend collector over gRPC. + +The reporter does not offer strong guarantees regarding reliability of +network operations and may drop data at any point, an "eventual consistency" +model. + +### Process manager + +The process manager receives process creation/termination events from +[tracer](#tracer) and is responsible for making available any information to the +BPF code that it needs to conduct unwinding. It maintains a map of the +executables mapped into each process, loads stack unwinding deltas for native +modules and creates interpreter handlers for each memory mapping that belongs to +a supported language interpreter. + +During trace conversion the process manager is further responsible for routing +symbolization requests to the correct interpreter handlers. + +### Interpreter handlers + +Each interpreted or JITed language that we support has a corresponding type that +implements the interpreter handler interface. It is responsible for: + +- detecting the interpreter's version and structure layouts +- placing information that the corresponding BPF interpreter unwinder needs into BPF maps +- translating interpreter frames from the BPF format to the user-land format by symbolizing them + +### Stack delta provider + +Unwinding the stack of native executables compiled without frame pointers +requires stack deltas. These deltas are essentially a mapping from each PC in an +executable to instructions describing how to find the caller and how to adjust +the unwinder machine state in preparation of locating the next frame. Typically +these instructions consist of a register that is used as a base address and an +offset (delta) that needs to be added to it -- hence the name. The stack delta +provider is responsible for analyzing executables and creating stack deltas for +them. + +For most native executables, we rely on the information present in `.eh_frame`. +`.eh_frame` was originally meant only for C++ exception unwinding, but it has +since been repurposed for stack unwinding in general. Even applications written +in many other native languages like C, Zig or Rust will typically come with +`.eh_frame`. + +One important exception to this general pattern is Go. As of writing, Go +executables do not come with `.eh_frame` sections unless they are built with CGo +enabled. Even with CGo the `.eh_frame` section will only contain information for +a small subset of functions that are either written in C/C++ or part of the CGo +runtime. For Go executables we extract the stack delta information from the +Go-specific section called `.gopclntab`. In-depth documentation on the format is +available in [a separate document](gopclntab.md)). + +## BPF components + +The BPF portion of the host agent implements the actual stack unwinding. It uses +the eBPF virtual machine to execute our code directly in the Linux kernel. The +components are implemented in BPF C and live in the +[`opentelemetry-ebpf-profiler/support/ebpf`](./../support/ebpf) directory. + +### Limitations + +BPF programs must adhere to various restrictions imposed by the verifier. Many +of these limitations are significantly relaxed in newer kernel versions, but we +still have to stick to the old limits because we wish to continue supporting +older kernels. + +The minimum supported Linux kernel versions are +- 5.4 for amd64/x86_64 +- 5.5 for arm64/aarch64 + +The most notable limitations are the following two: + +- **4096 instructions per program**\ + A single BPF program can consist of a maximum of 4096 instructions, otherwise + older kernels will refuse to load it. Since BPF does not allow for loops, they + instead need to be unrolled. +- **32 tail-calls**\ + Linux allows BPF programs to do a tail-call to another BPF program. A tail + call is essentially a `jmp` into another BPF program, ending execution of the + current handler and starting a new one. This allows us to circumvent the 4096 + instruction limit a bit by doing a tail-call before we run into the limit. + There's a maximum of 32 tail calls that a BPF program can do. + +These limitations mean that we generally try to prepare as much work as possible +in user-land and then only do the minimal work necessary within BPF. We can only +use $O(\log{n})$ algorithms at worst and try to stick with $O(1)$ for most things. +All processing that cannot be implemented like this must be delegated to +user-land. As a general rule of thumb, anything that needs more than 32 +iterations in a loop is out of the question for BPF. + +### Unwinders + +Unwinding always begins in [`native_tracer_entry`]. This entry point for our +tracer starts by reading the register state of the thread that we just +interrupted and initializes the [`PerCPURecord`] structure. The per-CPU record +persists data between tail-calls of the same unwinder invocation. The unwinder's +current `PC`, `SP` etc. values are initialized from register values. + +After the initial setup the entry point consults a BPF map that is maintained +by the user-land portion of the agent to determine which interpreter unwinder +is responsible for unwinding the code at `PC`. If a record for the memory +region is found, we then tail-call to the corresponding interpreter unwinder. + +Each interpreter unwinder has their own BPF program. The interpreter unwinders +typically have an unrolled main loop where they try to unwind as many frames for +that interpreter as they can without going over the instruction limit. After +each iteration the unwinders will typically check whether the current PC value +still belongs to the current unwinder and tail-call to the right unwinder +otherwise. + +When an unwinder detects that we've reached the last frame in the trace, +unwinding is terminated with a tail call to [`unwind_stop`]. For most traces +this call will happen in the native unwinder, since even JITed languages +usually call through a few layers of native C/C++ code before entering the VM. +We detect the end of a trace by heuristically marking certain functions with +`PROG_UNWIND_STOP` in the BPF maps prepared by user-land. `unwind_stop` then +sends the completed BPF trace to user-land. + +If any frame in the trace requires symbolization in user-mode, we additionally +send a BPF event to request an expedited read from user-land. For all other +traces user-land will simply read and then clear this map on a timer. + +[`native_tracer_entry`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/support/ebpf/native_stack_trace.ebpf.c#L875 +[`PerCPURecord`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/support/ebpf/types.h#L576 +[`unwind_stop`]: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/0945fe628da5c4854d55dd95e5dc4b4cf46a3c76/support/ebpf/interpreter_dispatcher.ebpf.c#L125 + +### PID events + +The BPF components are responsible for notifying user-land about new and exiting +processes. An event about a new process is produced when we first interrupt it +with the unwinders. Events about exiting processes are created with a +`sched_process_free` tracepoint. In both cases the BPF code sends a perf event to +notify user-land. We also re-report a PID if we detect execution in previously +unknown memory region to prompt re-scan of the mappings. Finally, the profiler +can also profile processes whose main thread exits, leaving other threads running. + +## Network protocol + +All collected information is reported to a backend collector via a push-based, +stateless, one-way gRPC [protocol](https://github.com/open-telemetry/opentelemetry-proto/pull/534). + +All data to be transmitted is stored in bounded FIFO queues (ring buffers). Old +data is overwritten when the queues fill up (e.g. due to a lagging or offline +backend collector). There is no explicit reliability or redundancy (besides +retries internal to gRPC) and the assumption is that data will be resent +(eventually consistent). + +## Trace processing pipeline + +The host agent contains an internal pipeline that incrementally processes the +raw traces that are produced by the BPF unwinders, enriches them with additional +information (e.g. symbols for interpreter frames and container info), deduplicates +known traces and combines trace counts that occurred in the same update period. + +The traces produced in BPF start out with the information shown in the following +diagram. + +
+Note: please read this if you wish to update the diagrams + +The diagrams in this section were created via draw.io. The SVGs can be loaded +into draw.io for editing. When you're done, make sure to export via +File -> Export As -> SVG and then select +a zoom level of 200%. If you simply save the diagram via CTRL+S, +it won't fill the whole width of the documentation page. Also make sure that +"Include a copy of my diagram" remains ticked to keep the diagram editable. + +
+ +![bpf-trace-diagram](bpf-trace.drawio.svg) + +Our backend collector expects to receive trace information in a normalized and +enriched format. This diagram below is relatively close to the data-structures +that are actually sent over the network, minus the batching and domain-specific +deduplication that we apply prior to sending it out. + +![net-trace-diagram](network-trace.drawio.svg) + +The diagram below provides a detailed overview on how the various components of +the host agent interact to transform raw traces into the network format. It +is focused around our data structures and how data flows through them. Dotted +lines represent indirect interaction with data structures, solid ones correspond +to code flow. "UM" is short for "user mode". + +![trace-pipe-diagram](trace-pipe.drawio.svg) + +## Testing strategy + +The host agent code is tested with three test suites: + +- **Go unit tests**\ + Functionality of individual functions and types is tested with regular Go unit + tests. This works great for the user-land portion of the agent, but is unable + to test any of the unwinding logic and BPF interaction. +- **coredump test suite**\ + The coredump test suite (`tools/coredump`) we compile the whole BPF unwinder + code into a user-mode executable, then use the information from a coredump to + simulate a realistic environment to test the unwinder code in. The coredump + suite essentially implements all required BPF helper functions in user-space, + reading memory and thread contexts from the coredump. The resulting traces are + then compared to a frame list in a JSON file, serving as regression tests. +- **BPF integration tests**\ + A special build of the host agent with the `integration` tag is created that + enables specialized test cases that actually load BPF tracers into the kernel. + These test cases require root privileges and thus cannot be part of the + regular unit test suite. The test cases focus on covering the interaction and + communication of BPF with user-mode code, as well as testing that our BPF code + passes the BPF verifier. Our CI builds the integration test executable once and + then executes it on a wide range of different Linux kernel versions via qemu. + +## Probabilistic profiling + +Probabilistic profiling allows you to reduce storage costs by collecting a representative +sample of profiling data. This method decreases storage costs with a visibility trade-off, +as not all Profiling Host Agents will have profile collection enabled at all times. + +Profiling Events linearly correlate with the probabilistic profiling value. The lower the value, +the fewer events are collected. + +### Configure probabilistic profiling + +To configure probabilistic profiling, set the `-probabilistic-threshold` and `-probabilistic-interval` options. + +Set the `-probabilistic-threshold` option to a unsigned integer between 1 and 99 to enable + probabilistic profiling. At every probabilistic interval, a random number between 0 and 99 is chosen. + If the probabilistic threshold that you've set is greater than this random number, the agent collects + profiles from this system for the duration of the interval. The default value is 100. + +Set the `-probabilistic-interval` option to a time duration to define the time interval for which +probabilistic profiling is either enabled or disabled. The default value is 1 minute. + +### Example + +The following example shows how to configure the profiling agent with a threshold of 50 and an interval of 2 minutes and 30 seconds: +```bash +sudo ./ebpf-profiler -probabilistic-threshold=50 -probabilistic-interval=2m30s +``` \ No newline at end of file From cc2e9b4c2c15411e98a71a527a54a6f24a4dd55b Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Mon, 18 Aug 2025 17:01:45 +0200 Subject: [PATCH 20/24] CI: use `go mod tidy` as post update step (#696) --- .github/renovate.json5 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index f887740fe..b8b82728f 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -4,6 +4,9 @@ "config:best-practices", "helpers:pinGitHubActionDigestsToSemver" ], + "postUpdateOptions" : [ + "gomodTidy" + ], "packageRules": [ { "groupName": "Go dependencies", From e989cecea354cc8962912975313df5414f1ba09b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:54:13 +0200 Subject: [PATCH 21/24] chore(deps): update rust crate serde_json to v1.0.143 (#709) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fcdc88f0..b18ba6011 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,9 +507,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", From e25c61f72c2dc72a6eda339c982a34b9fc9488ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:54:28 +0200 Subject: [PATCH 22/24] chore(deps): update github/codeql-action action to v3.29.10 (#708) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 628e6caa2..1a18ff5b9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,7 +28,7 @@ jobs: uses: ./.github/workflows/env - name: Initialize CodeQL - uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 + uses: github/codeql-action/init@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.10 with: languages: go @@ -37,7 +37,7 @@ jobs: make TARGET_ARCH=${{ matrix.target_arch }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 + uses: github/codeql-action/analyze@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.10 with: category: "/language:Go" timeout-minutes: 10 diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index dd336090f..9f803e011 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -42,6 +42,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9 + uses: github/codeql-action/upload-sarif@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.10 with: sarif_file: results.sarif From b2e0ab456588bb9427c11f654bbd44115710aed7 Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Tue, 19 Aug 2025 12:20:44 -0400 Subject: [PATCH 23/24] interpreter: Support multiple interpreters for single ELF object (#702) Co-authored-by: Christos Kalkanis --- .../integrationtests/golabels_test.go | 1 + interpreter/multi.go | 143 ++++++++++++++++++ processmanager/execinfomanager/manager.go | 22 ++- processmanager/manager.go | 14 +- 4 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 interpreter/multi.go diff --git a/interpreter/golabels/integrationtests/golabels_test.go b/interpreter/golabels/integrationtests/golabels_test.go index e8c72bd26..ae5c1a33f 100644 --- a/interpreter/golabels/integrationtests/golabels_test.go +++ b/interpreter/golabels/integrationtests/golabels_test.go @@ -93,6 +93,7 @@ func Test_Golabels(t *testing.T) { enabledTracers, _ := tracertypes.Parse("") enabledTracers.Enable(tracertypes.Labels) + enabledTracers.Enable(tracertypes.GoTracer) trc, err := tracer.NewTracer(ctx, &tracer.Config{ Reporter: &mockReporter{}, diff --git a/interpreter/multi.go b/interpreter/multi.go new file mode 100644 index 000000000..54fc57eec --- /dev/null +++ b/interpreter/multi.go @@ -0,0 +1,143 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package interpreter // import "go.opentelemetry.io/ebpf-profiler/interpreter" + +import ( + "errors" + + log "github.com/sirupsen/logrus" + "go.opentelemetry.io/ebpf-profiler/host" + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/metrics" + "go.opentelemetry.io/ebpf-profiler/process" + "go.opentelemetry.io/ebpf-profiler/remotememory" + "go.opentelemetry.io/ebpf-profiler/reporter" + "go.opentelemetry.io/ebpf-profiler/tpbase" +) + +// MultiData implements the Data interface for multiple interpreters. +type MultiData struct { + interpreters []Data +} + +// NewMultiData creates a new MultiData instance from multiple Data instances. +func NewMultiData(interpreters []Data) *MultiData { + return &MultiData{ + interpreters: interpreters, + } +} + +// Attach attaches all interpreters and returns a MultiInstance. +func (m *MultiData) Attach(ebpf EbpfHandler, pid libpf.PID, bias libpf.Address, + rm remotememory.RemoteMemory) (Instance, error) { + var instances []Instance + var errs []error + + for _, data := range m.interpreters { + instance, err := data.Attach(ebpf, pid, bias, rm) + if err != nil { + errs = append(errs, err) + continue + } + if instance != nil { + instances = append(instances, instance) + } + } + + err := errors.Join(errs...) + if len(instances) == 0 { + // Either all interpreters returned nil instances without error (e.g., not ready yet) + // in which case return nil, nil (valid state) otherwise return combined error. + return nil, err + } + + // We got at least one valid instance, log any errors that occurred + if err != nil { + log.Errorf("Errors occurred while attaching interpreters: %v", err) + } + + return NewMultiInstance(instances), nil +} + +// Unload unloads all interpreters. +func (m *MultiData) Unload(ebpf EbpfHandler) { + for _, data := range m.interpreters { + data.Unload(ebpf) + } +} + +// MultiInstance implements the Instance interface for multiple interpreters. +type MultiInstance struct { + instances []Instance +} + +// NewMultiInstance creates a new MultiInstance from multiple Instance instances. +func NewMultiInstance(instances []Instance) *MultiInstance { + return &MultiInstance{ + instances: instances, + } +} + +// Detach detaches all interpreter instances. +func (m *MultiInstance) Detach(ebpf EbpfHandler, pid libpf.PID) error { + var errs []error + for _, instance := range m.instances { + if err := instance.Detach(ebpf, pid); err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) +} + +// SynchronizeMappings synchronizes mappings for all interpreter instances. +func (m *MultiInstance) SynchronizeMappings(ebpf EbpfHandler, + symbolReporter reporter.SymbolReporter, pr process.Process, mappings []process.Mapping) error { + var errs []error + for _, instance := range m.instances { + if err := instance.SynchronizeMappings(ebpf, symbolReporter, pr, mappings); err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) +} + +// UpdateTSDInfo updates TSD info for all interpreter instances. +func (m *MultiInstance) UpdateTSDInfo(ebpf EbpfHandler, pid libpf.PID, info tpbase.TSDInfo) error { + var errs []error + for _, instance := range m.instances { + if err := instance.UpdateTSDInfo(ebpf, pid, info); err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) +} + +// Symbolize tries to symbolize the frame with each interpreter instance until one succeeds. +func (m *MultiInstance) Symbolize(ebpfFrame *host.Frame, frames *libpf.Frames) error { + // Try each interpreter in order + for _, instance := range m.instances { + err := instance.Symbolize(ebpfFrame, frames) + if err != ErrMismatchInterpreterType { + return err + } + } + return ErrMismatchInterpreterType +} + +// GetAndResetMetrics collects metrics from all interpreter instances. +func (m *MultiInstance) GetAndResetMetrics() ([]metrics.Metric, error) { + var allMetrics []metrics.Metric + var errs []error + + for _, instance := range m.instances { + metrics, err := instance.GetAndResetMetrics() + if err != nil { + errs = append(errs, err) + continue + } + allMetrics = append(allMetrics, metrics...) + } + + return allMetrics, errors.Join(errs...) +} diff --git a/processmanager/execinfomanager/manager.go b/processmanager/execinfomanager/manager.go index ac03157c0..8a04b6ea0 100644 --- a/processmanager/execinfomanager/manager.go +++ b/processmanager/execinfomanager/manager.go @@ -347,9 +347,11 @@ type executableInfoManagerState struct { // detectAndLoadInterpData attempts to detect the given executable as an interpreter. If detection // succeeds, it then loads additional per-interpreter data into the BPF maps and returns the -// interpreter data. +// interpreter data. If multiple loaders recognize the executable, it returns a MultiData instance. func (state *executableInfoManagerState) detectAndLoadInterpData( loaderInfo *interpreter.LoaderInfo) interpreter.Data { + var interpreterDatas []interpreter.Data //nolint:prealloc + // Ask all interpreter loaders whether they want to handle this executable. for _, loader := range state.interpreterLoaders { data, err := loader(state.ebpf, loaderInfo) @@ -362,7 +364,8 @@ func (state *executableInfoManagerState) detectAndLoadInterpData( log.Errorf("Failed to load %v (%#016x): %v", loaderInfo.FileName(), loaderInfo.FileID(), err) } - return nil + // Continue checking other loaders even if one fails + continue } if data == nil { continue @@ -370,10 +373,21 @@ func (state *executableInfoManagerState) detectAndLoadInterpData( log.Debugf("Interpreter data %v for %v (%#016x)", data, loaderInfo.FileName(), loaderInfo.FileID()) - return data + interpreterDatas = append(interpreterDatas, data) } - return nil + // Return based on how many interpreters matched + switch len(interpreterDatas) { + case 0: + return nil + case 1: + return interpreterDatas[0] + default: + // Multiple interpreters matched, create a MultiData + log.Debugf("Multiple interpreters (%d) matched for %v (%#016x)", + len(interpreterDatas), loaderInfo.FileName(), loaderInfo.FileID()) + return interpreter.NewMultiData(interpreterDatas) + } } // detectAndLoadObservers attempts to detect observers for the given executable. diff --git a/processmanager/manager.go b/processmanager/manager.go index 980dec91a..374026a26 100644 --- a/processmanager/manager.go +++ b/processmanager/manager.go @@ -121,20 +121,18 @@ func metricSummaryToSlice(summary metrics.Summary) []metrics.Metric { return result } -// updateMetricSummary gets the metrics from the provided interpreter instance and updaates the +// updateMetricSummary gets the metrics from the provided interpreter instance and updates the // provided summary by aggregating the new metrics into the summary. // The caller is responsible to hold the lock on the interpreter.Instance to avoid race conditions. func updateMetricSummary(ii interpreter.Instance, summary metrics.Summary) error { instanceMetrics, err := ii.GetAndResetMetrics() - if err != nil { - return err - } - + // Update metrics even if there was an error, because it's possible ii is a MultiInstance + // and some of the instances may have returned metrics. for _, metric := range instanceMetrics { summary[metric.ID] += metric.Value } - return nil + return err } // collectInterpreterMetrics starts a goroutine that periodically fetches and reports interpreter @@ -148,8 +146,8 @@ func collectInterpreterMetrics(ctx context.Context, pm *ProcessManager, summary := make(map[metrics.MetricID]metrics.MetricValue) for pid := range pm.interpreters { - for addr := range pm.interpreters[pid] { - if err := updateMetricSummary(pm.interpreters[pid][addr], summary); err != nil { + for addr, ii := range pm.interpreters[pid] { + if err := updateMetricSummary(ii, summary); err != nil { log.Errorf("Failed to get/reset metrics for PID %d at 0x%x: %v", pid, addr, err) } From 2250ed03a4a71bb81ea075fc73e0f69c90c2aa9b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:01:54 +0300 Subject: [PATCH 24/24] chore(deps): update rust dependencies (#712) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b18ba6011..719086a59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -587,9 +587,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom", @@ -600,18 +600,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote",