From 42bf4f5226f3252b1410ce7d8336c95672db8ff0 Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Tue, 10 Dec 2024 10:56:58 +0100 Subject: [PATCH] Extract reporter data generation, and use pdata (#245) Co-authored-by: Florian Lehner Co-authored-by: Christos Kalkanis --- LICENSES/github.com/gogo/protobuf/LICENSE | 35 ++ .../grpc-ecosystem/grpc-gateway/v2/LICENSE | 27 - LICENSES/github.com/json-iterator/go/LICENSE | 21 + .../modern-go/concurrent}/LICENSE | 0 .../github.com/modern-go/reflect2/LICENSE | 201 +++++++ .../collector/pdata}/LICENSE | 0 .../collector/pdata/pprofile/LICENSE | 202 +++++++ .../go.opentelemetry.io/otel/trace/LICENSE | 201 +++++++ LICENSES/go.uber.org/multierr/LICENSE.txt | 19 + LICENSES/golang.org/x/net/LICENSE | 4 +- LICENSES/golang.org/x/text/LICENSE | 4 +- go.mod | 23 +- go.sum | 80 ++- reporter/attrmgr.go | 71 --- reporter/attrmgr_test.go | 172 ------ reporter/config.go | 22 +- reporter/iface.go | 10 +- reporter/internal/pdata/generate.go | 271 +++++++++ reporter/internal/pdata/generate_test.go | 162 +++++ reporter/internal/pdata/pdata.go | 63 ++ reporter/internal/samples/attrmgr.go | 92 +++ reporter/internal/samples/attrmgr_test.go | 124 ++++ reporter/internal/samples/samples.go | 72 +++ reporter/otlp_reporter.go | 560 +++--------------- 24 files changed, 1639 insertions(+), 797 deletions(-) create mode 100644 LICENSES/github.com/gogo/protobuf/LICENSE delete mode 100644 LICENSES/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE create mode 100644 LICENSES/github.com/json-iterator/go/LICENSE rename LICENSES/{go.opentelemetry.io/proto/otlp => github.com/modern-go/concurrent}/LICENSE (100%) create mode 100644 LICENSES/github.com/modern-go/reflect2/LICENSE rename LICENSES/{google.golang.org/genproto/googleapis/api/httpbody => go.opentelemetry.io/collector/pdata}/LICENSE (100%) create mode 100644 LICENSES/go.opentelemetry.io/collector/pdata/pprofile/LICENSE create mode 100644 LICENSES/go.opentelemetry.io/otel/trace/LICENSE create mode 100644 LICENSES/go.uber.org/multierr/LICENSE.txt delete mode 100644 reporter/attrmgr.go delete mode 100644 reporter/attrmgr_test.go create mode 100644 reporter/internal/pdata/generate.go create mode 100644 reporter/internal/pdata/generate_test.go create mode 100644 reporter/internal/pdata/pdata.go create mode 100644 reporter/internal/samples/attrmgr.go create mode 100644 reporter/internal/samples/attrmgr_test.go create mode 100644 reporter/internal/samples/samples.go diff --git a/LICENSES/github.com/gogo/protobuf/LICENSE b/LICENSES/github.com/gogo/protobuf/LICENSE new file mode 100644 index 00000000..f57de90d --- /dev/null +++ b/LICENSES/github.com/gogo/protobuf/LICENSE @@ -0,0 +1,35 @@ +Copyright (c) 2013, The GoGo Authors. All rights reserved. + +Protocol Buffers for Go with Gadgets + +Go support for Protocol Buffers - Google's data interchange format + +Copyright 2010 The Go Authors. All rights reserved. +https://github.com/golang/protobuf + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/LICENSES/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE b/LICENSES/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE deleted file mode 100644 index 36451625..00000000 --- a/LICENSES/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2015, Gengo, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of Gengo, Inc. nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/github.com/json-iterator/go/LICENSE b/LICENSES/github.com/json-iterator/go/LICENSE new file mode 100644 index 00000000..2cf4f5ab --- /dev/null +++ b/LICENSES/github.com/json-iterator/go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 json-iterator + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSES/go.opentelemetry.io/proto/otlp/LICENSE b/LICENSES/github.com/modern-go/concurrent/LICENSE similarity index 100% rename from LICENSES/go.opentelemetry.io/proto/otlp/LICENSE rename to LICENSES/github.com/modern-go/concurrent/LICENSE diff --git a/LICENSES/github.com/modern-go/reflect2/LICENSE b/LICENSES/github.com/modern-go/reflect2/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSES/github.com/modern-go/reflect2/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSES/google.golang.org/genproto/googleapis/api/httpbody/LICENSE b/LICENSES/go.opentelemetry.io/collector/pdata/LICENSE similarity index 100% rename from LICENSES/google.golang.org/genproto/googleapis/api/httpbody/LICENSE rename to LICENSES/go.opentelemetry.io/collector/pdata/LICENSE diff --git a/LICENSES/go.opentelemetry.io/collector/pdata/pprofile/LICENSE b/LICENSES/go.opentelemetry.io/collector/pdata/pprofile/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSES/go.opentelemetry.io/collector/pdata/pprofile/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSES/go.opentelemetry.io/otel/trace/LICENSE b/LICENSES/go.opentelemetry.io/otel/trace/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSES/go.opentelemetry.io/otel/trace/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSES/go.uber.org/multierr/LICENSE.txt b/LICENSES/go.uber.org/multierr/LICENSE.txt new file mode 100644 index 00000000..413e30f7 --- /dev/null +++ b/LICENSES/go.uber.org/multierr/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2017-2021 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/LICENSES/golang.org/x/net/LICENSE b/LICENSES/golang.org/x/net/LICENSE index 6a66aea5..2a7cf70d 100644 --- a/LICENSES/golang.org/x/net/LICENSE +++ b/LICENSES/golang.org/x/net/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/LICENSES/golang.org/x/text/LICENSE b/LICENSES/golang.org/x/text/LICENSE index 6a66aea5..2a7cf70d 100644 --- a/LICENSES/golang.org/x/text/LICENSE +++ b/LICENSES/golang.org/x/text/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/go.mod b/go.mod index 145446b0..5302be5b 100644 --- a/go.mod +++ b/go.mod @@ -16,16 +16,17 @@ require ( github.com/minio/sha256-simd v1.0.1 github.com/peterbourgon/ff/v3 v3.4.0 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/tklauser/numcpus v0.8.0 github.com/zeebo/xxh3 v1.0.2 + go.opentelemetry.io/collector/pdata v1.20.1-0.20241128084017-33264a5408cd + go.opentelemetry.io/collector/pdata/pprofile v0.114.1-0.20241128084017-33264a5408cd go.opentelemetry.io/otel v1.30.0 - go.opentelemetry.io/proto/otlp v1.3.1 golang.org/x/arch v0.10.0 golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/sync v0.8.0 golang.org/x/sys v0.26.0 - google.golang.org/grpc v1.66.2 + google.golang.org/grpc v1.67.1 ) require ( @@ -45,17 +46,21 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.30.8 // indirect github.com/aws/smithy-go v1.20.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/josharian/native v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/protobuf v1.34.1 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/text v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7fe300bb..777e7bec 100644 --- a/go.sum +++ b/go.sum @@ -47,18 +47,23 @@ github.com/elastic/go-perf v0.0.0-20241016160959-1342461adb4a h1:ymmtaN4bVCmKKeu github.com/elastic/go-perf v0.0.0-20241016160959-1342461adb4a/go.mod h1:Nt+pnRYvf0POC+7pXsrv8ubsEOSsaipJP0zlz1Ms1RM= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v1.4.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5GTEn90= github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM= github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= @@ -73,6 +78,11 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -82,41 +92,75 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.opentelemetry.io/collector/pdata v1.20.1-0.20241128084017-33264a5408cd h1:oDUPuAoT1ZFPtJy5RuluKxv2zHz9GWpwXKxXoSpESpU= +go.opentelemetry.io/collector/pdata v1.20.1-0.20241128084017-33264a5408cd/go.mod h1:GKb1/zocKJMvxKbS+sl0W85lxhYBTFJ6h6I1tphVyDU= +go.opentelemetry.io/collector/pdata/pprofile v0.114.1-0.20241128084017-33264a5408cd h1:eRHWQlyf0m9ZkbfklXBkqond3u/ymz2IiFAZIWJrArM= +go.opentelemetry.io/collector/pdata/pprofile v0.114.1-0.20241128084017-33264a5408cd/go.mod h1:1rWPNiz6qNS+wWb1u7RV/at2cKC/kUqXF6fs04ZMCoM= go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +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/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +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/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= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= -google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/reporter/attrmgr.go b/reporter/attrmgr.go deleted file mode 100644 index e675cf67..00000000 --- a/reporter/attrmgr.go +++ /dev/null @@ -1,71 +0,0 @@ -package reporter // import "go.opentelemetry.io/ebpf-profiler/reporter" - -import ( - "fmt" - - "go.opentelemetry.io/otel/attribute" - common "go.opentelemetry.io/proto/otlp/common/v1" -) - -// AttrIndex is an index in the `Profile.attribute_table`. -type AttrIndex = uint64 - -// AttrTableManager maintains index allocation and deduplication for attribute tables. -type AttrTableManager struct { - // indices maps compound keys to the indices in the attribute table. - indices map[string]AttrIndex - - // attrTable being populated. - attrTable *[]*common.KeyValue -} - -func NewAttrTableManager(attrTable *[]*common.KeyValue) *AttrTableManager { - return &AttrTableManager{ - indices: make(map[string]AttrIndex), - attrTable: attrTable, - } -} - -// AppendInt adds the index for the given integer attribute to an attribute index slice. -func (m *AttrTableManager) AppendInt( - attrs *[]AttrIndex, key attribute.Key, value int64) { - compound := fmt.Sprintf("%v_%d", key, value) - val := common.AnyValue{Value: &common.AnyValue_IntValue{IntValue: value}} - m.appendAny(attrs, key, compound, &val) -} - -// AppendOptionalString adds the index for the given string attribute to an -// attribute index slice if it is non-empty. -func (m *AttrTableManager) AppendOptionalString( - attrs *[]AttrIndex, key attribute.Key, value string) { - if value == "" { - return - } - - compound := fmt.Sprintf("%v_%s", key, value) - val := common.AnyValue{Value: &common.AnyValue_StringValue{StringValue: value}} - m.appendAny(attrs, key, compound, &val) -} - -func (m *AttrTableManager) appendAny( - attrs *[]AttrIndex, - key attribute.Key, - compoundKey string, - value *common.AnyValue, -) { - if attributeIndex, exists := m.indices[compoundKey]; exists { - *attrs = append(*attrs, attributeIndex) - return - } - - newIndex := AttrIndex(len(*m.attrTable)) - - *m.attrTable = append(*m.attrTable, &common.KeyValue{ - Key: string(key), - Value: value, - }) - - m.indices[compoundKey] = newIndex - - *attrs = append(*attrs, newIndex) -} diff --git a/reporter/attrmgr_test.go b/reporter/attrmgr_test.go deleted file mode 100644 index 652b96a5..00000000 --- a/reporter/attrmgr_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package reporter - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/ebpf-profiler/libpf" - semconv "go.opentelemetry.io/otel/semconv/v1.25.0" - common "go.opentelemetry.io/proto/otlp/common/v1" -) - -func TestAttrTableManager(t *testing.T) { - tests := map[string]struct { - k []traceAndMetaKey - expectedIndices [][]uint64 - expectedAttributeTable []*common.KeyValue - }{ - "empty": { - k: []traceAndMetaKey{ - { - hash: libpf.TraceHash{}, - comm: "", - apmServiceName: "", - containerID: "", - pid: 0, - }, - }, - expectedIndices: [][]uint64{{0}}, - expectedAttributeTable: []*common.KeyValue{ - { - Key: "process.pid", - Value: &common.AnyValue{ - Value: &common.AnyValue_IntValue{IntValue: 0}, - }, - }, - }, - }, - "duplicate": { - k: []traceAndMetaKey{ - { - hash: libpf.TraceHash{}, - comm: "comm1", - apmServiceName: "apmServiceName1", - containerID: "containerID1", - pid: 1234, - }, - { - hash: libpf.TraceHash{}, - comm: "comm1", - apmServiceName: "apmServiceName1", - containerID: "containerID1", - pid: 1234, - }, - }, - expectedIndices: [][]uint64{{0, 1, 2, 3}, {0, 1, 2, 3}}, - expectedAttributeTable: []*common.KeyValue{ - { - Key: "container.id", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "containerID1"}, - }, - }, - { - Key: "thread.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "comm1"}, - }, - }, - { - Key: "service.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "apmServiceName1"}, - }, - }, - { - Key: "process.pid", - Value: &common.AnyValue{ - Value: &common.AnyValue_IntValue{IntValue: 1234}, - }, - }, - }, - }, - "different": { - k: []traceAndMetaKey{ - { - hash: libpf.TraceHash{}, - comm: "comm1", - apmServiceName: "apmServiceName1", - containerID: "containerID1", - pid: 1234, - }, - { - hash: libpf.TraceHash{}, - comm: "comm2", - apmServiceName: "apmServiceName2", - containerID: "containerID2", - pid: 6789, - }, - }, - expectedIndices: [][]uint64{{0, 1, 2, 3}, {4, 5, 6, 7}}, - expectedAttributeTable: []*common.KeyValue{ - { - Key: "container.id", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "containerID1"}, - }, - }, - { - Key: "thread.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "comm1"}, - }, - }, - { - Key: "service.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "apmServiceName1"}, - }, - }, - { - Key: "process.pid", - Value: &common.AnyValue{ - Value: &common.AnyValue_IntValue{IntValue: 1234}, - }, - }, - { - Key: "container.id", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "containerID2"}, - }, - }, - { - Key: "thread.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "comm2"}, - }, - }, - { - Key: "service.name", - Value: &common.AnyValue{ - Value: &common.AnyValue_StringValue{StringValue: "apmServiceName2"}, - }, - }, - { - Key: "process.pid", - Value: &common.AnyValue{ - Value: &common.AnyValue_IntValue{IntValue: 6789}, - }, - }, - }, - }, - } - - for name, tc := range tests { - name := name - t.Run(name, func(t *testing.T) { - attrTable := []*common.KeyValue{} - mgr := NewAttrTableManager(&attrTable) - indices := make([][]AttrIndex, 0) - for _, k := range tc.k { - var inner []AttrIndex - mgr.AppendOptionalString(&inner, semconv.ContainerIDKey, k.containerID) - mgr.AppendOptionalString(&inner, semconv.ThreadNameKey, k.comm) - mgr.AppendOptionalString(&inner, semconv.ServiceNameKey, k.apmServiceName) - mgr.AppendInt(&inner, semconv.ProcessPIDKey, k.pid) - indices = append(indices, inner) - } - require.Equal(t, tc.expectedIndices, indices) - require.Equal(t, tc.expectedAttributeTable, attrTable) - }) - } -} diff --git a/reporter/config.go b/reporter/config.go index 7e0a5bd1..87e37041 100644 --- a/reporter/config.go +++ b/reporter/config.go @@ -6,8 +6,9 @@ package reporter // import "go.opentelemetry.io/ebpf-profiler/reporter" import ( "time" - "go.opentelemetry.io/ebpf-profiler/libpf" "google.golang.org/grpc" + + "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" ) type Config struct { @@ -55,22 +56,5 @@ type Config struct { // ExtraSampleAttrProd is an optional hook point for adding custom // attributes to samples. - ExtraSampleAttrProd SampleAttrProducer -} - -// SampleAttrProducer provides a hook point to: -// -// - inspect each trace and its meta when it is enqueued in the reporter -// - produce extra meta info -// - attach extra attributes to the trace -type SampleAttrProducer interface { - // CollectExtraSampleMeta gathers extra sample meta-info and returns it as - // a pointer to a **hashable** struct. - CollectExtraSampleMeta(trace *libpf.Trace, meta *TraceEventMeta) any - - // ExtraSampleAttrs is called when the reporter populates the Sample struct - // before sending it out. Attributes returned from this function are added - // as Sample attributes. `meta` receives the pointer that was returned from - // CollectExtraSampleMeta. - ExtraSampleAttrs(attrMgr *AttrTableManager, meta any) []AttrIndex + ExtraSampleAttrProd samples.SampleAttrProducer } diff --git a/reporter/iface.go b/reporter/iface.go index 94a3a585..87760cdb 100644 --- a/reporter/iface.go +++ b/reporter/iface.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/process" + "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" ) // Reporter is the top-level interface implemented by a full reporter. @@ -31,14 +32,7 @@ type Reporter interface { GetMetrics() Metrics } -type TraceEventMeta struct { - Timestamp libpf.UnixTime64 - Comm string - Executable string - APMServiceName string - PID, TID libpf.PID - CPU int -} +type TraceEventMeta = samples.TraceEventMeta type TraceReporter interface { // ReportFramesForTrace accepts a trace with the corresponding frames diff --git a/reporter/internal/pdata/generate.go b/reporter/internal/pdata/generate.go new file mode 100644 index 00000000..3352f26c --- /dev/null +++ b/reporter/internal/pdata/generate.go @@ -0,0 +1,271 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pdata // import "go.opentelemetry.io/ebpf-profiler/reporter/internal/pdata" + +import ( + "crypto/rand" + "slices" + "time" + + log "github.com/sirupsen/logrus" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" +) + +const ( + ExecutableCacheLifetime = 1 * time.Hour + FramesCacheLifetime = 1 * time.Hour +) + +// Generate generates a pdata request out of internal profiles data, to be +// exported. +func (p Pdata) Generate(events map[samples.TraceAndMetaKey]*samples.TraceEvents) pprofile.Profiles { + profiles := pprofile.NewProfiles() + rp := profiles.ResourceProfiles().AppendEmpty() + sp := rp.ScopeProfiles().AppendEmpty() + prof := sp.Profiles().AppendEmpty() + prof.SetProfileID(pprofile.ProfileID(mkProfileID())) + p.setProfile(events, prof) + + return profiles +} + +// mkProfileID creates a random profile ID. +func mkProfileID() []byte { + profileID := make([]byte, 16) + _, err := rand.Read(profileID) + if err != nil { + return []byte("opentelemetry-ebpf-profiler") + } + return profileID +} + +// setProfile sets the data an OTLP profile with all collected samples up to +// this moment. +func (p *Pdata) setProfile( + events map[samples.TraceAndMetaKey]*samples.TraceEvents, + profile pprofile.Profile, +) { + // stringMap is a temporary helper that will build the StringTable. + // By specification, the first element should be empty. + stringMap := make(map[string]int32) + stringMap[""] = 0 + + // funcMap is a temporary helper that will build the Function array + // in profile and make sure information is deduplicated. + funcMap := make(map[samples.FuncInfo]int32) + funcMap[samples.FuncInfo{Name: "", FileName: ""}] = 0 + + st := profile.SampleType().AppendEmpty() + st.SetTypeStrindex(getStringMapIndex(stringMap, "samples")) + st.SetUnitStrindex(getStringMapIndex(stringMap, "count")) + + pt := profile.PeriodType() + pt.SetTypeStrindex(getStringMapIndex(stringMap, "cpu")) + pt.SetUnitStrindex(getStringMapIndex(stringMap, "nanoseconds")) + profile.SetPeriod(1e9 / int64(p.samplesPerSecond)) + + // Temporary lookup to reference existing Mappings. + fileIDtoMapping := make(map[libpf.FileID]int32) + + attrMgr := samples.NewAttrTableManager(profile.AttributeTable()) + var locationIndex int32 + var startTS, endTS pcommon.Timestamp + for traceKey, traceInfo := range events { + sample := profile.Sample().AppendEmpty() + sample.SetLocationsStartIndex(locationIndex) + + slices.Sort(traceInfo.Timestamps) + startTS = pcommon.Timestamp(traceInfo.Timestamps[0]) + endTS = pcommon.Timestamp(traceInfo.Timestamps[len(traceInfo.Timestamps)-1]) + + sample.TimestampsUnixNano().FromRaw(traceInfo.Timestamps) + sample.Value().Append(1) + + // Walk every frame of the trace. + for i := range traceInfo.FrameTypes { + loc := profile.LocationTable().AppendEmpty() + loc.SetAddress(uint64(traceInfo.Linenos[i])) + attrMgr.AppendOptionalString(loc.AttributeIndices(), + "profile.frame.type", traceInfo.FrameTypes[i].String()) + + switch frameKind := traceInfo.FrameTypes[i]; frameKind { + case libpf.NativeFrame: + // As native frames are resolved in the backend, we use Mapping to + // report these frames. + + var locationMappingIndex int32 + if tmpMappingIndex, exists := fileIDtoMapping[traceInfo.Files[i]]; exists { + locationMappingIndex = tmpMappingIndex + } else { + idx := int32(len(fileIDtoMapping)) + fileIDtoMapping[traceInfo.Files[i]] = idx + locationMappingIndex = idx + + ei, exists := p.Executables.GetAndRefresh(traceInfo.Files[i], + ExecutableCacheLifetime) + + // Next step: Select a proper default value, + // if the name of the executable is not known yet. + var fileName = "UNKNOWN" + if exists { + fileName = ei.FileName + } + + mapping := profile.MappingTable().AppendEmpty() + mapping.SetMemoryStart(uint64(traceInfo.MappingStarts[i])) + mapping.SetMemoryLimit(uint64(traceInfo.MappingEnds[i])) + mapping.SetFileOffset(traceInfo.MappingFileOffsets[i]) + mapping.SetFilenameStrindex(getStringMapIndex(stringMap, fileName)) + + // Once SemConv and its Go package is released with the new + // semantic convention for build_id, replace these hard coded + // strings. + attrMgr.AppendOptionalString(mapping.AttributeIndices(), + "process.executable.build_id.gnu", ei.GnuBuildID) + attrMgr.AppendOptionalString(mapping.AttributeIndices(), + "process.executable.build_id.htlhash", traceInfo.Files[i].StringNoQuotes()) + } + loc.SetMappingIndex(locationMappingIndex) + case libpf.AbortFrame: + // Next step: Figure out how the OTLP protocol + // could handle artificial frames, like AbortFrame, + // that are not originated from a native or interpreted + // program. + default: + // Store interpreted frame information as a Line message: + line := loc.Line().AppendEmpty() + + fileIDInfoLock, exists := p.Frames.GetAndRefresh(traceInfo.Files[i], + FramesCacheLifetime) + if !exists { + // At this point, we do not have enough information for the frame. + // Therefore, we report a dummy entry and use the interpreter as filename. + line.SetFunctionIndex(createFunctionEntry(funcMap, + "UNREPORTED", frameKind.String())) + } else { + fileIDInfo := fileIDInfoLock.RLock() + if si, exists := (*fileIDInfo)[traceInfo.Linenos[i]]; exists { + line.SetLine(int64(si.LineNumber)) + + line.SetFunctionIndex(createFunctionEntry(funcMap, + si.FunctionName, si.FilePath)) + } else { + // At this point, we do not have enough information for the frame. + // Therefore, we report a dummy entry and use the interpreter as filename. + // To differentiate this case from the case where no information about + // the file ID is available at all, we use a different name for reported + // function. + line.SetFunctionIndex(createFunctionEntry(funcMap, + "UNRESOLVED", frameKind.String())) + } + fileIDInfoLock.RUnlock(&fileIDInfo) + } + + // To be compliant with the protocol, generate a dummy mapping entry. + loc.SetMappingIndex(getDummyMappingIndex(fileIDtoMapping, stringMap, + attrMgr, profile, traceInfo.Files[i])) + } + } + + attrMgr.AppendOptionalString(sample.AttributeIndices(), + semconv.ContainerIDKey, traceKey.ContainerID) + attrMgr.AppendOptionalString(sample.AttributeIndices(), + semconv.ThreadNameKey, traceKey.Comm) + attrMgr.AppendOptionalString(sample.AttributeIndices(), + semconv.ProcessExecutablePathKey, traceKey.Executable) + attrMgr.AppendOptionalString(sample.AttributeIndices(), + semconv.ServiceNameKey, traceKey.ApmServiceName) + attrMgr.AppendInt(sample.AttributeIndices(), + semconv.ProcessPIDKey, traceKey.Pid) + + if p.ExtraSampleAttrProd != nil { + extra := p.ExtraSampleAttrProd.ExtraSampleAttrs(attrMgr, traceKey.ExtraMeta) + sample.AttributeIndices().Append(extra...) + } + + sample.SetLocationsLength(int32(len(traceInfo.FrameTypes))) + locationIndex += sample.LocationsLength() + } + log.Debugf("Reporting OTLP profile with %d samples", profile.Sample().Len()) + + // Populate the deduplicated functions into profile. + for v := range funcMap { + f := profile.FunctionTable().AppendEmpty() + f.SetNameStrindex(getStringMapIndex(stringMap, v.Name)) + f.SetFilenameStrindex(getStringMapIndex(stringMap, v.FileName)) + } + + // When ranging over stringMap, the order will be according to the + // hash value of the key. To get the correct order for profile.StringTable, + // put the values in stringMap, in the correct array order. + stringTable := make([]string, len(stringMap)) + for v, idx := range stringMap { + stringTable[idx] = v + } + + for _, v := range stringTable { + profile.StringTable().Append(v) + } + + // profile.LocationIndices is not optional, and we only write elements into + // profile.Location that at least one sample references. + for i := int32(0); i < int32(profile.LocationTable().Len()); i++ { + profile.LocationIndices().Append(i) + } + + profile.SetDuration(endTS - startTS) + profile.SetStartTime(startTS) +} + +// getStringMapIndex inserts or looks up the index for value in stringMap. +func getStringMapIndex(stringMap map[string]int32, value string) int32 { + if idx, exists := stringMap[value]; exists { + return idx + } + + idx := int32(len(stringMap)) + stringMap[value] = idx + + return idx +} + +// createFunctionEntry adds a new function and returns its reference index. +func createFunctionEntry(funcMap map[samples.FuncInfo]int32, + name string, fileName string) int32 { + key := samples.FuncInfo{ + Name: name, + FileName: fileName, + } + if idx, exists := funcMap[key]; exists { + return idx + } + + idx := int32(len(funcMap)) + funcMap[key] = idx + + return idx +} + +// getDummyMappingIndex inserts or looks up an entry for interpreted FileIDs. +func getDummyMappingIndex(fileIDtoMapping map[libpf.FileID]int32, + stringMap map[string]int32, attrMgr *samples.AttrTableManager, profile pprofile.Profile, + fileID libpf.FileID) int32 { + if mappingIndex, exists := fileIDtoMapping[fileID]; exists { + return mappingIndex + } + + locationMappingIndex := int32(len(fileIDtoMapping)) + fileIDtoMapping[fileID] = locationMappingIndex + + mapping := profile.MappingTable().AppendEmpty() + mapping.SetFilenameStrindex(getStringMapIndex(stringMap, "")) + attrMgr.AppendOptionalString(mapping.AttributeIndices(), + "process.executable.build_id.htlhash", fileID.StringNoQuotes()) + return locationMappingIndex +} diff --git a/reporter/internal/pdata/generate_test.go b/reporter/internal/pdata/generate_test.go new file mode 100644 index 00000000..e370bd6b --- /dev/null +++ b/reporter/internal/pdata/generate_test.go @@ -0,0 +1,162 @@ +package pdata + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pprofile" + + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" +) + +func TestGetStringMapIndex(t *testing.T) { + for _, tt := range []struct { + name string + stringMap map[string]int32 + value string + + wantStringMap map[string]int32 + wantIndex int32 + }{ + { + name: "with a value not yet in the string map", + stringMap: map[string]int32{}, + value: "test", + + wantIndex: 0, + wantStringMap: map[string]int32{"test": 0}, + }, + { + name: "with a value already in the string map", + stringMap: map[string]int32{"test": 42}, + value: "test", + + wantIndex: 42, + wantStringMap: map[string]int32{"test": 42}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + stringMap := tt.stringMap + + i := getStringMapIndex(stringMap, tt.value) + assert.Equal(t, tt.wantIndex, i) + assert.Equal(t, tt.wantStringMap, stringMap) + }) + } +} + +func TestCreateFunctionEntry(t *testing.T) { + for _, tt := range []struct { + name string + funcMap map[samples.FuncInfo]int32 + funcName string + fileName string + + wantIndex int32 + wantFuncMap map[samples.FuncInfo]int32 + }{ + { + name: "with ane entry not yet in the func map", + funcMap: map[samples.FuncInfo]int32{}, + funcName: "my_method", + fileName: "/tmp", + + wantIndex: 0, + wantFuncMap: map[samples.FuncInfo]int32{ + {Name: "my_method", FileName: "/tmp"}: 0, + }, + }, + { + name: "with ane entry already in the func map", + funcMap: map[samples.FuncInfo]int32{ + {Name: "my_method", FileName: "/tmp"}: 42, + }, + funcName: "my_method", + fileName: "/tmp", + + wantIndex: 42, + wantFuncMap: map[samples.FuncInfo]int32{ + {Name: "my_method", FileName: "/tmp"}: 42, + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + funcMap := tt.funcMap + + i := createFunctionEntry(funcMap, tt.funcName, tt.fileName) + assert.Equal(t, tt.wantIndex, i) + assert.Equal(t, tt.wantFuncMap, funcMap) + }) + } +} + +func TestGetDummyMappingIndex(t *testing.T) { + for _, tt := range []struct { + name string + fileIDToMapping map[libpf.FileID]int32 + stringMap map[string]int32 + fileID libpf.FileID + + wantIndex int32 + wantFileIDToMapping map[libpf.FileID]int32 + wantMappingTable []int32 + wantStringMap map[string]int32 + }{ + { + name: "with an index already in the file id mapping", + fileIDToMapping: map[libpf.FileID]int32{ + libpf.UnsymbolizedFileID: 42, + }, + fileID: libpf.UnsymbolizedFileID, + + wantIndex: 42, + }, + { + name: "with an index not yet in the file id mapping", + fileIDToMapping: map[libpf.FileID]int32{}, + stringMap: map[string]int32{}, + fileID: libpf.UnsymbolizedFileID, + + wantIndex: 0, + wantFileIDToMapping: map[libpf.FileID]int32{ + libpf.UnsymbolizedFileID: 0, + }, + wantMappingTable: []int32{0}, + wantStringMap: map[string]int32{"": 0}, + }, + { + name: "with an index not yet in the file id mapping and a filename in the string table", + + fileIDToMapping: map[libpf.FileID]int32{}, + stringMap: map[string]int32{"": 42}, + fileID: libpf.UnsymbolizedFileID, + + wantIndex: 0, + wantFileIDToMapping: map[libpf.FileID]int32{ + libpf.UnsymbolizedFileID: 0, + }, + wantMappingTable: []int32{42}, + wantStringMap: map[string]int32{"": 42}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + fitm := tt.fileIDToMapping + stringMap := tt.stringMap + profile := pprofile.NewProfile() + mgr := samples.NewAttrTableManager(profile.AttributeTable()) + + i := getDummyMappingIndex(fitm, stringMap, mgr, profile, tt.fileID) + assert.Equal(t, tt.wantIndex, i) + assert.Equal(t, tt.fileIDToMapping, fitm) + assert.Equal(t, tt.wantStringMap, stringMap) + + require.Equal(t, len(tt.wantMappingTable), profile.MappingTable().Len()) + for i, v := range tt.wantMappingTable { + mapp := profile.MappingTable().At(i) + assert.Equal(t, v, mapp.FilenameStrindex()) + } + }) + } +} diff --git a/reporter/internal/pdata/pdata.go b/reporter/internal/pdata/pdata.go new file mode 100644 index 00000000..bab02de6 --- /dev/null +++ b/reporter/internal/pdata/pdata.go @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pdata // import "go.opentelemetry.io/ebpf-profiler/reporter/internal/pdata" + +import ( + lru "github.com/elastic/go-freelru" + + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/libpf/xsync" + "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" +) + +// Pdata holds the cache for the data used to generate the events reporters +// will export when handling OTLP data. +type Pdata struct { + // samplesPerSecond is the number of samples per second. + samplesPerSecond int + + // Executables stores metadata for executables. + Executables *lru.SyncedLRU[libpf.FileID, samples.ExecInfo] + + // Frames maps frame information to its source location. + Frames *lru.SyncedLRU[ + libpf.FileID, + *xsync.RWMutex[map[libpf.AddressOrLineno]samples.SourceInfo], + ] + + // ExtraSampleAttrProd is an optional hook point for adding custom + // attributes to samples. + ExtraSampleAttrProd samples.SampleAttrProducer +} + +func New(samplesPerSecond int, executablesCacheElements, framesCacheElements uint32, + extra samples.SampleAttrProducer) (*Pdata, error) { + executables, err := + lru.NewSynced[libpf.FileID, samples.ExecInfo](executablesCacheElements, libpf.FileID.Hash32) + if err != nil { + return nil, err + } + executables.SetLifetime(ExecutableCacheLifetime) // Allow GC to clean stale items. + + frames, err := lru.NewSynced[libpf.FileID, + *xsync.RWMutex[map[libpf.AddressOrLineno]samples.SourceInfo]]( + framesCacheElements, libpf.FileID.Hash32) + if err != nil { + return nil, err + } + frames.SetLifetime(FramesCacheLifetime) // Allow GC to clean stale items. + + return &Pdata{ + samplesPerSecond: samplesPerSecond, + Executables: executables, + Frames: frames, + ExtraSampleAttrProd: extra, + }, nil +} + +// Purge purges all the expired data +func (p Pdata) Purge() { + p.Executables.PurgeExpired() + p.Frames.PurgeExpired() +} diff --git a/reporter/internal/samples/attrmgr.go b/reporter/internal/samples/attrmgr.go new file mode 100644 index 00000000..98ff9a8e --- /dev/null +++ b/reporter/internal/samples/attrmgr.go @@ -0,0 +1,92 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package samples // import "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" + +import ( + "fmt" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + "go.opentelemetry.io/otel/attribute" + + "go.opentelemetry.io/ebpf-profiler/libpf" +) + +// SampleAttrProducer provides a hook point to: +// +// - inspect each trace and its meta when it is enqueued in the reporter +// - produce extra meta info +// - attach extra attributes to the trace +type SampleAttrProducer interface { + // CollectExtraSampleMeta gathers extra sample meta-info and returns it as + // a pointer to a **hashable** struct. + CollectExtraSampleMeta(trace *libpf.Trace, meta *TraceEventMeta) any + + // ExtraSampleAttrs is called when the reporter populates the Sample struct + // before sending it out. Attributes returned from this function are added + // as Sample attributes. `meta` receives the pointer that was returned from + // CollectExtraSampleMeta. + ExtraSampleAttrs(attrMgr *AttrTableManager, meta any) []int32 +} + +// AttrTableManager maintains index allocation and deduplication for attribute tables. +type AttrTableManager struct { + // indices maps compound keys to the indices in the attribute table. + indices map[string]int32 + + // attrTable being populated. + attrTable pprofile.AttributeTableSlice +} + +func NewAttrTableManager(attrTable pprofile.AttributeTableSlice) *AttrTableManager { + return &AttrTableManager{ + indices: make(map[string]int32), + attrTable: attrTable, + } +} + +// AppendInt adds the index for the given integer attribute to an attribute index slice. +func (m *AttrTableManager) AppendInt( + attrs pcommon.Int32Slice, key attribute.Key, value int64) { + compound := fmt.Sprintf("%v_%d", key, value) + m.appendAny(attrs, key, compound, value) +} + +// AppendOptionalString adds the index for the given string attribute to an +// attribute index slice if it is non-empty. +func (m *AttrTableManager) AppendOptionalString( + attrs pcommon.Int32Slice, key attribute.Key, value string) { + if value == "" { + return + } + + compound := fmt.Sprintf("%v_%s", key, value) + m.appendAny(attrs, key, compound, value) +} + +func (m *AttrTableManager) appendAny( + attrs pcommon.Int32Slice, + key attribute.Key, + compoundKey string, + value any, +) { + if attributeIndex, exists := m.indices[compoundKey]; exists { + attrs.Append(attributeIndex) + return + } + + newIndex := int32(m.attrTable.Len()) + + a := m.attrTable.AppendEmpty() + a.SetKey(string(key)) + + switch v := value.(type) { + case int64: + a.Value().SetInt(v) + case string: + a.Value().SetStr(v) + } + m.indices[compoundKey] = newIndex + attrs.Append(newIndex) +} diff --git a/reporter/internal/samples/attrmgr_test.go b/reporter/internal/samples/attrmgr_test.go new file mode 100644 index 00000000..34e82ddc --- /dev/null +++ b/reporter/internal/samples/attrmgr_test.go @@ -0,0 +1,124 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package samples + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + "go.opentelemetry.io/ebpf-profiler/libpf" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +type attributeStruct struct { + Key string + Value any +} + +func TestAttrTableManager(t *testing.T) { + tests := map[string]struct { + k []TraceAndMetaKey + expectedIndices [][]int32 + expectedAttributeTable []attributeStruct + }{ + "empty": { + k: []TraceAndMetaKey{ + { + Hash: libpf.TraceHash{}, + Comm: "", + ApmServiceName: "", + ContainerID: "", + Pid: 0, + }, + }, + expectedIndices: [][]int32{{0}}, + expectedAttributeTable: []attributeStruct{ + {Key: "process.pid", Value: int64(0)}, + }, + }, + "duplicate": { + k: []TraceAndMetaKey{ + { + Hash: libpf.TraceHash{}, + Comm: "comm1", + ApmServiceName: "apmServiceName1", + ContainerID: "containerID1", + Pid: 1234, + }, + { + Hash: libpf.TraceHash{}, + Comm: "comm1", + ApmServiceName: "apmServiceName1", + ContainerID: "containerID1", + Pid: 1234, + }, + }, + expectedIndices: [][]int32{{0, 1, 2, 3}, {0, 1, 2, 3}}, + expectedAttributeTable: []attributeStruct{ + {Key: "container.id", Value: "containerID1"}, + {Key: "thread.name", Value: "comm1"}, + {Key: "service.name", Value: "apmServiceName1"}, + {Key: "process.pid", Value: int64(1234)}, + }, + }, + "different": { + k: []TraceAndMetaKey{ + { + Hash: libpf.TraceHash{}, + Comm: "comm1", + ApmServiceName: "apmServiceName1", + ContainerID: "containerID1", + Pid: 1234, + }, + { + Hash: libpf.TraceHash{}, + Comm: "comm2", + ApmServiceName: "apmServiceName2", + ContainerID: "containerID2", + Pid: 6789, + }, + }, + expectedIndices: [][]int32{{0, 1, 2, 3}, {4, 5, 6, 7}}, + expectedAttributeTable: []attributeStruct{ + {Key: "container.id", Value: "containerID1"}, + {Key: "thread.name", Value: "comm1"}, + {Key: "service.name", Value: "apmServiceName1"}, + {Key: "process.pid", Value: int64(1234)}, + {Key: "container.id", Value: "containerID2"}, + {Key: "thread.name", Value: "comm2"}, + {Key: "service.name", Value: "apmServiceName2"}, + {Key: "process.pid", Value: int64(6789)}, + }, + }, + } + + for name, tc := range tests { + name := name + t.Run(name, func(t *testing.T) { + attrTable := pprofile.NewAttributeTableSlice() + mgr := NewAttrTableManager(attrTable) + indices := make([][]int32, 0) + for _, k := range tc.k { + inner := pcommon.NewInt32Slice() + mgr.AppendOptionalString(inner, semconv.ContainerIDKey, k.ContainerID) + mgr.AppendOptionalString(inner, semconv.ThreadNameKey, k.Comm) + mgr.AppendOptionalString(inner, semconv.ServiceNameKey, k.ApmServiceName) + mgr.AppendInt(inner, semconv.ProcessPIDKey, k.Pid) + indices = append(indices, inner.AsRaw()) + } + + require.Equal(t, tc.expectedIndices, indices) + require.Equal(t, len(tc.expectedAttributeTable), attrTable.Len()) + for i, v := range tc.expectedAttributeTable { + attr := attrTable.At(i) + assert.Equal(t, v.Key, attr.Key()) + assert.Equal(t, v.Value, attr.Value().AsRaw()) + } + }) + } +} diff --git a/reporter/internal/samples/samples.go b/reporter/internal/samples/samples.go new file mode 100644 index 00000000..e938a6eb --- /dev/null +++ b/reporter/internal/samples/samples.go @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package samples // import "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" + +import "go.opentelemetry.io/ebpf-profiler/libpf" + +type TraceEventMeta struct { + Timestamp libpf.UnixTime64 + Comm string + Executable string + APMServiceName string + PID, TID libpf.PID + CPU int +} + +// TraceEvents holds known information about a trace. +type TraceEvents struct { + Files []libpf.FileID + Linenos []libpf.AddressOrLineno + FrameTypes []libpf.FrameType + MappingStarts []libpf.Address + MappingEnds []libpf.Address + MappingFileOffsets []uint64 + Timestamps []uint64 // in nanoseconds +} + +// TraceAndMetaKey is the deduplication key for samples. This **must always** +// contain all trace fields that aren't already part of the trace hash to ensure +// that we don't accidentally merge traces with different fields. +type TraceAndMetaKey struct { + Hash libpf.TraceHash + // comm and apmServiceName are provided by the eBPF programs + Comm string + ApmServiceName string + // containerID is annotated based on PID information + ContainerID string + Pid int64 + // Executable path is retrieved from /proc/PID/exe + Executable string + // ExtraMeta stores extra meta info that may have been produced by a + // `SampleAttrProducer` instance. May be nil. + ExtraMeta any +} + +// AttrKeyValue is a helper to populate Profile.attribute_table. +type AttrKeyValue[T string | int64] struct { + Key string + // Set to true for OTel SemConv attributes with requirement level: Required + Required bool + Value T +} + +// ExecInfo enriches an executable with additional metadata. +type ExecInfo struct { + FileName string + GnuBuildID string +} + +// SourceInfo allows mapping a frame to its source origin. +type SourceInfo struct { + LineNumber libpf.SourceLineno + FunctionOffset uint32 + FunctionName string + FilePath string +} + +// FuncInfo is a helper to construct profile.Function messages. +type FuncInfo struct { + Name string + FileName string +} diff --git a/reporter/otlp_reporter.go b/reporter/otlp_reporter.go index c1aa6e45..4ed59e2c 100644 --- a/reporter/otlp_reporter.go +++ b/reporter/otlp_reporter.go @@ -5,87 +5,30 @@ package reporter // import "go.opentelemetry.io/ebpf-profiler/reporter" import ( "context" - "crypto/rand" "crypto/tls" "maps" - "slices" "strconv" "time" lru "github.com/elastic/go-freelru" log "github.com/sirupsen/logrus" "github.com/zeebo/xxh3" - "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/collector/pdata/pprofile" + "go.opentelemetry.io/collector/pdata/pprofile/pprofileotlp" semconv "go.opentelemetry.io/otel/semconv/v1.25.0" - otlpcollector "go.opentelemetry.io/proto/otlp/collector/profiles/v1experimental" - common "go.opentelemetry.io/proto/otlp/common/v1" - profiles "go.opentelemetry.io/proto/otlp/profiles/v1experimental" - resource "go.opentelemetry.io/proto/otlp/resource/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/libpf/xsync" -) - -const ( - executableCacheLifetime = 1 * time.Hour - framesCacheLifetime = 1 * time.Hour + "go.opentelemetry.io/ebpf-profiler/reporter/internal/pdata" + "go.opentelemetry.io/ebpf-profiler/reporter/internal/samples" ) // Assert that we implement the full Reporter interface. var _ Reporter = (*OTLPReporter)(nil) -// execInfo enriches an executable with additional metadata. -type execInfo struct { - fileName string - gnuBuildID string -} - -// sourceInfo allows mapping a frame to its source origin. -type sourceInfo struct { - lineNumber libpf.SourceLineno - functionOffset uint32 - functionName string - filePath string -} - -// funcInfo is a helper to construct profile.Function messages. -type funcInfo struct { - name string - fileName string -} - -// traceAndMetaKey is the deduplication key for samples. This **must always** -// contain all trace fields that aren't already part of the trace hash to ensure -// that we don't accidentally merge traces with different fields. -type traceAndMetaKey struct { - hash libpf.TraceHash - // comm and apmServiceName are provided by the eBPF programs - comm string - apmServiceName string - // containerID is annotated based on PID information - containerID string - pid int64 - // executable path is retrieved from /proc/PID/exe - executable string - // extraMeta stores extra meta info that may have been produced by a - // `SampleAttrProducer` instance. May be nil. - extraMeta any -} - -// traceEvents holds known information about a trace. -type traceEvents struct { - files []libpf.FileID - linenos []libpf.AddressOrLineno - frameTypes []libpf.FrameType - mappingStarts []libpf.Address - mappingEnds []libpf.Address - mappingFileOffsets []uint64 - timestamps []uint64 // in nanoseconds -} - // OTLPReporter receives and transforms information to be OTLP/profiles compliant. type OTLPReporter struct { config *Config @@ -96,7 +39,7 @@ type OTLPReporter struct { version string // client for the connection to the receiver. - client otlpcollector.ProfilesServiceClient + client pprofileotlp.GRPCClient // runLoop handles the run loop runLoop *runLoop @@ -111,24 +54,18 @@ type OTLPReporter struct { // hostmetadata stores metadata that is sent out with every request. hostmetadata *lru.SyncedLRU[string, string] - // executables stores metadata for executables. - executables *lru.SyncedLRU[libpf.FileID, execInfo] - // cgroupv2ID caches PID to container ID information for cgroupv2 containers. cgroupv2ID *lru.SyncedLRU[libpf.PID, string] - // frames maps frame information to its source location. - frames *lru.SyncedLRU[libpf.FileID, *xsync.RWMutex[map[libpf.AddressOrLineno]sourceInfo]] + // pdata holds the generator for the data being exported. + pdata *pdata.Pdata // traceEvents stores reported trace events (trace metadata with frames and counts) - traceEvents xsync.RWMutex[map[traceAndMetaKey]*traceEvents] + traceEvents xsync.RWMutex[map[samples.TraceAndMetaKey]*samples.TraceEvents] // pkgGRPCOperationTimeout sets the time limit for GRPC requests. pkgGRPCOperationTimeout time.Duration - // samplesPerSecond is the number of samples per second. - samplesPerSecond int - // hostID is the unique identifier of the host. hostID string @@ -144,21 +81,6 @@ type OTLPReporter struct { // NewOTLP returns a new instance of OTLPReporter func NewOTLP(cfg *Config) (*OTLPReporter, error) { - executables, err := - lru.NewSynced[libpf.FileID, execInfo](cfg.ExecutablesCacheElements, libpf.FileID.Hash32) - if err != nil { - return nil, err - } - executables.SetLifetime(executableCacheLifetime) // Allow GC to clean stale items. - - frames, err := lru.NewSynced[libpf.FileID, - *xsync.RWMutex[map[libpf.AddressOrLineno]sourceInfo]]( - cfg.FramesCacheElements, libpf.FileID.Hash32) - if err != nil { - return nil, err - } - frames.SetLifetime(framesCacheLifetime) // Allow GC to clean stale items. - cgroupv2ID, err := lru.NewSynced[libpf.PID, string](cfg.CGroupCacheElements, func(pid libpf.PID) uint32 { return uint32(pid) }) if err != nil { @@ -175,26 +97,36 @@ func NewOTLP(cfg *Config) (*OTLPReporter, error) { return nil, err } + data, err := pdata.New( + cfg.SamplesPerSecond, + cfg.ExecutablesCacheElements, + cfg.FramesCacheElements, + cfg.ExtraSampleAttrProd, + ) + if err != nil { + return nil, err + } + return &OTLPReporter{ - config: cfg, - name: cfg.Name, - version: cfg.Version, - kernelVersion: cfg.KernelVersion, - hostName: cfg.HostName, - ipAddress: cfg.IPAddress, - samplesPerSecond: cfg.SamplesPerSecond, - hostID: strconv.FormatUint(cfg.HostID, 10), + config: cfg, + name: cfg.Name, + version: cfg.Version, + kernelVersion: cfg.KernelVersion, + hostName: cfg.HostName, + ipAddress: cfg.IPAddress, + hostID: strconv.FormatUint(cfg.HostID, 10), runLoop: &runLoop{ stopSignal: make(chan libpf.Void), }, pkgGRPCOperationTimeout: cfg.GRPCOperationTimeout, client: nil, rpcStats: NewStatsHandler(), - executables: executables, - frames: frames, + pdata: data, hostmetadata: hostmetadata, - traceEvents: xsync.NewRWMutex(map[traceAndMetaKey]*traceEvents{}), - cgroupv2ID: cgroupv2ID, + traceEvents: xsync.NewRWMutex( + map[samples.TraceAndMetaKey]*samples.TraceEvents{}, + ), + cgroupv2ID: cgroupv2ID, }, nil } @@ -223,30 +155,30 @@ func (r *OTLPReporter) ReportTraceEvent(trace *libpf.Trace, meta *TraceEventMeta meta.PID, err) } - key := traceAndMetaKey{ - hash: trace.Hash, - comm: meta.Comm, - executable: meta.Executable, - apmServiceName: meta.APMServiceName, - containerID: containerID, - pid: int64(meta.PID), - extraMeta: extraMeta, + key := samples.TraceAndMetaKey{ + Hash: trace.Hash, + Comm: meta.Comm, + Executable: meta.Executable, + ApmServiceName: meta.APMServiceName, + ContainerID: containerID, + Pid: int64(meta.PID), + ExtraMeta: extraMeta, } if events, exists := (*traceEventsMap)[key]; exists { - events.timestamps = append(events.timestamps, uint64(meta.Timestamp)) + events.Timestamps = append(events.Timestamps, uint64(meta.Timestamp)) (*traceEventsMap)[key] = events return } - (*traceEventsMap)[key] = &traceEvents{ - files: trace.Files, - linenos: trace.Linenos, - frameTypes: trace.FrameTypes, - mappingStarts: trace.MappingStart, - mappingEnds: trace.MappingEnd, - mappingFileOffsets: trace.MappingFileOffsets, - timestamps: []uint64{uint64(meta.Timestamp)}, + (*traceEventsMap)[key] = &samples.TraceEvents{ + Files: trace.Files, + Linenos: trace.Linenos, + FrameTypes: trace.FrameTypes, + MappingStarts: trace.MappingStart, + MappingEnds: trace.MappingEnd, + MappingFileOffsets: trace.MappingFileOffsets, + Timestamps: []uint64{uint64(meta.Timestamp)}, } } @@ -260,16 +192,16 @@ func (r *OTLPReporter) ReportCountForTrace(_ libpf.TraceHash, _ uint16, _ *Trace // ExecutableKnown returns true if the metadata of the Executable specified by fileID is // cached in the reporter. func (r *OTLPReporter) ExecutableKnown(fileID libpf.FileID) bool { - _, known := r.executables.GetAndRefresh(fileID, executableCacheLifetime) + _, known := r.pdata.Executables.GetAndRefresh(fileID, pdata.ExecutableCacheLifetime) return known } // ExecutableMetadata accepts a fileID with the corresponding filename // and caches this information. func (r *OTLPReporter) ExecutableMetadata(args *ExecutableMetadataArgs) { - r.executables.Add(args.FileID, execInfo{ - fileName: args.FileName, - gnuBuildID: args.GnuBuildID, + r.pdata.Executables.Add(args.FileID, samples.ExecInfo{ + FileName: args.FileName, + GnuBuildID: args.GnuBuildID, }) } @@ -277,8 +209,8 @@ func (r *OTLPReporter) ExecutableMetadata(args *ExecutableMetadataArgs) { // cached in the reporter. func (r *OTLPReporter) FrameKnown(frameID libpf.FrameID) bool { known := false - if frameMapLock, exists := r.frames.GetAndRefresh(frameID.FileID(), - framesCacheLifetime); exists { + if frameMapLock, exists := r.pdata.Frames.GetAndRefresh(frameID.FileID(), + pdata.FramesCacheLifetime); exists { frameMap := frameMapLock.RLock() defer frameMapLock.RUnlock(&frameMap) _, known = (*frameMap)[frameID.AddressOrLine()] @@ -295,7 +227,8 @@ func (r *OTLPReporter) FrameMetadata(args *FrameMetadataArgs) { fileID, args.FunctionName, args.FunctionOffset, args.SourceFile, args.SourceLine) - if frameMapLock, exists := r.frames.Get(fileID); exists { + if frameMapLock, exists := r.pdata.Frames.GetAndRefresh(fileID, + pdata.FramesCacheLifetime); exists { frameMap := frameMapLock.WLock() defer frameMapLock.WUnlock(&frameMap) @@ -304,28 +237,28 @@ func (r *OTLPReporter) FrameMetadata(args *FrameMetadataArgs) { // The new SourceFile may be empty, and we don't want to overwrite // an existing filePath with it. if s, exists := (*frameMap)[addressOrLine]; exists { - sourceFile = s.filePath + sourceFile = s.FilePath } } - (*frameMap)[addressOrLine] = sourceInfo{ - lineNumber: args.SourceLine, - filePath: sourceFile, - functionOffset: args.FunctionOffset, - functionName: args.FunctionName, + (*frameMap)[addressOrLine] = samples.SourceInfo{ + LineNumber: args.SourceLine, + FilePath: sourceFile, + FunctionOffset: args.FunctionOffset, + FunctionName: args.FunctionName, } return } - v := make(map[libpf.AddressOrLineno]sourceInfo) - v[addressOrLine] = sourceInfo{ - lineNumber: args.SourceLine, - filePath: args.SourceFile, - functionOffset: args.FunctionOffset, - functionName: args.FunctionName, + v := make(map[libpf.AddressOrLineno]samples.SourceInfo) + v[addressOrLine] = samples.SourceInfo{ + LineNumber: args.SourceLine, + FilePath: args.SourceFile, + FunctionOffset: args.FunctionOffset, + FunctionName: args.FunctionName, } mu := xsync.NewRWMutex(v) - r.frames.Add(fileID, &mu) + r.pdata.Frames.Add(fileID, &mu) } // ReportHostMetadata enqueues host metadata. @@ -379,7 +312,7 @@ func (r *OTLPReporter) Start(ctx context.Context) error { r.runLoop.Stop() return err } - r.client = otlpcollector.NewProfilesServiceClient(otlpGrpcConn) + r.client = pprofileotlp.NewGRPCClient(otlpGrpcConn) r.runLoop.Start(ctx, r.config.ReportInterval, func() { if err := r.reportOTLPProfile(ctx); err != nil { @@ -387,8 +320,7 @@ func (r *OTLPReporter) Start(ctx context.Context) error { } }, func() { // Allow the GC to purge expired entries to avoid memory leaks. - r.executables.PurgeExpired() - r.frames.PurgeExpired() + r.pdata.Purge() r.cgroupv2ID.PurgeExpired() }) @@ -408,359 +340,49 @@ func (r *OTLPReporter) Start(ctx context.Context) error { // reportOTLPProfile creates and sends out an OTLP profile. func (r *OTLPReporter) reportOTLPProfile(ctx context.Context) error { - profile, startTS, endTS := r.getProfile() + traceEvents := r.traceEvents.WLock() + events := maps.Clone(*traceEvents) + clear(*traceEvents) + r.traceEvents.WUnlock(&traceEvents) - if len(profile.Sample) == 0 { - log.Debugf("Skip sending of OTLP profile with no samples") - return nil + profiles := r.pdata.Generate(events) + for i := 0; i < profiles.ResourceProfiles().Len(); i++ { + r.setResource(profiles.ResourceProfiles().At(i)) } - pc := []*profiles.ProfileContainer{{ - ProfileId: mkProfileID(), - StartTimeUnixNano: startTS, - EndTimeUnixNano: endTS, - // Attributes - Optional element we do not use. - // DroppedAttributesCount - Optional element we do not use. - // OriginalPayloadFormat - Optional element we do not use. - // OriginalPayload - Optional element we do not use. - Profile: profile, - }} - - scopeProfiles := []*profiles.ScopeProfiles{{ - Profiles: pc, - Scope: &common.InstrumentationScope{ - Name: r.name, - Version: r.version, - }, - // SchemaUrl - This element is not well-defined yet. Therefore, we skip it. - }} - - resourceProfiles := []*profiles.ResourceProfiles{{ - Resource: r.getResource(), - ScopeProfiles: scopeProfiles, - // SchemaUrl - This element is not well-defined yet. Therefore, we skip it. - }} - - req := otlpcollector.ExportProfilesServiceRequest{ - ResourceProfiles: resourceProfiles, + if profiles.SampleCount() == 0 { + log.Debugf("Skip sending of OTLP profile with no samples") + return nil } + req := pprofileotlp.NewExportRequestFromProfiles(profiles) reqCtx, ctxCancel := context.WithTimeout(ctx, r.pkgGRPCOperationTimeout) defer ctxCancel() - _, err := r.client.Export(reqCtx, &req) + _, err := r.client.Export(reqCtx, req) return err } -// mkProfileID creates a random profile ID. -func mkProfileID() []byte { - profileID := make([]byte, 16) - _, err := rand.Read(profileID) - if err != nil { - return []byte("opentelemetry-ebpf-profiler") - } - return profileID -} - -// getResource returns the OTLP resource information of the origin of the profiles. +// setResource sets the resource information of the origin of the profiles. // Next step: maybe extend this information with go.opentelemetry.io/otel/sdk/resource. -func (r *OTLPReporter) getResource() *resource.Resource { +func (r *OTLPReporter) setResource(rp pprofile.ResourceProfiles) { keys := r.hostmetadata.Keys() - - attributes := make([]*common.KeyValue, 0, len(keys)+6) - - addAttr := func(k attribute.Key, v string) { - attributes = append(attributes, &common.KeyValue{ - Key: string(k), - Value: &common.AnyValue{Value: &common.AnyValue_StringValue{StringValue: v}}, - }) - } + attrs := rp.Resource().Attributes() // Add hostmedata to the attributes. for _, k := range keys { if v, ok := r.hostmetadata.Get(k); ok { - addAttr(attribute.Key(k), v) + attrs.PutStr(k, v) } } // Add event specific attributes. // These attributes are also included in the host metadata, but with different names/keys. // That makes our hostmetadata attributes incompatible with OTEL collectors. - addAttr(semconv.HostIDKey, r.hostID) - addAttr(semconv.HostIPKey, r.ipAddress) - addAttr(semconv.HostNameKey, r.hostName) - addAttr(semconv.ServiceVersionKey, r.version) - addAttr("os.kernel", r.kernelVersion) - - return &resource.Resource{ - Attributes: attributes, - } -} - -// getProfile returns an OTLP profile containing all collected samples up to this moment. -func (r *OTLPReporter) getProfile() (profile *profiles.Profile, startTS, endTS uint64) { - traceEvents := r.traceEvents.WLock() - samples := maps.Clone(*traceEvents) - clear(*traceEvents) - r.traceEvents.WUnlock(&traceEvents) - - // stringMap is a temporary helper that will build the StringTable. - // By specification, the first element should be empty. - stringMap := make(map[string]uint32) - stringMap[""] = 0 - - // funcMap is a temporary helper that will build the Function array - // in profile and make sure information is deduplicated. - funcMap := make(map[funcInfo]uint64) - funcMap[funcInfo{name: "", fileName: ""}] = 0 - - numSamples := len(samples) - profile = &profiles.Profile{ - // SampleType - Next step: Figure out the correct SampleType. - Sample: make([]*profiles.Sample, 0, numSamples), - SampleType: []*profiles.ValueType{{ - Type: int64(getStringMapIndex(stringMap, "samples")), - Unit: int64(getStringMapIndex(stringMap, "count")), - }}, - PeriodType: &profiles.ValueType{ - Type: int64(getStringMapIndex(stringMap, "cpu")), - Unit: int64(getStringMapIndex(stringMap, "nanoseconds")), - }, - Period: 1e9 / int64(r.samplesPerSecond), - // AttributeUnits - Optional element we do not use. - // LinkTable - Optional element we do not use. - // DropFrames - Optional element we do not use. - // KeepFrames - Optional element we do not use. - // Comment - Optional element we do not use. - // DefaultSampleType - Optional element we do not use. - } - - attrMgr := NewAttrTableManager(&profile.AttributeTable) - locationIndex := uint64(0) - - // Temporary lookup to reference existing Mappings. - fileIDtoMapping := make(map[libpf.FileID]uint64) - - for traceKey, traceInfo := range samples { - sample := &profiles.Sample{} - sample.LocationsStartIndex = locationIndex - - sample.StacktraceIdIndex = getStringMapIndex(stringMap, - traceKey.hash.Base64()) - - slices.Sort(traceInfo.timestamps) - startTS = traceInfo.timestamps[0] - endTS = traceInfo.timestamps[len(traceInfo.timestamps)-1] - - sample.TimestampsUnixNano = traceInfo.timestamps - sample.Value = []int64{1} - - // Walk every frame of the trace. - for i := range traceInfo.frameTypes { - var frameAttributes []AttrIndex - - attrMgr.AppendOptionalString(&frameAttributes, - "profile.frame.type", traceInfo.frameTypes[i].String()) - - loc := &profiles.Location{ - // Id - Optional element we do not use. - Address: uint64(traceInfo.linenos[i]), - // IsFolded - Optional element we do not use. - Attributes: frameAttributes, - } - - switch frameKind := traceInfo.frameTypes[i]; frameKind { - case libpf.NativeFrame: - // As native frames are resolved in the backend, we use Mapping to - // report these frames. - - var locationMappingIndex uint64 - if tmpMappingIndex, exists := fileIDtoMapping[traceInfo.files[i]]; exists { - locationMappingIndex = tmpMappingIndex - } else { - idx := uint64(len(fileIDtoMapping)) - fileIDtoMapping[traceInfo.files[i]] = idx - locationMappingIndex = idx - - // Ensure that actively used executables do not expire. - execInfo, exists := r.executables.GetAndRefresh(traceInfo.files[i], - executableCacheLifetime) - - // Next step: Select a proper default value, - // if the name of the executable is not known yet. - var fileName = "UNKNOWN" - if exists { - fileName = execInfo.fileName - } - - // Once SemConv and its Go package is released with the new - // semantic convention for build_id, replace these hard coded - // strings. - var mappingAttributes []AttrIndex - attrMgr.AppendOptionalString(&mappingAttributes, - "process.executable.build_id.gnu", execInfo.gnuBuildID) - attrMgr.AppendOptionalString(&mappingAttributes, - "process.executable.build_id.htlhash", traceInfo.files[i].StringNoQuotes()) - - profile.Mapping = append(profile.Mapping, &profiles.Mapping{ - // Id - Optional element we do not use. - MemoryStart: uint64(traceInfo.mappingStarts[i]), - MemoryLimit: uint64(traceInfo.mappingEnds[i]), - FileOffset: traceInfo.mappingFileOffsets[i], - Filename: int64(getStringMapIndex(stringMap, fileName)), - Attributes: mappingAttributes, - // HasFunctions - Optional element we do not use. - // HasFilenames - Optional element we do not use. - // HasLineNumbers - Optional element we do not use. - // HasInlinedFrames - Optional element we do not use. - }) - } - loc.MappingIndex = locationMappingIndex - case libpf.AbortFrame: - // Next step: Figure out how the OTLP protocol - // could handle artificial frames, like AbortFrame, - // that are not originated from a native or interpreted - // program. - default: - // Store interpreted frame information as a Line message: - line := &profiles.Line{} - - fileIDInfoLock, exists := r.frames.GetAndRefresh(traceInfo.files[i], - framesCacheLifetime) - if !exists { - // At this point, we do not have enough information for the frame. - // Therefore, we report a dummy entry and use the interpreter as filename. - line.FunctionIndex = createFunctionEntry(funcMap, - "UNREPORTED", frameKind.String()) - } else { - fileIDInfo := fileIDInfoLock.RLock() - if si, exists := (*fileIDInfo)[traceInfo.linenos[i]]; exists { - line.Line = int64(si.lineNumber) - - line.FunctionIndex = createFunctionEntry(funcMap, - si.functionName, si.filePath) - } else { - // At this point, we do not have enough information for the frame. - // Therefore, we report a dummy entry and use the interpreter as filename. - // To differentiate this case from the case where no information about - // the file ID is available at all, we use a different name for reported - // function. - line.FunctionIndex = createFunctionEntry(funcMap, - "UNRESOLVED", frameKind.String()) - } - fileIDInfoLock.RUnlock(&fileIDInfo) - } - loc.Line = append(loc.Line, line) - - // To be compliant with the protocol, generate a dummy mapping entry. - loc.MappingIndex = getDummyMappingIndex(fileIDtoMapping, - stringMap, attrMgr, profile, traceInfo.files[i]) - } - profile.Location = append(profile.Location, loc) - } - - attrMgr.AppendOptionalString(&sample.Attributes, - semconv.ContainerIDKey, traceKey.containerID) - attrMgr.AppendOptionalString(&sample.Attributes, - semconv.ThreadNameKey, traceKey.comm) - attrMgr.AppendOptionalString(&sample.Attributes, - semconv.ProcessExecutablePathKey, traceKey.executable) - attrMgr.AppendOptionalString(&sample.Attributes, - semconv.ServiceNameKey, traceKey.apmServiceName) - attrMgr.AppendInt(&sample.Attributes, - semconv.ProcessPIDKey, traceKey.pid) - - if r.config.ExtraSampleAttrProd != nil { - extra := r.config.ExtraSampleAttrProd.ExtraSampleAttrs(attrMgr, traceKey.extraMeta) - sample.Attributes = append(sample.Attributes, extra...) - } - - sample.LocationsLength = uint64(len(traceInfo.frameTypes)) - locationIndex += sample.LocationsLength - - profile.Sample = append(profile.Sample, sample) - } - log.Debugf("Reporting OTLP profile with %d samples", len(profile.Sample)) - - // Populate the deduplicated functions into profile. - funcTable := make([]*profiles.Function, len(funcMap)) - for v, idx := range funcMap { - funcTable[idx] = &profiles.Function{ - Name: int64(getStringMapIndex(stringMap, v.name)), - Filename: int64(getStringMapIndex(stringMap, v.fileName)), - } - } - profile.Function = append(profile.Function, funcTable...) - - // When ranging over stringMap, the order will be according to the - // hash value of the key. To get the correct order for profile.StringTable, - // put the values in stringMap, in the correct array order. - stringTable := make([]string, len(stringMap)) - for v, idx := range stringMap { - stringTable[idx] = v - } - profile.StringTable = append(profile.StringTable, stringTable...) - - // profile.LocationIndices is not optional, and we only write elements into - // profile.Location that at least one sample references. - profile.LocationIndices = make([]int64, len(profile.Location)) - for i := int64(0); i < int64(len(profile.Location)); i++ { - profile.LocationIndices[i] = i - } - - profile.DurationNanos = int64(endTS - startTS) - profile.TimeNanos = int64(startTS) - - return profile, startTS, endTS -} - -// getStringMapIndex inserts or looks up the index for value in stringMap. -func getStringMapIndex(stringMap map[string]uint32, value string) uint32 { - if idx, exists := stringMap[value]; exists { - return idx - } - - idx := uint32(len(stringMap)) - stringMap[value] = idx - - return idx -} - -// createFunctionEntry adds a new function and returns its reference index. -func createFunctionEntry(funcMap map[funcInfo]uint64, - name string, fileName string) uint64 { - key := funcInfo{ - name: name, - fileName: fileName, - } - if idx, exists := funcMap[key]; exists { - return idx - } - - idx := uint64(len(funcMap)) - funcMap[key] = idx - - return idx -} - -// getDummyMappingIndex inserts or looks up an entry for interpreted FileIDs. -func getDummyMappingIndex(fileIDtoMapping map[libpf.FileID]uint64, - stringMap map[string]uint32, attrMgr *AttrTableManager, - profile *profiles.Profile, fileID libpf.FileID) uint64 { - if tmpMappingIndex, exists := fileIDtoMapping[fileID]; exists { - return tmpMappingIndex - } - idx := uint64(len(fileIDtoMapping)) - fileIDtoMapping[fileID] = idx - - var mappingAttributes []AttrIndex - attrMgr.AppendOptionalString(&mappingAttributes, - "process.executable.build_id.htlhash", fileID.StringNoQuotes()) - - profile.Mapping = append(profile.Mapping, &profiles.Mapping{ - Filename: int64(getStringMapIndex(stringMap, "")), - Attributes: mappingAttributes, - }) - return idx + attrs.PutStr(string(semconv.HostIDKey), r.hostID) + attrs.PutStr(string(semconv.HostIPKey), r.ipAddress) + attrs.PutStr(string(semconv.HostNameKey), r.hostName) + attrs.PutStr(string(semconv.ServiceVersionKey), r.version) + attrs.PutStr("os.kernel", r.kernelVersion) } // waitGrpcEndpoint waits until the gRPC connection is established.