├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd └── utrace │ ├── main.go │ └── run │ ├── cmd.go │ ├── dump.go │ ├── options.go │ └── utrace.go ├── documentation ├── utrace-ebpf-map.svg ├── utrace-finish-task-switch.svg ├── utrace-kmalloc.svg ├── utrace-ovl.svg ├── utrace-perf-event.svg └── utrace-tracepoint.svg ├── ebpf ├── .gitignore ├── bpf │ ├── bpf.h │ ├── bpf_helpers.h │ └── bpf_map.h ├── main.c └── utrace │ ├── defs.h │ ├── exec.h │ └── utrace.h ├── go.mod ├── go.sum ├── pkg ├── assets │ └── probe.go └── utrace │ ├── byteorder.go │ ├── model.go │ ├── utils.go │ └── utrace.go └── tools.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | bin/ 4 | vendor/ 5 | graphs/ 6 | TODO 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: build-ebpf build 2 | 3 | build-ebpf: 4 | mkdir -p ebpf/bin 5 | clang -D__KERNEL__ -D__ASM_SYSREG_H \ 6 | -Wno-unused-value \ 7 | -Wno-pointer-sign \ 8 | -Wno-compare-distinct-pointer-types \ 9 | -Wunused \ 10 | -Wall \ 11 | -Werror \ 12 | -I/lib/modules/$$(uname -r)/build/include \ 13 | -I/lib/modules/$$(uname -r)/build/include/uapi \ 14 | -I/lib/modules/$$(uname -r)/build/include/generated/uapi \ 15 | -I/lib/modules/$$(uname -r)/build/arch/x86/include \ 16 | -I/lib/modules/$$(uname -r)/build/arch/x86/include/uapi \ 17 | -I/lib/modules/$$(uname -r)/build/arch/x86/include/generated \ 18 | -O2 -emit-llvm \ 19 | ebpf/main.c \ 20 | -c -o - | llc -march=bpf -filetype=obj -o ebpf/bin/probe.o 21 | go run github.com/shuLhan/go-bindata/cmd/go-bindata -pkg assets -prefix "ebpf/bin" -o "pkg/assets/probe.go" "ebpf/bin/probe.o" 22 | 23 | build: 24 | mkdir -p bin/ 25 | go build -o bin/ ./cmd/... 26 | 27 | run: 28 | sudo ./bin/utrace --generate-graph --kernel-pattern "^vfs_open$$" --latency 29 | 30 | install: 31 | sudo cp ./bin/utrace /usr/bin/ 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## UTrace 2 | 3 | UTrace is a tracing utility that leverages eBPF to trace both user space and kernel space functions. You can configure it to collect stack traces leading to a predefined executable symbol (or a kernel space symbol). Once the stack traces are collected, UTrace can compile them into a dot graph to visualize user space / kernel space context switches as well as pinpoint the most triggered call paths by looking at the heat map color coding. For the traced functions, UTrace can also compute the latency of the functions. 4 | 5 | ### System requirements 6 | 7 | - golang 1.13+ 8 | - This project was developed on an Ubuntu Focal machine (Linux Kernel 5.4) but should be compatible with 4.13+ kernels (not tested). 9 | - Kernel headers are expected to be installed in `lib/modules/$(uname -r)`, update the `Makefile` with their location otherwise. 10 | - clang & llvm (developed with 11.0.1) 11 | - (optionnal) graphviz 12 | 13 | ### Build 14 | 15 | 1) If you need to rebuild the eBPF programs, use the following command: 16 | 17 | ```shell script 18 | # ~ make build-ebpf 19 | ``` 20 | 21 | 2) To build UTrace, run: 22 | 23 | ```shell script 24 | # ~ make build 25 | ``` 26 | 27 | 3) To install UTrace (copy to /usr/bin/uprobe) run: 28 | ```shell script 29 | # ~ make install 30 | ``` 31 | 32 | ### Getting started 33 | 34 | UTrace needs to run as root. Run `sudo utrace -h` to get help. 35 | 36 | ```shell script 37 | # ~ utrace -h 38 | Usage: 39 | utrace [flags] 40 | 41 | Flags: 42 | -b, --binary string Path to the binary 43 | -g, --generate-graph when set, utrace will generate a .dot graph with the collected statistics 44 | -h, --help help for utrace 45 | -k, --kernel-pattern regexp kernel function(s) pattern to trace (default *) 46 | -t, --latency when set, utrace will use uprobes to compute functions latency 47 | -l, --log-level string log level, options: panic, fatal, error, warn, info, debug or trace (default "info") 48 | -p, --pattern regexp function(s) pattern to trace (default *) 49 | --pid int when set, utrace will add the provided pid to the list of traced pids. Leave empty to disable filtering. 50 | -s, --stack-traces when set, utrace will use uprobes to collect functions stack traces 51 | ``` 52 | 53 | ### Examples 54 | 55 | #### Find memory allocations 56 | 57 | 1) Let's trace all the memory allocations of `system-probe` on start up (`system-probe` is one of the binaries of the Datadog agent). To do so, run the following command: 58 | 59 | ```shell script 60 | # ~ sudo utrace --binary /home/vagrant/go/src/github.com/DataDog/datadog-agent/bin/system-probe/system-probe --generate-graph --kernel-pattern "(^__kmalloc$)" 61 | INFO[2021-02-03T00:09:25Z] Tracing started on 1 symbols ... (Ctrl + C to stop) 62 | ``` 63 | 64 | 2) Start `system-probe` and wait for a few seconds. On exit, utrace will dump all the collected stack traces to the screen and generate a dot graph in your temporary directory. 65 | 66 | ```shell script 67 | [...] 68 | 69 | 63888 user space stack traces collected (0 lost) 70 | 63888 kernel space stack traces collected (0 lost) 71 | Tracing lasted for 1m17.850732316s 72 | INFO[2021-02-03T00:10:43Z] Graph generated: /tmp/utrace-dump-120828521 73 | ``` 74 | 75 | 3) You can export the dot graph to an svg file using gaphviz. 76 | 77 | ```shell script 78 | # ~ dot -Tsvg -o /tmp/utrace-dump-120828521.svg /tmp/utrace-dump-120828521 79 | ``` 80 | 81 | ![utrace-kmalloc.svg](documentation/utrace-kmalloc.svg) 82 | 83 | #### Track context switches 84 | 85 | ```shell script 86 | # ~ sudo utrace --binary /home/vagrant/go/src/github.com/DataDog/datadog-agent/bin/system-probe/system-probe --generate-graph --kernel-pattern "(^finish_task_switch$)" 87 | ``` 88 | 89 | ![utrace-finish-task-switch.svg](documentation/utrace-finish-task-switch.svg) 90 | 91 | #### Trace eBPF map operations in system-probe 92 | 93 | ```shell script 94 | # ~ sudo utrace --binary /home/vagrant/go/src/github.com/DataDog/datadog-agent/bin/system-probe/system-probe --generate-graph --pattern "github.com/DataDog/ebpf-manager.\(\*PerfMap\)|github.com/cilium/ebpf.\(\*Map\)" 95 | ``` 96 | 97 | ![utrace-ebpf-map.svg](documentation/utrace-ebpf-map.svg) 98 | 99 | #### Trace all overlay operations in the kernel 100 | 101 | ```shell script 102 | # ~ sudo utrace --kernel-pattern "^ovl_" --generate-graph 103 | ``` 104 | 105 | ![utrace-ovl.svg](documentation/utrace-ovl.svg) 106 | 107 | #### Trace page faults in system-probe on startup 108 | 109 | ```shell script 110 | # ~ sudo utrace --binary /home/vagrant/go/src/github.com/DataDog/datadog-agent/bin/system-probe/system-probe --generate-graph --tracepoint "exceptions:page_fault_user" 111 | ``` 112 | 113 | ![utrace-tracepoint.svg](documentation/utrace-tracepoint.svg) 114 | 115 | #### Trace task-clock perf event in system-probe on startup 116 | 117 | ```shell script 118 | # ~ sudo utrace --binary /home/vagrant/go/src/github.com/DataDog/datadog-agent/bin/system-probe/system-probe --generate-graph --perf 1:1:10 119 | ``` 120 | 121 | ![utrace-perf-event.svg](documentation/utrace-perf-event.svg) -------------------------------------------------------------------------------- /cmd/utrace/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "github.com/sirupsen/logrus" 21 | 22 | "github.com/Gui774ume/utrace/cmd/utrace/run" 23 | ) 24 | 25 | func main() { 26 | logrus.SetFormatter(&logrus.TextFormatter{ 27 | FullTimestamp: true, 28 | TimestampFormat: "2006-01-02T15:04:05Z", 29 | DisableLevelTruncation: true, 30 | }) 31 | run.Utrace.Execute() 32 | } 33 | -------------------------------------------------------------------------------- /cmd/utrace/run/cmd.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package run 18 | 19 | import ( 20 | "github.com/spf13/cobra" 21 | ) 22 | 23 | // Utrace represents the base command of utrace 24 | var Utrace = &cobra.Command{ 25 | Use: "utrace", 26 | RunE: utraceCmd, 27 | } 28 | 29 | var options CLIOptions 30 | 31 | func init() { 32 | Utrace.Flags().VarP( 33 | NewLogLevelSanitizer(&options.LogLevel), 34 | "log-level", 35 | "l", 36 | `log level, options: panic, fatal, error, warn, info, debug or trace`) 37 | Utrace.Flags().BoolVarP( 38 | &options.GenerateGraph, 39 | "generate-graph", 40 | "g", 41 | false, 42 | `when set, utrace will generate a .dot graph with the collected statistics`) 43 | Utrace.Flags().VarP( 44 | NewUTraceOptionsSanitizer(&options.UTraceOptions, "binary"), 45 | "binary", 46 | "b", 47 | `list of paths to the binaries you want to trace`) 48 | Utrace.Flags().VarP( 49 | NewUTraceOptionsSanitizer(&options.UTraceOptions, "pattern"), 50 | "pattern", 51 | "p", 52 | `user space function(s) pattern to trace`) 53 | Utrace.Flags().VarP( 54 | NewUTraceOptionsSanitizer(&options.UTraceOptions, "kernel-pattern"), 55 | "kernel-pattern", 56 | "k", 57 | `kernel space function(s) pattern to trace`) 58 | Utrace.Flags().BoolVarP( 59 | &options.UTraceOptions.Latency, 60 | "latency", 61 | "t", 62 | false, 63 | `when set, utrace will use uprobes to compute functions latency`) 64 | Utrace.Flags().BoolVarP( 65 | &options.UTraceOptions.StackTraces, 66 | "stack-traces", 67 | "s", 68 | false, 69 | `when set, utrace will use uprobes to collect functions stack traces`) 70 | Utrace.Flags().Var( 71 | NewUTraceOptionsSanitizer(&options.UTraceOptions, "pid"), 72 | "pid", 73 | `list of pids to trace. Leave empty to disable filtering`) 74 | Utrace.Flags().Var( 75 | NewUTraceOptionsSanitizer(&options.UTraceOptions, "tracepoint"), 76 | "tracepoint", 77 | `list of tracepoints to trace. Expected format: [category]:[name]`) 78 | Utrace.Flags().Var( 79 | NewUTraceOptionsSanitizer(&options.UTraceOptions, "perf"), 80 | "perf", 81 | `list of perf events to trace. Expected format: [perf_event_type]:[perf_event_name]:[frequency], where perf_event_type, perf_event_name and frequency are numbers. For example, add '1:1:1' to trace PERF_COUNT_SW_TASK_CLOCK from the PERF_TYPE_SOFTWARE perf events at a frequency of 1 event per second`) 82 | } 83 | -------------------------------------------------------------------------------- /cmd/utrace/run/dump.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package run 18 | 19 | import ( 20 | "fmt" 21 | "io/ioutil" 22 | "os" 23 | "text/template" 24 | "time" 25 | 26 | "github.com/sirupsen/logrus" 27 | "golang.org/x/crypto/blake2b" 28 | 29 | "github.com/Gui774ume/utrace/pkg/utrace" 30 | ) 31 | 32 | func dump(report utrace.Report, options CLIOptions, tracer *utrace.UTrace) { 33 | if options.UTraceOptions.Latency { 34 | dumpReportWithLatency(report, tracer) 35 | } else { 36 | dumpReport(report, tracer) 37 | } 38 | var printLater []string 39 | for cookie, funcs := range report.GetFunctionsByHits() { 40 | binary := tracer.TracedBinaries[cookie] 41 | 42 | if options.UTraceOptions.StackTraces { 43 | if binary == nil { 44 | fmt.Printf("\n* Dumping kernel stack traces:\n") 45 | } else { 46 | fmt.Printf("\n* Dumping stack traces for %s:\n", binaryTitle(binary)) 47 | } 48 | outputFile, err := dumpStackTraces(report, funcs) 49 | if err != nil { 50 | logrus.Warnf("couldn't generate stack trace dump for %s: %s", binaryTitle(binary), err) 51 | } else { 52 | if binary != nil { 53 | printLater = append(printLater, fmt.Sprintf("User space stack traces dump for %s: %s", binaryTitle(binary), outputFile)) 54 | } else { 55 | printLater = append(printLater, fmt.Sprintf("Kernel stack traces dump: %s", outputFile)) 56 | } 57 | } 58 | } 59 | 60 | if options.GenerateGraph { 61 | outputFile, err := generateDotGraph(report, funcs, tracer.TracedBinaries[cookie]) 62 | if err != nil { 63 | logrus.Warnf("couldn't generate graph: %s", err) 64 | } else { 65 | if binary == nil { 66 | printLater = append(printLater, fmt.Sprintf("Kernel space graph: %s", outputFile)) 67 | } else { 68 | printLater = append(printLater, fmt.Sprintf("Userspace graph for %s: %s", binaryTitle(binary), outputFile)) 69 | } 70 | } 71 | } 72 | } 73 | logrus.Infof("%d user space stack traces collected (%d lost)", report.GetStackTraceCount().User, report.GetStackTraceCount().LostUser) 74 | logrus.Infof("%d kernel space stack traces collected (%d lost)", report.GetStackTraceCount().Kernel, report.GetStackTraceCount().LostKernel) 75 | logrus.Infof("Tracing lasted for %s", report.GetDuration()) 76 | 77 | for _, msg := range printLater { 78 | logrus.Infoln(msg) 79 | } 80 | } 81 | 82 | func generateNodeID(node utrace.StackTraceNode) string { 83 | name := node.Symbol.Name 84 | if name == utrace.SymbolNotFound.Name { 85 | if node.Symbol.Value > 0 { 86 | name = fmt.Sprintf("0x%x", node.Symbol.Value) 87 | } else { 88 | name = fmt.Sprintf("0x%x", node.Offset) 89 | } 90 | } 91 | var id string 92 | for _, b := range blake2b.Sum256([]byte(name)) { 93 | id += fmt.Sprintf("%v", b) 94 | } 95 | return id 96 | } 97 | 98 | type node struct { 99 | ID string 100 | Label string 101 | Size int 102 | Color string 103 | Hits uint64 104 | } 105 | 106 | type graph struct { 107 | Title string 108 | Nodes map[string]*node 109 | UserStackTraces []string 110 | KernelStackTraces []string 111 | Bridges []string 112 | } 113 | 114 | var ( 115 | userColor = "#8fbbff" 116 | kernelColor = "#77bf77" 117 | bridgeColor = "orange" 118 | ) 119 | 120 | func reverse(lines []string) []string { 121 | for i := 0; i < len(lines)/2; i++ { 122 | j := len(lines) - i - 1 123 | lines[i], lines[j] = lines[j], lines[i] 124 | } 125 | return lines 126 | } 127 | 128 | func generateDotGraph(report utrace.Report, tracedFuncs []utrace.Func, binary *utrace.TracedBinary) (string, error) { 129 | tmpl := `strict digraph { 130 | label = "{{ .Title }}" 131 | labelloc = "t" 132 | fontsize = 75 133 | fontcolor = "black" 134 | fontname = "arial" 135 | 136 | node [style=rounded, style="rounded", shape=record, fontname = "arial"] 137 | edge [color="#aaaaaa"] 138 | {{ range .Nodes }} 139 | {{ .ID }} [label="{{ .Label }}", fontsize={{ .Size }}, color="{{ .Color }}"]{{ end }} 140 | 141 | {{ range .UserStackTraces }} 142 | {{ . }} 143 | {{ end }} 144 | {{ range .Bridges }} 145 | {{ . }} [dir="both"] 146 | {{ end }} 147 | {{ range .KernelStackTraces }} 148 | {{ . }} 149 | {{ end }} 150 | } 151 | ` 152 | data := graph{ 153 | Nodes: make(map[string]*node), 154 | } 155 | 156 | maxHits := uint64(1) 157 | var usrFuncCount, krnFuncCount int 158 | for i, f := range tracedFuncs { 159 | if f.Type == utrace.Kernel { 160 | krnFuncCount++ 161 | } else { 162 | usrFuncCount++ 163 | } 164 | 165 | if f.Count == 0 { 166 | continue 167 | } 168 | 169 | if i == 0 { 170 | maxHits = f.Count 171 | } 172 | 173 | fNodeID := generateNodeID(utrace.StackTraceNode{Symbol: f.Symbol}) 174 | if graphNode, ok := data.Nodes[fNodeID]; ok { 175 | graphNode.Label = fmt.Sprintf("{ %s | hits:%d | avg_latency:%s }", f.Symbol.Name, f.Count, f.AverageLatency) 176 | graphNode.Hits += f.Count 177 | } else { 178 | var color string 179 | if f.Type == utrace.Kernel { 180 | color = kernelColor 181 | } else { 182 | color = userColor 183 | } 184 | data.Nodes[fNodeID] = &node{ 185 | ID: fNodeID, 186 | Size: 1, 187 | Hits: f.Count, 188 | Color: color, 189 | Label: fmt.Sprintf("{ %s | hits:%d | avg_latency:%s }", f.Symbol.Name, f.Count, f.AverageLatency), 190 | } 191 | if f.Symbol.Name == utrace.SymbolNotFound.Name { 192 | data.Nodes[fNodeID].Label = fmt.Sprintf("{ 0x%x }", f.Symbol.Value) 193 | } 194 | } 195 | 196 | maxTraceHits := 1 197 | for j, trace := range f.GetStackTracesByHits() { 198 | if j == 0 { 199 | maxTraceHits = trace.Count 200 | } 201 | var usertraceStr, kernelTraceStr string 202 | var userBridge, kernelBridge string 203 | for i, n := range trace.UserStacktrace { 204 | nodeID := generateNodeID(n) 205 | if i == 0 { 206 | userBridge = nodeID 207 | } 208 | if graphNode, ok := data.Nodes[nodeID]; ok { 209 | graphNode.Size += trace.Count 210 | } else { 211 | data.Nodes[nodeID] = &node{ 212 | ID: nodeID, 213 | Size: trace.Count, 214 | Color: userColor, 215 | Label: fmt.Sprintf("{ %s }", n.Symbol.Name), 216 | } 217 | if n.Symbol.Name == utrace.SymbolNotFound.Name { 218 | data.Nodes[nodeID].Label = fmt.Sprintf("{ 0x%x }", n.Offset) 219 | } 220 | } 221 | if len(usertraceStr) == 0 { 222 | usertraceStr = nodeID 223 | } else { 224 | usertraceStr = nodeID + " -> " + usertraceStr 225 | } 226 | } 227 | usertraceStr += fmt.Sprintf(`[color="%d", colorscheme="blues9"]`, int(6*float64(trace.Count)/float64(maxTraceHits)+3)) 228 | data.UserStackTraces = append(data.UserStackTraces, usertraceStr) 229 | 230 | for _, n := range trace.KernelStackTrace { 231 | nodeID := generateNodeID(n) 232 | if graphNode, ok := data.Nodes[nodeID]; ok { 233 | graphNode.Size += trace.Count 234 | } else { 235 | data.Nodes[nodeID] = &node{ 236 | ID: nodeID, 237 | Size: trace.Count, 238 | Color: kernelColor, 239 | Label: fmt.Sprintf("{ %s }", n.Symbol.Name), 240 | } 241 | if n.Symbol.Name == utrace.SymbolNotFound.Name { 242 | data.Nodes[nodeID].Label = fmt.Sprintf("{ 0x%x }", n.Offset) 243 | } 244 | } 245 | if len(kernelTraceStr) == 0 { 246 | kernelTraceStr = nodeID 247 | } else { 248 | kernelTraceStr = nodeID + " -> " + kernelTraceStr 249 | } 250 | kernelBridge = nodeID 251 | } 252 | kernelTraceStr += fmt.Sprintf(`[color="%d", colorscheme="greens9"]`, int(6*float64(trace.Count)/float64(maxTraceHits)+3)) 253 | data.KernelStackTraces = append(data.KernelStackTraces, kernelTraceStr) 254 | 255 | if len(kernelBridge) > 0 && len(userBridge) > 0 { 256 | data.Bridges = append(data.Bridges, 257 | fmt.Sprintf(`%s -> %s [color="%s"]`, userBridge, kernelBridge, bridgeColor)) 258 | } 259 | } 260 | data.UserStackTraces = reverse(data.UserStackTraces) 261 | data.KernelStackTraces = reverse(data.KernelStackTraces) 262 | } 263 | 264 | // normalize nodes size 265 | var maxCount int 266 | for _, graphNode := range data.Nodes { 267 | if maxCount < graphNode.Size { 268 | maxCount = graphNode.Size 269 | } 270 | } 271 | for _, graphNode := range data.Nodes { 272 | graphNode.Size = 10 + (30 * graphNode.Size / maxCount) + int(10*graphNode.Hits/maxHits) 273 | } 274 | 275 | // generate graph title 276 | if binary != nil { 277 | data.Title += fmt.Sprintf("[binary: %s]", binaryTitle(binary)) 278 | } 279 | data.Title += fmt.Sprintf("\n[kernel: %d traced function(s), %d stack trace(s), %d lost]", krnFuncCount, report.GetStackTraceCount().Kernel, report.GetStackTraceCount().LostKernel) 280 | data.Title += fmt.Sprintf("\n[user: %d traced function(s), %d stack trace(s), %d lost]", usrFuncCount, report.GetStackTraceCount().User, report.GetStackTraceCount().LostUser) 281 | data.Title += fmt.Sprintf("\n[duration: %s]", report.GetDuration()) 282 | 283 | f, err := ioutil.TempFile("/tmp", "utrace-graph-") 284 | if err != nil { 285 | return "", err 286 | } 287 | defer f.Close() 288 | 289 | if err := os.Chmod(f.Name(), os.ModePerm); err != nil { 290 | return "", err 291 | } 292 | 293 | t := template.Must(template.New("tmpl").Parse(tmpl)) 294 | if err := t.Execute(f, data); err != nil { 295 | return "", err 296 | } 297 | return f.Name(), nil 298 | } 299 | 300 | func dumpReport(report utrace.Report, tracer *utrace.UTrace) { 301 | for cookie, funcs := range report.GetFunctionsByHits() { 302 | binary := tracer.TracedBinaries[cookie] 303 | if binary != nil { 304 | fmt.Printf("\nUserspace and kernel space functions hits for %s:\n", binaryTitle(binary)) 305 | } else { 306 | fmt.Printf("\nKernel functions hits:\n") 307 | } 308 | fmt.Printf("\n%10v %s\n", "COUNT", "FUNC_NAME") 309 | for _, f := range funcs { 310 | fmt.Printf("%10v %s\n", f.Count, f.Symbol.Name) 311 | } 312 | } 313 | } 314 | 315 | func dumpReportWithLatency(report utrace.Report, tracer *utrace.UTrace) { 316 | for cookie, funcs := range report.GetFunctionsByLatency() { 317 | binary := tracer.TracedBinaries[cookie] 318 | if binary != nil { 319 | fmt.Printf("\nUserspace and kernel space functions ordered by latency for %s:\n", binaryTitle(binary)) 320 | } else { 321 | fmt.Printf("\nKernel functions ordered by latency:\n") 322 | } 323 | fmt.Printf("%10v %20v %s\n", "COUNT", "AVG_LATENCY", "FUNC_NAME") 324 | for _, f := range funcs { 325 | fmt.Printf("%10v %20v %s\n", f.Count, f.AverageLatency, f.Symbol.Name) 326 | } 327 | } 328 | } 329 | 330 | const ( 331 | stackTracesDumpHeader = "total_hits;symbol_name;symbol_type;symbol_addr;offset;avg_latency;\n" 332 | stackTracesDumpNodeFormat = "%s;%s;0x%x;%d;%s;" 333 | ) 334 | 335 | func binaryTitle(binary *utrace.TracedBinary) string { 336 | var str string 337 | if len(binary.ResolvedPath) > 0 { 338 | str += binary.ResolvedPath 339 | } else { 340 | str += binary.Path 341 | } 342 | if len(binary.Pids) > 0 { 343 | str += fmt.Sprintf(" %v", binary.Pids) 344 | } 345 | return str 346 | } 347 | 348 | func dumpStackTraces(report utrace.Report, tracedFuncs []utrace.Func) (string, error) { 349 | d, err := ioutil.TempFile("/tmp", "utrace-dump-") 350 | if err != nil { 351 | return "", err 352 | } 353 | defer d.Close() 354 | if _, err = d.WriteString(stackTracesDumpHeader); err != nil { 355 | return "", err 356 | } 357 | 358 | var latency time.Duration 359 | for _, f := range tracedFuncs { 360 | fmt.Printf("\n - symbol %v:\n", f.Symbol.Name) 361 | if len(f.GetStackTracesByHits()) == 0 { 362 | continue 363 | } 364 | 365 | for stackID, trace := range f.GetStackTracesByHits() { 366 | fmt.Printf("\n\t* Stack %d [%d hit(s)]\n", stackID, trace.Count) 367 | if _, err = d.WriteString(fmt.Sprintf("%d;", trace.Count)); err != nil { 368 | return "", err 369 | } 370 | 371 | for _, n := range trace.UserStacktrace { 372 | latency = 0 373 | fmt.Printf("\t\t- %s (offset: 0x%x)\n", n.Symbol.Name, n.Offset) 374 | if fun := report.GetFunc(n.FuncID); fun.Symbol.Name != utrace.SymbolNotFound.Name { 375 | fmt.Printf("\t\t\thit(s): %d avg_latency: %s\n", fun.Count, fun.AverageLatency) 376 | latency = fun.AverageLatency 377 | } 378 | if _, err = d.WriteString(fmt.Sprintf(stackTracesDumpNodeFormat, n.Symbol.Name, n.Type, n.Symbol.Value, n.Offset, latency)); err != nil { 379 | return "", err 380 | } 381 | } 382 | if len(trace.KernelStackTrace) > 0 { 383 | fmt.Print("\t\t--------------------------\n") 384 | } 385 | for _, n := range trace.KernelStackTrace { 386 | latency = 0 387 | fmt.Printf("\t\t- %s (offset: 0x%x)\n", n.Symbol.Name, n.Offset) 388 | if fun := report.GetFunc(n.FuncID); fun.Symbol.Name != utrace.SymbolNotFound.Name { 389 | fmt.Printf("\t\t\thit(s): %d avg_latency: %s\n", fun.Count, fun.AverageLatency) 390 | latency = fun.AverageLatency 391 | } 392 | if _, err = d.WriteString(fmt.Sprintf(stackTracesDumpNodeFormat, n.Symbol.Name, n.Type, n.Symbol.Value, n.Offset, latency)); err != nil { 393 | return "", err 394 | } 395 | } 396 | if _, err = d.WriteString("\n"); err != nil { 397 | return "", err 398 | } 399 | } 400 | } 401 | return d.Name(), nil 402 | } 403 | -------------------------------------------------------------------------------- /cmd/utrace/run/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package run 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "regexp" 23 | "strconv" 24 | 25 | "github.com/sirupsen/logrus" 26 | 27 | "github.com/Gui774ume/utrace/pkg/utrace" 28 | ) 29 | 30 | // CLIOptions are the command line options of ssh-probe 31 | type CLIOptions struct { 32 | LogLevel logrus.Level 33 | GenerateGraph bool 34 | UTraceOptions utrace.Options 35 | } 36 | 37 | // LogLevelSanitizer is a log level sanitizer that ensures that the provided log level exists 38 | type LogLevelSanitizer struct { 39 | logLevel *logrus.Level 40 | } 41 | 42 | // NewLogLevelSanitizer creates a new instance of LogLevelSanitizer. The sanitized level will be written in the provided 43 | // logrus level 44 | func NewLogLevelSanitizer(sanitizedLevel *logrus.Level) *LogLevelSanitizer { 45 | *sanitizedLevel = logrus.InfoLevel 46 | return &LogLevelSanitizer{ 47 | logLevel: sanitizedLevel, 48 | } 49 | } 50 | 51 | func (lls *LogLevelSanitizer) String() string { 52 | return fmt.Sprintf("%v", *lls.logLevel) 53 | } 54 | 55 | func (lls *LogLevelSanitizer) Set(val string) error { 56 | sanitized, err := logrus.ParseLevel(val) 57 | if err != nil { 58 | return err 59 | } 60 | *lls.logLevel = sanitized 61 | return nil 62 | } 63 | 64 | func (lls *LogLevelSanitizer) Type() string { 65 | return "string" 66 | } 67 | 68 | // UTraceOptionsSanitizer is a generic options sanitizer for UTrace 69 | type UTraceOptionsSanitizer struct { 70 | field string 71 | options *utrace.Options 72 | } 73 | 74 | // NewUTraceOptionsSanitizer creates a new instance of UTraceOptionsSanitizer 75 | func NewUTraceOptionsSanitizer(options *utrace.Options, field string) *UTraceOptionsSanitizer { 76 | return &UTraceOptionsSanitizer{ 77 | options: options, 78 | field: field, 79 | } 80 | } 81 | 82 | func (uos *UTraceOptionsSanitizer) String() string { 83 | switch uos.field { 84 | case "pid": 85 | return fmt.Sprintf("%v", uos.options.PIDFilter) 86 | case "binary": 87 | return fmt.Sprintf("%v", uos.options.Binary) 88 | case "pattern": 89 | return fmt.Sprintf("%v", uos.options.FuncPattern) 90 | case "kernel-pattern": 91 | return fmt.Sprintf("%v", uos.options.KernelFuncPattern) 92 | case "tracepoint": 93 | return fmt.Sprintf("%v", uos.options.Tracepoints) 94 | case "perf": 95 | return fmt.Sprintf("%v", uos.options.PerfEvents) 96 | } 97 | return "" 98 | } 99 | 100 | func (uos *UTraceOptionsSanitizer) Set(val string) error { 101 | switch uos.field { 102 | case "pid": 103 | pid, err := strconv.Atoi(val) 104 | if err != nil { 105 | return fmt.Errorf("%v is not a valid pid value: %v", val, err) 106 | } 107 | uos.options.PIDFilter = append(uos.options.PIDFilter, pid) 108 | case "binary": 109 | if len(val) == 0 { 110 | return nil 111 | } 112 | if len(val) > utrace.PathMax { 113 | return fmt.Errorf("%v is longer than the maximum length allowed for a binary path: got %d, limit is %d", val, len(val), utrace.PathMax) 114 | } 115 | // check if the file exists 116 | if _, err := os.Stat(val); err != nil { 117 | return fmt.Errorf("can't trace %s: %v", val, err) 118 | } 119 | uos.options.Binary = append(uos.options.Binary, val) 120 | case "pattern": 121 | if len(val) == 0 { 122 | return fmt.Errorf("empty pattern") 123 | } 124 | pattern, err := regexp.Compile(val) 125 | if err != nil { 126 | return fmt.Errorf("'%s' isn't a valid pattern: %v", val, err) 127 | } 128 | uos.options.FuncPattern = pattern 129 | case "kernel-pattern": 130 | if len(val) == 0 { 131 | return fmt.Errorf("empty kernel pattern") 132 | } 133 | pattern, err := regexp.Compile(val) 134 | if err != nil { 135 | return fmt.Errorf("'%s' isn't a valid kernel pattern: %v", val, err) 136 | } 137 | uos.options.KernelFuncPattern = pattern 138 | case "tracepoint": 139 | if len(val) == 0 { 140 | return fmt.Errorf("empty tracepoint") 141 | } 142 | uos.options.Tracepoints = append(uos.options.Tracepoints, val) 143 | case "perf": 144 | if len(val) == 0 { 145 | return fmt.Errorf("empty perf event") 146 | } 147 | uos.options.PerfEvents = append(uos.options.PerfEvents, val) 148 | } 149 | return nil 150 | } 151 | 152 | func (uos *UTraceOptionsSanitizer) Type() string { 153 | switch uos.field { 154 | case "pid": 155 | return "int array" 156 | case "binary": 157 | return "string array" 158 | case "pattern", "kernel-pattern": 159 | return "regexp" 160 | case "tracepoint": 161 | return "string array" 162 | case "perf": 163 | return "string array" 164 | } 165 | return "" 166 | } 167 | -------------------------------------------------------------------------------- /cmd/utrace/run/utrace.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package run 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "os/signal" 23 | 24 | "github.com/Gui774ume/utrace/pkg/utrace" 25 | "github.com/pkg/errors" 26 | "github.com/sirupsen/logrus" 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | func utraceCmd(cmd *cobra.Command, args []string) error { 31 | // Set log level 32 | logrus.SetLevel(options.LogLevel) 33 | 34 | if options.GenerateGraph { 35 | options.UTraceOptions.StackTraces = true 36 | } 37 | 38 | tracer := utrace.NewUTrace(options.UTraceOptions) 39 | if err := tracer.Start(); err != nil { 40 | return errors.Wrap(err, "couldn't start") 41 | } 42 | 43 | wait() 44 | 45 | // Dump hit counters 46 | report, err := tracer.Stop() 47 | if err != nil { 48 | logrus.Error(errors.Wrap(err, "couldn't dump utrace")) 49 | return nil 50 | } 51 | 52 | dump(report, options, tracer) 53 | return nil 54 | } 55 | 56 | // wait stops the main goroutine until an interrupt or kill signal is sent 57 | func wait() { 58 | sig := make(chan os.Signal, 1) 59 | signal.Notify(sig, os.Interrupt, os.Kill) 60 | <-sig 61 | fmt.Println() 62 | } 63 | -------------------------------------------------------------------------------- /ebpf/.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | -------------------------------------------------------------------------------- /ebpf/bpf/bpf_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_HELPERS__ 3 | #define __BPF_HELPERS__ 4 | 5 | #define __uint(name, val) int (*name)[val] 6 | #define __type(name, val) val *name 7 | 8 | /* helper macro to print out debug messages */ 9 | #define bpf_printk(fmt, ...) \ 10 | ({ \ 11 | char ____fmt[] = fmt; \ 12 | bpf_trace_printk(____fmt, sizeof(____fmt), \ 13 | ##__VA_ARGS__); \ 14 | }) 15 | 16 | #ifdef __clang__ 17 | 18 | /* helper macro to place programs, maps, license in 19 | * different sections in elf_bpf file. Section names 20 | * are interpreted by elf_bpf loader 21 | */ 22 | #define SEC(NAME) __attribute__((section(NAME), used)) 23 | 24 | /* helper functions called from eBPF programs written in C */ 25 | static void *(*bpf_map_lookup_elem)(void *map, const void *key) = 26 | (void *) BPF_FUNC_map_lookup_elem; 27 | static int (*bpf_map_update_elem)(void *map, const void *key, const void *value, 28 | unsigned long long flags) = 29 | (void *) BPF_FUNC_map_update_elem; 30 | static int (*bpf_map_delete_elem)(void *map, const void *key) = 31 | (void *) BPF_FUNC_map_delete_elem; 32 | static int (*bpf_map_push_elem)(void *map, const void *value, 33 | unsigned long long flags) = 34 | (void *) BPF_FUNC_map_push_elem; 35 | static int (*bpf_map_pop_elem)(void *map, void *value) = 36 | (void *) BPF_FUNC_map_pop_elem; 37 | static int (*bpf_map_peek_elem)(void *map, void *value) = 38 | (void *) BPF_FUNC_map_peek_elem; 39 | static int (*bpf_probe_read)(void *dst, int size, const void *unsafe_ptr) = 40 | (void *) BPF_FUNC_probe_read; 41 | static unsigned long long (*bpf_ktime_get_ns)(void) = 42 | (void *) BPF_FUNC_ktime_get_ns; 43 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = 44 | (void *) BPF_FUNC_trace_printk; 45 | static void (*bpf_tail_call)(void *ctx, void *map, int index) = 46 | (void *) BPF_FUNC_tail_call; 47 | static unsigned long long (*bpf_get_smp_processor_id)(void) = 48 | (void *) BPF_FUNC_get_smp_processor_id; 49 | static unsigned long long (*bpf_get_current_pid_tgid)(void) = 50 | (void *) BPF_FUNC_get_current_pid_tgid; 51 | static unsigned long long (*bpf_get_current_uid_gid)(void) = 52 | (void *) BPF_FUNC_get_current_uid_gid; 53 | static int (*bpf_get_current_comm)(void *buf, int buf_size) = 54 | (void *) BPF_FUNC_get_current_comm; 55 | static unsigned long long (*bpf_perf_event_read)(void *map, 56 | unsigned long long flags) = 57 | (void *) BPF_FUNC_perf_event_read; 58 | static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) = 59 | (void *) BPF_FUNC_clone_redirect; 60 | static int (*bpf_redirect)(int ifindex, int flags) = 61 | (void *) BPF_FUNC_redirect; 62 | static int (*bpf_redirect_map)(void *map, int key, int flags) = 63 | (void *) BPF_FUNC_redirect_map; 64 | static int (*bpf_perf_event_output)(void *ctx, void *map, 65 | unsigned long long flags, void *data, 66 | int size) = 67 | (void *) BPF_FUNC_perf_event_output; 68 | static int (*bpf_get_stackid)(void *ctx, void *map, int flags) = 69 | (void *) BPF_FUNC_get_stackid; 70 | static int (*bpf_probe_write_user)(void *dst, const void *src, int size) = 71 | (void *) BPF_FUNC_probe_write_user; 72 | static int (*bpf_current_task_under_cgroup)(void *map, int index) = 73 | (void *) BPF_FUNC_current_task_under_cgroup; 74 | static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) = 75 | (void *) BPF_FUNC_skb_get_tunnel_key; 76 | static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) = 77 | (void *) BPF_FUNC_skb_set_tunnel_key; 78 | static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) = 79 | (void *) BPF_FUNC_skb_get_tunnel_opt; 80 | static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = 81 | (void *) BPF_FUNC_skb_set_tunnel_opt; 82 | static unsigned long long (*bpf_get_prandom_u32)(void) = 83 | (void *) BPF_FUNC_get_prandom_u32; 84 | static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = 85 | (void *) BPF_FUNC_xdp_adjust_head; 86 | static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) = 87 | (void *) BPF_FUNC_xdp_adjust_meta; 88 | static int (*bpf_get_socket_cookie)(void *ctx) = 89 | (void *) BPF_FUNC_get_socket_cookie; 90 | static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval, 91 | int optlen) = 92 | (void *) BPF_FUNC_setsockopt; 93 | static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval, 94 | int optlen) = 95 | (void *) BPF_FUNC_getsockopt; 96 | static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) = 97 | (void *) BPF_FUNC_sock_ops_cb_flags_set; 98 | static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) = 99 | (void *) BPF_FUNC_sk_redirect_map; 100 | static int (*bpf_sk_redirect_hash)(void *ctx, void *map, void *key, int flags) = 101 | (void *) BPF_FUNC_sk_redirect_hash; 102 | static int (*bpf_sock_map_update)(void *map, void *key, void *value, 103 | unsigned long long flags) = 104 | (void *) BPF_FUNC_sock_map_update; 105 | static int (*bpf_sock_hash_update)(void *map, void *key, void *value, 106 | unsigned long long flags) = 107 | (void *) BPF_FUNC_sock_hash_update; 108 | static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags, 109 | void *buf, unsigned int buf_size) = 110 | (void *) BPF_FUNC_perf_event_read_value; 111 | static int (*bpf_perf_prog_read_value)(void *ctx, void *buf, 112 | unsigned int buf_size) = 113 | (void *) BPF_FUNC_perf_prog_read_value; 114 | static int (*bpf_override_return)(void *ctx, unsigned long rc) = 115 | (void *) BPF_FUNC_override_return; 116 | static int (*bpf_msg_redirect_map)(void *ctx, void *map, int key, int flags) = 117 | (void *) BPF_FUNC_msg_redirect_map; 118 | static int (*bpf_msg_redirect_hash)(void *ctx, 119 | void *map, void *key, int flags) = 120 | (void *) BPF_FUNC_msg_redirect_hash; 121 | static int (*bpf_msg_apply_bytes)(void *ctx, int len) = 122 | (void *) BPF_FUNC_msg_apply_bytes; 123 | static int (*bpf_msg_cork_bytes)(void *ctx, int len) = 124 | (void *) BPF_FUNC_msg_cork_bytes; 125 | static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) = 126 | (void *) BPF_FUNC_msg_pull_data; 127 | static int (*bpf_msg_push_data)(void *ctx, int start, int end, int flags) = 128 | (void *) BPF_FUNC_msg_push_data; 129 | static int (*bpf_msg_pop_data)(void *ctx, int start, int cut, int flags) = 130 | (void *) BPF_FUNC_msg_pop_data; 131 | static int (*bpf_bind)(void *ctx, void *addr, int addr_len) = 132 | (void *) BPF_FUNC_bind; 133 | static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) = 134 | (void *) BPF_FUNC_xdp_adjust_tail; 135 | static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state, 136 | int size, int flags) = 137 | (void *) BPF_FUNC_skb_get_xfrm_state; 138 | static int (*bpf_sk_select_reuseport)(void *ctx, void *map, void *key, __u32 flags) = 139 | (void *) BPF_FUNC_sk_select_reuseport; 140 | static int (*bpf_get_stack)(void *ctx, void *buf, int size, int flags) = 141 | (void *) BPF_FUNC_get_stack; 142 | static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, 143 | int plen, __u32 flags) = 144 | (void *) BPF_FUNC_fib_lookup; 145 | static int (*bpf_lwt_push_encap)(void *ctx, unsigned int type, void *hdr, 146 | unsigned int len) = 147 | (void *) BPF_FUNC_lwt_push_encap; 148 | static int (*bpf_lwt_seg6_store_bytes)(void *ctx, unsigned int offset, 149 | void *from, unsigned int len) = 150 | (void *) BPF_FUNC_lwt_seg6_store_bytes; 151 | static int (*bpf_lwt_seg6_action)(void *ctx, unsigned int action, void *param, 152 | unsigned int param_len) = 153 | (void *) BPF_FUNC_lwt_seg6_action; 154 | static int (*bpf_lwt_seg6_adjust_srh)(void *ctx, unsigned int offset, 155 | unsigned int len) = 156 | (void *) BPF_FUNC_lwt_seg6_adjust_srh; 157 | static int (*bpf_rc_repeat)(void *ctx) = 158 | (void *) BPF_FUNC_rc_repeat; 159 | static int (*bpf_rc_keydown)(void *ctx, unsigned int protocol, 160 | unsigned long long scancode, unsigned int toggle) = 161 | (void *) BPF_FUNC_rc_keydown; 162 | static unsigned long long (*bpf_get_current_cgroup_id)(void) = 163 | (void *) BPF_FUNC_get_current_cgroup_id; 164 | static void *(*bpf_get_local_storage)(void *map, unsigned long long flags) = 165 | (void *) BPF_FUNC_get_local_storage; 166 | static unsigned long long (*bpf_skb_cgroup_id)(void *ctx) = 167 | (void *) BPF_FUNC_skb_cgroup_id; 168 | static unsigned long long (*bpf_skb_ancestor_cgroup_id)(void *ctx, int level) = 169 | (void *) BPF_FUNC_skb_ancestor_cgroup_id; 170 | static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, 171 | struct bpf_sock_tuple *tuple, 172 | int size, unsigned long long netns_id, 173 | unsigned long long flags) = 174 | (void *) BPF_FUNC_sk_lookup_tcp; 175 | static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, 176 | struct bpf_sock_tuple *tuple, 177 | int size, unsigned long long netns_id, 178 | unsigned long long flags) = 179 | (void *) BPF_FUNC_skc_lookup_tcp; 180 | static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, 181 | struct bpf_sock_tuple *tuple, 182 | int size, unsigned long long netns_id, 183 | unsigned long long flags) = 184 | (void *) BPF_FUNC_sk_lookup_udp; 185 | static int (*bpf_sk_release)(struct bpf_sock *sk) = 186 | (void *) BPF_FUNC_sk_release; 187 | static int (*bpf_skb_vlan_push)(void *ctx, __be16 vlan_proto, __u16 vlan_tci) = 188 | (void *) BPF_FUNC_skb_vlan_push; 189 | static int (*bpf_skb_vlan_pop)(void *ctx) = 190 | (void *) BPF_FUNC_skb_vlan_pop; 191 | static int (*bpf_rc_pointer_rel)(void *ctx, int rel_x, int rel_y) = 192 | (void *) BPF_FUNC_rc_pointer_rel; 193 | static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) = 194 | (void *) BPF_FUNC_spin_lock; 195 | static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = 196 | (void *) BPF_FUNC_spin_unlock; 197 | static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = 198 | (void *) BPF_FUNC_sk_fullsock; 199 | static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = 200 | (void *) BPF_FUNC_tcp_sock; 201 | static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = 202 | (void *) BPF_FUNC_get_listener_sock; 203 | static int (*bpf_skb_ecn_set_ce)(void *ctx) = 204 | (void *) BPF_FUNC_skb_ecn_set_ce; 205 | static int (*bpf_tcp_check_syncookie)(struct bpf_sock *sk, 206 | void *ip, int ip_len, void *tcp, int tcp_len) = 207 | (void *) BPF_FUNC_tcp_check_syncookie; 208 | static int (*bpf_sysctl_get_name)(void *ctx, char *buf, 209 | unsigned long long buf_len, 210 | unsigned long long flags) = 211 | (void *) BPF_FUNC_sysctl_get_name; 212 | static int (*bpf_sysctl_get_current_value)(void *ctx, char *buf, 213 | unsigned long long buf_len) = 214 | (void *) BPF_FUNC_sysctl_get_current_value; 215 | static int (*bpf_sysctl_get_new_value)(void *ctx, char *buf, 216 | unsigned long long buf_len) = 217 | (void *) BPF_FUNC_sysctl_get_new_value; 218 | static int (*bpf_sysctl_set_new_value)(void *ctx, const char *buf, 219 | unsigned long long buf_len) = 220 | (void *) BPF_FUNC_sysctl_set_new_value; 221 | static int (*bpf_strtol)(const char *buf, unsigned long long buf_len, 222 | unsigned long long flags, long *res) = 223 | (void *) BPF_FUNC_strtol; 224 | static int (*bpf_strtoul)(const char *buf, unsigned long long buf_len, 225 | unsigned long long flags, unsigned long *res) = 226 | (void *) BPF_FUNC_strtoul; 227 | static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk, 228 | void *value, __u64 flags) = 229 | (void *) BPF_FUNC_sk_storage_get; 230 | static int (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) = 231 | (void *)BPF_FUNC_sk_storage_delete; 232 | static int (*bpf_send_signal)(unsigned sig) = (void *)BPF_FUNC_send_signal; 233 | static long long (*bpf_tcp_gen_syncookie)(struct bpf_sock *sk, void *ip, 234 | int ip_len, void *tcp, int tcp_len) = 235 | (void *) BPF_FUNC_tcp_gen_syncookie; 236 | 237 | /* llvm builtin functions that eBPF C program may use to 238 | * emit BPF_LD_ABS and BPF_LD_IND instructions 239 | */ 240 | struct sk_buff; 241 | unsigned long long load_byte(void *skb, 242 | unsigned long long off) asm("llvm.bpf.load.byte"); 243 | unsigned long long load_half(void *skb, 244 | unsigned long long off) asm("llvm.bpf.load.half"); 245 | unsigned long long load_word(void *skb, 246 | unsigned long long off) asm("llvm.bpf.load.word"); 247 | 248 | #else 249 | 250 | #include 251 | 252 | #endif 253 | 254 | #define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \ 255 | struct ____btf_map_##name { \ 256 | type_key key; \ 257 | type_val value; \ 258 | }; \ 259 | struct ____btf_map_##name \ 260 | __attribute__ ((section(".maps." #name), used)) \ 261 | ____btf_map_##name = { } 262 | 263 | static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = 264 | (void *) BPF_FUNC_skb_load_bytes; 265 | static int (*bpf_skb_load_bytes_relative)(void *ctx, int off, void *to, int len, __u32 start_header) = 266 | (void *) BPF_FUNC_skb_load_bytes_relative; 267 | static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = 268 | (void *) BPF_FUNC_skb_store_bytes; 269 | static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = 270 | (void *) BPF_FUNC_l3_csum_replace; 271 | static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = 272 | (void *) BPF_FUNC_l4_csum_replace; 273 | static int (*bpf_csum_diff)(void *from, int from_size, void *to, int to_size, int seed) = 274 | (void *) BPF_FUNC_csum_diff; 275 | static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = 276 | (void *) BPF_FUNC_skb_under_cgroup; 277 | static int (*bpf_skb_change_head)(void *, int len, int flags) = 278 | (void *) BPF_FUNC_skb_change_head; 279 | static int (*bpf_skb_pull_data)(void *, int len) = 280 | (void *) BPF_FUNC_skb_pull_data; 281 | static unsigned int (*bpf_get_cgroup_classid)(void *ctx) = 282 | (void *) BPF_FUNC_get_cgroup_classid; 283 | static unsigned int (*bpf_get_route_realm)(void *ctx) = 284 | (void *) BPF_FUNC_get_route_realm; 285 | static int (*bpf_skb_change_proto)(void *ctx, __be16 proto, __u64 flags) = 286 | (void *) BPF_FUNC_skb_change_proto; 287 | static int (*bpf_skb_change_type)(void *ctx, __u32 type) = 288 | (void *) BPF_FUNC_skb_change_type; 289 | static unsigned int (*bpf_get_hash_recalc)(void *ctx) = 290 | (void *) BPF_FUNC_get_hash_recalc; 291 | static unsigned long long (*bpf_get_current_task)(void) = 292 | (void *) BPF_FUNC_get_current_task; 293 | static int (*bpf_skb_change_tail)(void *ctx, __u32 len, __u64 flags) = 294 | (void *) BPF_FUNC_skb_change_tail; 295 | static long long (*bpf_csum_update)(void *ctx, __u32 csum) = 296 | (void *) BPF_FUNC_csum_update; 297 | static void (*bpf_set_hash_invalid)(void *ctx) = 298 | (void *) BPF_FUNC_set_hash_invalid; 299 | static int (*bpf_get_numa_node_id)(void) = 300 | (void *) BPF_FUNC_get_numa_node_id; 301 | static int (*bpf_probe_read_str)(void *ctx, __u32 size, 302 | const void *unsafe_ptr) = 303 | (void *) BPF_FUNC_probe_read_str; 304 | static unsigned int (*bpf_get_socket_uid)(void *ctx) = 305 | (void *) BPF_FUNC_get_socket_uid; 306 | static unsigned int (*bpf_set_hash)(void *ctx, __u32 hash) = 307 | (void *) BPF_FUNC_set_hash; 308 | static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode, 309 | unsigned long long flags) = 310 | (void *) BPF_FUNC_skb_adjust_room; 311 | 312 | /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ 313 | #if defined(__TARGET_ARCH_x86) 314 | #define bpf_target_x86 315 | #define bpf_target_defined 316 | #elif defined(__TARGET_ARCH_s390) 317 | #define bpf_target_s390 318 | #define bpf_target_defined 319 | #elif defined(__TARGET_ARCH_arm) 320 | #define bpf_target_arm 321 | #define bpf_target_defined 322 | #elif defined(__TARGET_ARCH_arm64) 323 | #define bpf_target_arm64 324 | #define bpf_target_defined 325 | #elif defined(__TARGET_ARCH_mips) 326 | #define bpf_target_mips 327 | #define bpf_target_defined 328 | #elif defined(__TARGET_ARCH_powerpc) 329 | #define bpf_target_powerpc 330 | #define bpf_target_defined 331 | #elif defined(__TARGET_ARCH_sparc) 332 | #define bpf_target_sparc 333 | #define bpf_target_defined 334 | #else 335 | #undef bpf_target_defined 336 | #endif 337 | 338 | /* Fall back to what the compiler says */ 339 | #ifndef bpf_target_defined 340 | #if defined(__x86_64__) 341 | #define bpf_target_x86 342 | #elif defined(__s390__) 343 | #define bpf_target_s390 344 | #elif defined(__arm__) 345 | #define bpf_target_arm 346 | #elif defined(__aarch64__) 347 | #define bpf_target_arm64 348 | #elif defined(__mips__) 349 | #define bpf_target_mips 350 | #elif defined(__powerpc__) 351 | #define bpf_target_powerpc 352 | #elif defined(__sparc__) 353 | #define bpf_target_sparc 354 | #endif 355 | #endif 356 | 357 | #if defined(bpf_target_x86) 358 | 359 | #ifdef __KERNEL__ 360 | #define PT_REGS_PARM1(x) ((x)->di) 361 | #define PT_REGS_PARM2(x) ((x)->si) 362 | #define PT_REGS_PARM3(x) ((x)->dx) 363 | #define PT_REGS_PARM4(x) ((x)->cx) 364 | #define PT_REGS_PARM5(x) ((x)->r8) 365 | #define PT_REGS_RET(x) ((x)->sp) 366 | #define PT_REGS_FP(x) ((x)->bp) 367 | #define PT_REGS_RC(x) ((x)->ax) 368 | #define PT_REGS_SP(x) ((x)->sp) 369 | #define PT_REGS_IP(x) ((x)->ip) 370 | #else 371 | #ifdef __i386__ 372 | /* i386 kernel is built with -mregparm=3 */ 373 | #define PT_REGS_PARM1(x) ((x)->eax) 374 | #define PT_REGS_PARM2(x) ((x)->edx) 375 | #define PT_REGS_PARM3(x) ((x)->ecx) 376 | #define PT_REGS_PARM4(x) 0 377 | #define PT_REGS_PARM5(x) 0 378 | #define PT_REGS_RET(x) ((x)->esp) 379 | #define PT_REGS_FP(x) ((x)->ebp) 380 | #define PT_REGS_RC(x) ((x)->eax) 381 | #define PT_REGS_SP(x) ((x)->esp) 382 | #define PT_REGS_IP(x) ((x)->eip) 383 | #else 384 | #define PT_REGS_PARM1(x) ((x)->rdi) 385 | #define PT_REGS_PARM2(x) ((x)->rsi) 386 | #define PT_REGS_PARM3(x) ((x)->rdx) 387 | #define PT_REGS_PARM4(x) ((x)->rcx) 388 | #define PT_REGS_PARM5(x) ((x)->r8) 389 | #define PT_REGS_RET(x) ((x)->rsp) 390 | #define PT_REGS_FP(x) ((x)->rbp) 391 | #define PT_REGS_RC(x) ((x)->rax) 392 | #define PT_REGS_SP(x) ((x)->rsp) 393 | #define PT_REGS_IP(x) ((x)->rip) 394 | #endif 395 | #endif 396 | 397 | #elif defined(bpf_target_s390) 398 | 399 | /* s390 provides user_pt_regs instead of struct pt_regs to userspace */ 400 | struct pt_regs; 401 | #define PT_REGS_S390 const volatile user_pt_regs 402 | #define PT_REGS_PARM1(x) (((PT_REGS_S390 *)(x))->gprs[2]) 403 | #define PT_REGS_PARM2(x) (((PT_REGS_S390 *)(x))->gprs[3]) 404 | #define PT_REGS_PARM3(x) (((PT_REGS_S390 *)(x))->gprs[4]) 405 | #define PT_REGS_PARM4(x) (((PT_REGS_S390 *)(x))->gprs[5]) 406 | #define PT_REGS_PARM5(x) (((PT_REGS_S390 *)(x))->gprs[6]) 407 | #define PT_REGS_RET(x) (((PT_REGS_S390 *)(x))->gprs[14]) 408 | /* Works only with CONFIG_FRAME_POINTER */ 409 | #define PT_REGS_FP(x) (((PT_REGS_S390 *)(x))->gprs[11]) 410 | #define PT_REGS_RC(x) (((PT_REGS_S390 *)(x))->gprs[2]) 411 | #define PT_REGS_SP(x) (((PT_REGS_S390 *)(x))->gprs[15]) 412 | #define PT_REGS_IP(x) (((PT_REGS_S390 *)(x))->psw.addr) 413 | 414 | #elif defined(bpf_target_arm) 415 | 416 | #define PT_REGS_PARM1(x) ((x)->uregs[0]) 417 | #define PT_REGS_PARM2(x) ((x)->uregs[1]) 418 | #define PT_REGS_PARM3(x) ((x)->uregs[2]) 419 | #define PT_REGS_PARM4(x) ((x)->uregs[3]) 420 | #define PT_REGS_PARM5(x) ((x)->uregs[4]) 421 | #define PT_REGS_RET(x) ((x)->uregs[14]) 422 | #define PT_REGS_FP(x) ((x)->uregs[11]) /* Works only with CONFIG_FRAME_POINTER */ 423 | #define PT_REGS_RC(x) ((x)->uregs[0]) 424 | #define PT_REGS_SP(x) ((x)->uregs[13]) 425 | #define PT_REGS_IP(x) ((x)->uregs[12]) 426 | 427 | #elif defined(bpf_target_arm64) 428 | 429 | /* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */ 430 | struct pt_regs; 431 | #define PT_REGS_ARM64 const volatile struct user_pt_regs 432 | #define PT_REGS_PARM1(x) (((PT_REGS_ARM64 *)(x))->regs[0]) 433 | #define PT_REGS_PARM2(x) (((PT_REGS_ARM64 *)(x))->regs[1]) 434 | #define PT_REGS_PARM3(x) (((PT_REGS_ARM64 *)(x))->regs[2]) 435 | #define PT_REGS_PARM4(x) (((PT_REGS_ARM64 *)(x))->regs[3]) 436 | #define PT_REGS_PARM5(x) (((PT_REGS_ARM64 *)(x))->regs[4]) 437 | #define PT_REGS_RET(x) (((PT_REGS_ARM64 *)(x))->regs[30]) 438 | /* Works only with CONFIG_FRAME_POINTER */ 439 | #define PT_REGS_FP(x) (((PT_REGS_ARM64 *)(x))->regs[29]) 440 | #define PT_REGS_RC(x) (((PT_REGS_ARM64 *)(x))->regs[0]) 441 | #define PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp) 442 | #define PT_REGS_IP(x) (((PT_REGS_ARM64 *)(x))->pc) 443 | 444 | #elif defined(bpf_target_mips) 445 | 446 | #define PT_REGS_PARM1(x) ((x)->regs[4]) 447 | #define PT_REGS_PARM2(x) ((x)->regs[5]) 448 | #define PT_REGS_PARM3(x) ((x)->regs[6]) 449 | #define PT_REGS_PARM4(x) ((x)->regs[7]) 450 | #define PT_REGS_PARM5(x) ((x)->regs[8]) 451 | #define PT_REGS_RET(x) ((x)->regs[31]) 452 | #define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */ 453 | #define PT_REGS_RC(x) ((x)->regs[1]) 454 | #define PT_REGS_SP(x) ((x)->regs[29]) 455 | #define PT_REGS_IP(x) ((x)->cp0_epc) 456 | 457 | #elif defined(bpf_target_powerpc) 458 | 459 | #define PT_REGS_PARM1(x) ((x)->gpr[3]) 460 | #define PT_REGS_PARM2(x) ((x)->gpr[4]) 461 | #define PT_REGS_PARM3(x) ((x)->gpr[5]) 462 | #define PT_REGS_PARM4(x) ((x)->gpr[6]) 463 | #define PT_REGS_PARM5(x) ((x)->gpr[7]) 464 | #define PT_REGS_RC(x) ((x)->gpr[3]) 465 | #define PT_REGS_SP(x) ((x)->sp) 466 | #define PT_REGS_IP(x) ((x)->nip) 467 | 468 | #elif defined(bpf_target_sparc) 469 | 470 | #define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0]) 471 | #define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1]) 472 | #define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2]) 473 | #define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3]) 474 | #define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4]) 475 | #define PT_REGS_RET(x) ((x)->u_regs[UREG_I7]) 476 | #define PT_REGS_RC(x) ((x)->u_regs[UREG_I0]) 477 | #define PT_REGS_SP(x) ((x)->u_regs[UREG_FP]) 478 | 479 | /* Should this also be a bpf_target check for the sparc case? */ 480 | #if defined(__arch64__) 481 | #define PT_REGS_IP(x) ((x)->tpc) 482 | #else 483 | #define PT_REGS_IP(x) ((x)->pc) 484 | #endif 485 | 486 | #endif 487 | 488 | #if defined(bpf_target_powerpc) 489 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) 490 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 491 | #elif defined(bpf_target_sparc) 492 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) 493 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 494 | #else 495 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \ 496 | bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) 497 | #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \ 498 | bpf_probe_read(&(ip), sizeof(ip), \ 499 | (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) 500 | #endif 501 | 502 | /* 503 | * BPF_CORE_READ abstracts away bpf_probe_read() call and captures offset 504 | * relocation for source address using __builtin_preserve_access_index() 505 | * built-in, provided by Clang. 506 | * 507 | * __builtin_preserve_access_index() takes as an argument an expression of 508 | * taking an address of a field within struct/union. It makes compiler emit 509 | * a relocation, which records BTF type ID describing root struct/union and an 510 | * accessor string which describes exact embedded field that was used to take 511 | * an address. See detailed description of this relocation format and 512 | * semantics in comments to struct bpf_offset_reloc in libbpf_internal.h. 513 | * 514 | * This relocation allows libbpf to adjust BPF instruction to use correct 515 | * actual field offset, based on target kernel BTF type that matches original 516 | * (local) BTF, used to record relocation. 517 | */ 518 | #define BPF_CORE_READ(dst, src) \ 519 | bpf_probe_read((dst), sizeof(*(src)), \ 520 | __builtin_preserve_access_index(src)) 521 | 522 | #endif 523 | -------------------------------------------------------------------------------- /ebpf/bpf/bpf_map.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* Copyright (c) 2020 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of version 2 of the GNU General Public 6 | * License as published by the Free Software Foundation. 7 | */ 8 | #define BUF_SIZE_MAP_NS 256 9 | 10 | typedef struct bpf_map_def { 11 | unsigned int type; 12 | unsigned int key_size; 13 | unsigned int value_size; 14 | unsigned int max_entries; 15 | unsigned int map_flags; 16 | unsigned int inner_map_idx; 17 | unsigned int pinning; 18 | char namespace[BUF_SIZE_MAP_NS]; 19 | } bpf_map_def; 20 | 21 | enum bpf_pin_type { 22 | PIN_NONE = 0, 23 | PIN_OBJECT_NS, 24 | PIN_GLOBAL_NS, 25 | PIN_CUSTOM_NS, 26 | }; 27 | -------------------------------------------------------------------------------- /ebpf/main.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* Copyright (c) 2020 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of version 2 of the GNU General Public 6 | * License as published by the Free Software Foundation. 7 | */ 8 | #pragma clang diagnostic push 9 | #pragma clang diagnostic ignored "-Waddress-of-packed-member" 10 | #pragma clang diagnostic ignored "-Warray-bounds" 11 | #pragma clang diagnostic ignored "-Wunused-label" 12 | #pragma clang diagnostic ignored "-Wgnu-variable-sized-type-not-at-end" 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | /* In Linux 5.4 asm_inline was introduced, but it's not supported by clang. 19 | * Redefine it to just asm to enable successful compilation. 20 | */ 21 | #ifdef asm_inline 22 | #undef asm_inline 23 | #define asm_inline asm 24 | #endif 25 | /* Before bpf_helpers.h is included, uapi bpf.h has been 26 | * included, which references linux/types.h. This may bring 27 | * in asm_volatile_goto definition if permitted based on 28 | * compiler setup and kernel configs. 29 | * 30 | * clang does not support "asm volatile goto" yet. 31 | * So redefine asm_volatile_goto to some invalid asm code. 32 | * If asm_volatile_goto is actually used by the bpf program, 33 | * a compilation error will appear. 34 | */ 35 | #ifdef asm_volatile_goto 36 | #undef asm_volatile_goto 37 | #endif 38 | #define asm_volatile_goto(x...) asm volatile("invalid use of asm_volatile_goto") 39 | #pragma clang diagnostic pop 40 | 41 | // Custom eBPF helpers 42 | #include "bpf/bpf.h" 43 | #include "bpf/bpf_map.h" 44 | #include "bpf/bpf_helpers.h" 45 | 46 | // utrace probes 47 | #include "utrace/defs.h" 48 | #include "utrace/utrace.h" 49 | #include "utrace/exec.h" 50 | 51 | char _license[] SEC("license") = "GPL"; 52 | __u32 _version SEC("version") = 0xFFFFFFFE; 53 | -------------------------------------------------------------------------------- /ebpf/utrace/defs.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* Copyright (c) 2020 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of version 2 of the GNU General Public 6 | * License as published by the Free Software Foundation. 7 | */ 8 | #ifndef _DEFS_H_ 9 | #define _DEFS_H_ 10 | 11 | #define LOAD_CONSTANT(param, var) asm("%0 = " param " ll" : "=r"(var)) 12 | 13 | __attribute__((always_inline)) static u64 load_func_id() { 14 | u64 func_id = 0; 15 | LOAD_CONSTANT("func_id", func_id); 16 | return func_id; 17 | } 18 | 19 | __attribute__((always_inline)) static u64 load_send_stack_trace() { 20 | u64 send_stack_trace = 0; 21 | LOAD_CONSTANT("send_stack_trace", send_stack_trace); 22 | return send_stack_trace; 23 | } 24 | 25 | __attribute__((always_inline)) static u64 load_filter_user_binary() { 26 | u64 filter_user_binary = 0; 27 | LOAD_CONSTANT("filter_user_binary", filter_user_binary); 28 | return filter_user_binary; 29 | } 30 | 31 | struct counter_t { 32 | u64 time; 33 | u64 count; 34 | }; 35 | 36 | struct bpf_map_def SEC("maps/counters") counters = { 37 | .type = BPF_MAP_TYPE_PERCPU_ARRAY, 38 | .key_size = sizeof(u32), 39 | .value_size = sizeof(struct counter_t), 40 | .max_entries = 1, 41 | .pinning = 0, 42 | .namespace = "", 43 | }; 44 | 45 | struct start_ts_key_t { 46 | u64 func_id; 47 | u64 pid; 48 | }; 49 | 50 | struct bpf_map_def SEC("maps/start_ts") start_ts = { 51 | .type = BPF_MAP_TYPE_LRU_HASH, 52 | .key_size = sizeof(struct start_ts_key_t), 53 | .value_size = sizeof(u64), 54 | .max_entries = 4096, 55 | .pinning = 0, 56 | .namespace = "", 57 | }; 58 | 59 | #define USER_STACK_TRACE 0 60 | #define KERNEL_STACK_TRACE 1 61 | 62 | struct bpf_map_def SEC("maps/lost_traces") lost_traces = { 63 | .type = BPF_MAP_TYPE_ARRAY, 64 | .key_size = sizeof(u32), 65 | .value_size = sizeof(u64), 66 | .max_entries = 2, 67 | .pinning = 0, 68 | .namespace = "", 69 | }; 70 | 71 | struct bpf_map_def SEC("maps/stack_traces") stack_traces = { 72 | .type = BPF_MAP_TYPE_STACK_TRACE, 73 | .key_size = sizeof(u32), 74 | .value_size = PERF_MAX_STACK_DEPTH * sizeof(u64), 75 | .max_entries = 10240, 76 | // .map_flags = BPF_F_STACK_BUILD_ID, 77 | .pinning = 0, 78 | .namespace = "", 79 | }; 80 | 81 | struct trace_event_t { 82 | u64 pid; 83 | u32 user_stack_id; 84 | u32 kernel_stack_id; 85 | u32 func_id; 86 | u32 cookie; 87 | }; 88 | 89 | struct bpf_map_def SEC("maps/trace_events") trace_events = { 90 | .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, 91 | .key_size = 0, 92 | .value_size = 0, 93 | .max_entries = 0, 94 | .pinning = PIN_NONE, 95 | .namespace = "", 96 | }; 97 | 98 | #define PATH_MAX_LEN 350 99 | 100 | struct path_t 101 | { 102 | char filename[PATH_MAX_LEN]; 103 | }; 104 | 105 | struct bpf_map_def SEC("maps/binary_path") binary_path = { 106 | .type = BPF_MAP_TYPE_LRU_HASH, 107 | .key_size = sizeof(struct path_t), 108 | .value_size = sizeof(u32), 109 | .max_entries = 512, 110 | .pinning = 0, 111 | .namespace = "", 112 | }; 113 | 114 | struct bpf_map_def SEC("maps/traced_pids") traced_pids = { 115 | .type = BPF_MAP_TYPE_LRU_HASH, 116 | .key_size = sizeof(u32), 117 | .value_size = sizeof(u32), 118 | .max_entries = 4096, 119 | .pinning = 0, 120 | .namespace = "", 121 | }; 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /ebpf/utrace/exec.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* Copyright (c) 2020 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of version 2 of the GNU General Public 6 | * License as published by the Free Software Foundation. 7 | */ 8 | #ifndef _EXECVE_H_ 9 | #define _EXECVE_H_ 10 | 11 | struct sched_process_exec_args 12 | { 13 | unsigned short common_type; 14 | unsigned char common_flags; 15 | unsigned char common_preempt_count; 16 | int common_pid; 17 | 18 | int data_loc_filename; 19 | pid_t pid; 20 | pid_t old_pid; 21 | }; 22 | 23 | SEC("tracepoint/sched/sched_process_exec") 24 | int tracepoint_sched_sched_process_exec(struct sched_process_exec_args *ctx) 25 | { 26 | unsigned short __offset = ctx->data_loc_filename & 0xFFFF; 27 | char *filename = (char *)ctx + __offset; 28 | struct path_t path = {}; 29 | bpf_probe_read_str(&path.filename, PATH_MAX_LEN, filename); 30 | 31 | u32 *cookie = bpf_map_lookup_elem(&binary_path, &path.filename); 32 | if (cookie == NULL) { 33 | return 0; 34 | } 35 | 36 | // insert pid in list of traced pids 37 | u32 new_cookie = *cookie; 38 | u32 pid = bpf_get_current_pid_tgid() >> 32; 39 | bpf_map_update_elem(&traced_pids, &pid, &new_cookie, BPF_ANY); 40 | return 0; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /ebpf/utrace/utrace.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* Copyright (c) 2020 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of version 2 of the GNU General Public 6 | * License as published by the Free Software Foundation. 7 | */ 8 | #ifndef _UTRACE_H_ 9 | #define _UTRACE_H_ 10 | 11 | SEC("uprobe/utrace") 12 | int uprobe_utrace(void *ctx) 13 | { 14 | u64 now = bpf_ktime_get_ns(); 15 | u64 pid_tgid = bpf_get_current_pid_tgid(); 16 | u32 pid = pid_tgid >> 32; 17 | u32 cookie = 0; 18 | 19 | // fetch pid cookie 20 | u32 *traced = bpf_map_lookup_elem(&traced_pids, &pid); 21 | if (traced == NULL) { 22 | return 0; 23 | } 24 | cookie = *traced; 25 | 26 | // hits counter 27 | u32 func_id = load_func_id(); 28 | struct counter_t *counter = bpf_map_lookup_elem(&counters, &func_id); 29 | if (counter == NULL) { 30 | // should never happen, the list of traced functions is known 31 | return 0; 32 | } 33 | __sync_fetch_and_add(&counter->count, 1); 34 | 35 | // store entry timestamp 36 | struct start_ts_key_t key = { 37 | .func_id = func_id, 38 | .pid = bpf_get_current_pid_tgid(), 39 | }; 40 | bpf_map_update_elem(&start_ts, &key, &now, BPF_ANY); 41 | 42 | // fetch stack trace 43 | u32 send_stack_trace = load_send_stack_trace(); 44 | if (send_stack_trace) { 45 | struct trace_event_t evt = {}; 46 | evt.pid = key.pid; 47 | evt.func_id = key.func_id; 48 | evt.cookie = cookie; 49 | evt.user_stack_id = bpf_get_stackid(ctx, &stack_traces, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK | BPF_F_REUSE_STACKID); 50 | 51 | u32 cpu = bpf_get_smp_processor_id(); 52 | int ret = bpf_perf_event_output(ctx, &trace_events, cpu, &evt, sizeof(evt)); 53 | if (ret != 0) { 54 | u32 lost_key = USER_STACK_TRACE; 55 | u64 *lost = bpf_map_lookup_elem(&lost_traces, &lost_key); 56 | if (lost != NULL) { 57 | __sync_fetch_and_add(lost, 1); 58 | } 59 | } 60 | } 61 | return 0; 62 | }; 63 | 64 | SEC("uretprobe/utrace") 65 | int uretprobe_utrace(void *ctx) 66 | { 67 | u64 now = bpf_ktime_get_ns(); 68 | 69 | // hits counter 70 | u32 func_id = load_func_id(); 71 | struct counter_t *counter = bpf_map_lookup_elem(&counters, &func_id); 72 | if (counter == NULL) { 73 | // should never happen, the list of traced functions is known 74 | return 0; 75 | } 76 | 77 | // fetch start ts and compute latency 78 | struct start_ts_key_t key = { 79 | .func_id = load_func_id(), 80 | .pid = bpf_get_current_pid_tgid(), 81 | }; 82 | u64 *ts = bpf_map_lookup_elem(&start_ts, &key); 83 | if (ts == NULL) { 84 | return 0; 85 | } 86 | __sync_fetch_and_add(&counter->time, now - *ts); 87 | 88 | return 0; 89 | }; 90 | 91 | SEC("kprobe/utrace") 92 | int kprobe_utrace(void *ctx) 93 | { 94 | u64 now = bpf_ktime_get_ns(); 95 | u64 pid_tgid = bpf_get_current_pid_tgid(); 96 | u32 pid = pid_tgid >> 32; 97 | u32 cookie = 0; 98 | 99 | // check if a filter should be applied on the process that made the call 100 | u32 filter_user_binary = load_filter_user_binary(); 101 | if (filter_user_binary) { 102 | // check if this pid is traced 103 | u32 *traced = bpf_map_lookup_elem(&traced_pids, &pid); 104 | if (traced == NULL) { 105 | return 0; 106 | } 107 | cookie = *traced; 108 | } 109 | 110 | // hits counter 111 | u32 func_id = load_func_id(); 112 | struct counter_t *counter = bpf_map_lookup_elem(&counters, &func_id); 113 | if (counter == NULL) { 114 | // should never happen, the list of traced functions is known 115 | return 0; 116 | } 117 | __sync_fetch_and_add(&counter->count, 1); 118 | 119 | // store entry timestamp 120 | struct start_ts_key_t key = { 121 | .func_id = func_id, 122 | .pid = pid_tgid, 123 | }; 124 | bpf_map_update_elem(&start_ts, &key, &now, BPF_ANY); 125 | 126 | // fetch stack trace 127 | u32 send_stack_trace = load_send_stack_trace(); 128 | if (send_stack_trace) { 129 | struct trace_event_t evt = {}; 130 | evt.pid = key.pid; 131 | evt.func_id = key.func_id; 132 | evt.cookie = cookie; 133 | evt.kernel_stack_id = bpf_get_stackid(ctx, &stack_traces, 0 | BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID); 134 | 135 | if (filter_user_binary) { 136 | evt.user_stack_id = bpf_get_stackid(ctx, &stack_traces, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK | BPF_F_REUSE_STACKID); 137 | } 138 | 139 | u32 cpu = bpf_get_smp_processor_id(); 140 | int ret = bpf_perf_event_output(ctx, &trace_events, cpu, &evt, sizeof(evt)); 141 | if (ret != 0) { 142 | u32 lost_key = KERNEL_STACK_TRACE; 143 | u64 *lost = bpf_map_lookup_elem(&lost_traces, &lost_key); 144 | if (lost != NULL) { 145 | __sync_fetch_and_add(lost, 1); 146 | } 147 | if (filter_user_binary) { 148 | lost_key = USER_STACK_TRACE; 149 | lost = bpf_map_lookup_elem(&lost_traces, &lost_key); 150 | if (lost != NULL) { 151 | __sync_fetch_and_add(lost, 1); 152 | } 153 | } 154 | } 155 | } 156 | return 0; 157 | }; 158 | 159 | SEC("kretprobe/utrace") 160 | int kretprobe_utrace(void *ctx) 161 | { 162 | u64 now = bpf_ktime_get_ns(); 163 | u64 pid_tgid = bpf_get_current_pid_tgid(); 164 | u32 pid = pid_tgid >> 32; 165 | 166 | // check if a filter should be applied on the process that made the call 167 | u32 filter_user_binary = load_filter_user_binary(); 168 | if (filter_user_binary) { 169 | // check if this pid is traced 170 | u32 *traced = bpf_map_lookup_elem(&traced_pids, &pid); 171 | if (traced == NULL) { 172 | return 0; 173 | } 174 | } 175 | 176 | // update latency 177 | u32 func_id = load_func_id(); 178 | struct counter_t *counter = bpf_map_lookup_elem(&counters, &func_id); 179 | if (counter == NULL) { 180 | // should never happen, the list of traced functions is known 181 | return 0; 182 | } 183 | 184 | // fetch start ts and compute latency 185 | struct start_ts_key_t key = { 186 | .func_id = load_func_id(), 187 | .pid = pid_tgid, 188 | }; 189 | u64 *ts = bpf_map_lookup_elem(&start_ts, &key); 190 | if (ts == NULL) { 191 | return 0; 192 | } 193 | __sync_fetch_and_add(&counter->time, now - *ts); 194 | 195 | return 0; 196 | }; 197 | 198 | SEC("tracepoint/utrace") 199 | int tracepoint_utrace(void *ctx) 200 | { 201 | u64 now = bpf_ktime_get_ns(); 202 | u64 pid_tgid = bpf_get_current_pid_tgid(); 203 | u32 pid = pid_tgid >> 32; 204 | u32 cookie = 0; 205 | 206 | // check if a filter should be applied on the process that made the call 207 | u32 filter_user_binary = load_filter_user_binary(); 208 | if (filter_user_binary) { 209 | // check if this pid is traced 210 | u32 *traced = bpf_map_lookup_elem(&traced_pids, &pid); 211 | if (traced == NULL) { 212 | return 0; 213 | } 214 | cookie = *traced; 215 | } 216 | 217 | // hits counter 218 | u32 func_id = load_func_id(); 219 | struct counter_t *counter = bpf_map_lookup_elem(&counters, &func_id); 220 | if (counter == NULL) { 221 | // should never happen, the list of traced functions is known 222 | return 0; 223 | } 224 | __sync_fetch_and_add(&counter->count, 1); 225 | 226 | // store entry timestamp 227 | struct start_ts_key_t key = { 228 | .func_id = func_id, 229 | .pid = pid_tgid, 230 | }; 231 | bpf_map_update_elem(&start_ts, &key, &now, BPF_ANY); 232 | 233 | // fetch stack trace 234 | u32 send_stack_trace = load_send_stack_trace(); 235 | if (send_stack_trace) { 236 | struct trace_event_t evt = {}; 237 | evt.pid = key.pid; 238 | evt.func_id = key.func_id; 239 | evt.cookie = cookie; 240 | evt.kernel_stack_id = bpf_get_stackid(ctx, &stack_traces, 0 | BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID); 241 | 242 | if (filter_user_binary) { 243 | evt.user_stack_id = bpf_get_stackid(ctx, &stack_traces, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK | BPF_F_REUSE_STACKID); 244 | } 245 | 246 | u32 cpu = bpf_get_smp_processor_id(); 247 | int ret = bpf_perf_event_output(ctx, &trace_events, cpu, &evt, sizeof(evt)); 248 | if (ret != 0) { 249 | u32 lost_key = KERNEL_STACK_TRACE; 250 | u64 *lost = bpf_map_lookup_elem(&lost_traces, &lost_key); 251 | if (lost != NULL) { 252 | __sync_fetch_and_add(lost, 1); 253 | } 254 | if (filter_user_binary) { 255 | lost_key = USER_STACK_TRACE; 256 | lost = bpf_map_lookup_elem(&lost_traces, &lost_key); 257 | if (lost != NULL) { 258 | __sync_fetch_and_add(lost, 1); 259 | } 260 | } 261 | } 262 | } 263 | return 0; 264 | }; 265 | 266 | SEC("perf_event/utrace") 267 | int perf_event_utrace(void *ctx) 268 | { 269 | u64 now = bpf_ktime_get_ns(); 270 | u64 pid_tgid = bpf_get_current_pid_tgid(); 271 | u32 pid = pid_tgid >> 32; 272 | u32 cookie = 0; 273 | 274 | // check if a filter should be applied on the process that made the call 275 | u32 filter_user_binary = load_filter_user_binary(); 276 | if (filter_user_binary) { 277 | // check if this pid is traced 278 | u32 *traced = bpf_map_lookup_elem(&traced_pids, &pid); 279 | if (traced == NULL) { 280 | return 0; 281 | } 282 | cookie = *traced; 283 | } 284 | 285 | // hits counter 286 | u32 func_id = load_func_id(); 287 | struct counter_t *counter = bpf_map_lookup_elem(&counters, &func_id); 288 | if (counter == NULL) { 289 | // should never happen, the list of traced functions is known 290 | return 0; 291 | } 292 | __sync_fetch_and_add(&counter->count, 1); 293 | 294 | // store entry timestamp 295 | struct start_ts_key_t key = { 296 | .func_id = func_id, 297 | .pid = pid_tgid, 298 | }; 299 | bpf_map_update_elem(&start_ts, &key, &now, BPF_ANY); 300 | 301 | // fetch stack trace 302 | u32 send_stack_trace = load_send_stack_trace(); 303 | if (send_stack_trace) { 304 | struct trace_event_t evt = {}; 305 | evt.pid = key.pid; 306 | evt.func_id = key.func_id; 307 | evt.cookie = cookie; 308 | evt.kernel_stack_id = bpf_get_stackid(ctx, &stack_traces, 0 | BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID); 309 | 310 | if (filter_user_binary) { 311 | evt.user_stack_id = bpf_get_stackid(ctx, &stack_traces, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK | BPF_F_REUSE_STACKID); 312 | } 313 | 314 | u32 cpu = bpf_get_smp_processor_id(); 315 | int ret = bpf_perf_event_output(ctx, &trace_events, cpu, &evt, sizeof(evt)); 316 | if (ret != 0) { 317 | u32 lost_key = KERNEL_STACK_TRACE; 318 | u64 *lost = bpf_map_lookup_elem(&lost_traces, &lost_key); 319 | if (lost != NULL) { 320 | __sync_fetch_and_add(lost, 1); 321 | } 322 | if (filter_user_binary) { 323 | lost_key = USER_STACK_TRACE; 324 | lost = bpf_map_lookup_elem(&lost_traces, &lost_key); 325 | if (lost != NULL) { 326 | __sync_fetch_and_add(lost, 1); 327 | } 328 | } 329 | } 330 | } 331 | return 0; 332 | }; 333 | 334 | #endif 335 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Gui774ume/utrace 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/DataDog/ebpf-manager v0.0.0-20220113113843-c3e09dfcb60c 7 | github.com/cilium/ebpf v0.7.1-0.20211227144435-70d770f1e5f9 8 | github.com/pkg/errors v0.9.1 9 | github.com/shuLhan/go-bindata v4.0.0+incompatible 10 | github.com/sirupsen/logrus v1.8.1 11 | github.com/spf13/cobra v1.1.1 12 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 13 | golang.org/x/sys v0.0.0-20210921065528-437939a70204 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 15 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 16 | github.com/DataDog/ebpf-manager v0.0.0-20220111092842-0c445a76d299 h1:KIbjlZP//7FgPR1x/mmUnJpwVXaHkOSPXXC3ws21Rd8= 17 | github.com/DataDog/ebpf-manager v0.0.0-20220111092842-0c445a76d299/go.mod h1:q6FEcnn+mlqeRyd+nNN/xuoHfVKVsZav5k3xEIQxkpI= 18 | github.com/DataDog/ebpf-manager v0.0.0-20220112215545-53ccf6db5fc5 h1:TcMuL1efB6EYeDB5UCOaTe1ixLXrH1VdNpWqrh16b+c= 19 | github.com/DataDog/ebpf-manager v0.0.0-20220112215545-53ccf6db5fc5/go.mod h1:q6FEcnn+mlqeRyd+nNN/xuoHfVKVsZav5k3xEIQxkpI= 20 | github.com/DataDog/ebpf-manager v0.0.0-20220113113843-c3e09dfcb60c h1:CCmCq3xAb0nrkDsJcmRSswY1ISwlsRhs3aq8cTcrHsY= 21 | github.com/DataDog/ebpf-manager v0.0.0-20220113113843-c3e09dfcb60c/go.mod h1:q6FEcnn+mlqeRyd+nNN/xuoHfVKVsZav5k3xEIQxkpI= 22 | github.com/DataDog/gopsutil v0.0.0-20200624212600-1b53412ef321 h1:OPAXA+r6yznoxWR5jQ2iTh5CvzIMrdw8AU0uFN2RwEw= 23 | github.com/DataDog/gopsutil v0.0.0-20200624212600-1b53412ef321/go.mod h1:tGQp6XG4XpOyy67WG/YWXVxzOY6LejK35e8KcQhtRIQ= 24 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 25 | github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 h1:UUppSQnhf4Yc6xGxSkoQpPhb7RVzuv5Nb1mwJ5VId9s= 26 | github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 27 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 28 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 29 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 30 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 31 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 32 | github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= 33 | github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= 34 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 35 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 36 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 37 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 38 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 39 | github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= 40 | github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= 41 | github.com/cilium/ebpf v0.7.1-0.20211227144435-70d770f1e5f9 h1:WJJWEj2ETcJwnTZWHaB2tzWb9nTteHfFg1bX9l4t7j4= 42 | github.com/cilium/ebpf v0.7.1-0.20211227144435-70d770f1e5f9/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk= 43 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 44 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 45 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 46 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 47 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 48 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 49 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 50 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 51 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 52 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 53 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 54 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 55 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 56 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 57 | github.com/florianl/go-tc v0.3.0 h1:qeqQB5kp2lwJP1p/8krLQIuRfkHWpiPPcYr3rhRSaC8= 58 | github.com/florianl/go-tc v0.3.0/go.mod h1:Ni/GTSK8ymDnsRQfL2meJeGmcXy7RFIvchiVHizU76U= 59 | github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= 60 | github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= 61 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 62 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 63 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 64 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 65 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 66 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 67 | github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= 68 | github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= 69 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 70 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 71 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 72 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 73 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 74 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 75 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 76 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 77 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 78 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 79 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 80 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 81 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 82 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 83 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 84 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 85 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 86 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 87 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 88 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 89 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 90 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 91 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 92 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 93 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 94 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 95 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 96 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 97 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 98 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 99 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 100 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 101 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 102 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 103 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 104 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 105 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 106 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 107 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 108 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 109 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 110 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 111 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 112 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 113 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 114 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 115 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 116 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 117 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 118 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 119 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 120 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 121 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 122 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 123 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 124 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 125 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 126 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 127 | github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= 128 | github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 129 | github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= 130 | github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= 131 | github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= 132 | github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= 133 | github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= 134 | github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= 135 | github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do= 136 | github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= 137 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 138 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 139 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 140 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 141 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 142 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 143 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 144 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 145 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 146 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 147 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 148 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 149 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 150 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 151 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 152 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 153 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 154 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 155 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 156 | github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY= 157 | github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= 158 | github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0= 159 | github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= 160 | github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= 161 | github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= 162 | github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= 163 | github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= 164 | github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= 165 | github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= 166 | github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= 167 | github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= 168 | github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0= 169 | github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= 170 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 171 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 172 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 173 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 174 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 175 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 176 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 177 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 178 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 179 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 180 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 181 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 182 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 183 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 184 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 185 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 186 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 187 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 188 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 189 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 190 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 191 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 192 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 193 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 194 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 195 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 196 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 197 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 198 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 199 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 200 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 201 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 202 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 203 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= 204 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 205 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 206 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 207 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 208 | github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= 209 | github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= 210 | github.com/shuLhan/go-bindata v4.0.0+incompatible h1:xD8LkuVZLV5OOn/IEuFdt6EEAW7deWiqgwaaSGhjAJc= 211 | github.com/shuLhan/go-bindata v4.0.0+incompatible/go.mod h1:pkcPAATLBDD2+SpAPnX5vEM90F7fcwHCvvLCMXcmw3g= 212 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 213 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 214 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 215 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 216 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 217 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 218 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 219 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 220 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 221 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 222 | github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= 223 | github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= 224 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 225 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 226 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 227 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 228 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 229 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 230 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 231 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 232 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 233 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 234 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 235 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 236 | github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= 237 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 238 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= 239 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 240 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 241 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 242 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 243 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 244 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 245 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 246 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 247 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 248 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 249 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 250 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 251 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 252 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 253 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 254 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 255 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 256 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 257 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 258 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 259 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 260 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 261 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 262 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 263 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 264 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 265 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 266 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 267 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 268 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 269 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 270 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 271 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 272 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 273 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 274 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 275 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 276 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 277 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 278 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 279 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 280 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 281 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 282 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 283 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 284 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 285 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 286 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 287 | golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 288 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 289 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 290 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 291 | golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 292 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 293 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= 294 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 295 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 296 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 297 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 298 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 299 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 300 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 301 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 302 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 303 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 304 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 305 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 306 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 307 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 308 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 309 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 310 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 311 | golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 312 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 313 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 314 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 315 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 316 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 317 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 318 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 319 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 321 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 322 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 323 | golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 324 | golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 325 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 326 | golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 327 | golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 328 | golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 329 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 330 | golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 331 | golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 332 | golang.org/x/sys v0.0.0-20210921065528-437939a70204 h1:JJhkWtBuTQKyz2bd5WG9H8iUsJRU3En/KRfN8B2RnDs= 333 | golang.org/x/sys v0.0.0-20210921065528-437939a70204/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 334 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 335 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 336 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 337 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 338 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 339 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 340 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 341 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 342 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 343 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 344 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 345 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 346 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 347 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 348 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 349 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 350 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 351 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 352 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 353 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 354 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 355 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 356 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 357 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 358 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 359 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 360 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 361 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 362 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 363 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 364 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 365 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 366 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 367 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 368 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 369 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 370 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 371 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 372 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 373 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 374 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 375 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 376 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 377 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 378 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 379 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 380 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 381 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 382 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 383 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 384 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 385 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 386 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 387 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 388 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 389 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 390 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 391 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 392 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 393 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 394 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 395 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 396 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 397 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 398 | -------------------------------------------------------------------------------- /pkg/assets/probe.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. DO NOT EDIT. 2 | // sources: 3 | // ebpf/bin/probe.o 4 | 5 | package assets 6 | 7 | 8 | import ( 9 | "bytes" 10 | "compress/gzip" 11 | "fmt" 12 | "io" 13 | "io/ioutil" 14 | "os" 15 | "path/filepath" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | func bindataRead(data []byte, name string) ([]byte, error) { 21 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 22 | if err != nil { 23 | return nil, fmt.Errorf("Read %q: %v", name, err) 24 | } 25 | 26 | var buf bytes.Buffer 27 | _, err = io.Copy(&buf, gz) 28 | clErr := gz.Close() 29 | 30 | if err != nil { 31 | return nil, fmt.Errorf("Read %q: %v", name, err) 32 | } 33 | if clErr != nil { 34 | return nil, err 35 | } 36 | 37 | return buf.Bytes(), nil 38 | } 39 | 40 | 41 | type asset struct { 42 | bytes []byte 43 | info fileInfoEx 44 | } 45 | 46 | type fileInfoEx interface { 47 | os.FileInfo 48 | MD5Checksum() string 49 | } 50 | 51 | type bindataFileInfo struct { 52 | name string 53 | size int64 54 | mode os.FileMode 55 | modTime time.Time 56 | md5checksum string 57 | } 58 | 59 | func (fi bindataFileInfo) Name() string { 60 | return fi.name 61 | } 62 | func (fi bindataFileInfo) Size() int64 { 63 | return fi.size 64 | } 65 | func (fi bindataFileInfo) Mode() os.FileMode { 66 | return fi.mode 67 | } 68 | func (fi bindataFileInfo) ModTime() time.Time { 69 | return fi.modTime 70 | } 71 | func (fi bindataFileInfo) MD5Checksum() string { 72 | return fi.md5checksum 73 | } 74 | func (fi bindataFileInfo) IsDir() bool { 75 | return false 76 | } 77 | func (fi bindataFileInfo) Sys() interface{} { 78 | return nil 79 | } 80 | 81 | var _bindataProbeO = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x59\x5d\x4c\x14\x57\x14\x3e\xb3\x08\x2c\x20\x05\xc5\x55\xa4\xa8\x68\xb4\xb5\xa4\xe5\xc7\xbf\x92\x9a\x46\x62\xfa\xc3\x03\x36\xb4\x31\x29\x4d\x8c\xe3\x32\x0c\xee\x0a\x2c\xdb\x99\x41\x81\xb5\xa9\x6d\x62\x62\x7d\xe2\xa1\x3f\x84\x98\x14\xb5\x69\x7c\x69\xc2\x83\x09\xbc\x2d\x0f\xa6\x21\x7d\xa2\x89\x0f\x24\x36\x29\x8f\x3c\x34\x0d\x4d\x6c\xca\x83\x65\x9a\xb9\xf7\x0c\x73\xe7\xcc\x5c\x58\x6d\x53\x30\xd9\x79\xf0\xdb\xf3\xdd\x7b\xce\x77\xee\xbd\xe7\xdc\x9d\xc5\x4f\xdf\x6e\x7f\x27\xa2\x28\xe0\x3e\x0a\xfc\x05\x9e\xe5\x3d\xd3\x07\xbd\xcf\xad\xf8\xef\x1e\x50\x20\xbb\x93\x73\xd7\x01\xa0\x10\x00\x32\xa5\xcb\xb6\x6b\xbf\x00\x00\x57\x00\xa0\x0e\x00\xb4\xd2\xc7\x8c\xcf\xde\xe1\xf3\x8b\x23\x00\x8f\x6d\xdb\xae\x26\x62\xd7\x59\x0e\x00\x31\x38\xcd\xec\x78\x31\xe7\xe9\x3c\xad\x66\x29\x10\x6f\x69\xcd\x78\x2d\xcc\x9e\x52\xb8\xfd\xa8\x32\xca\xe3\xdf\xe6\x71\x32\x35\x0b\xbe\xbc\x33\xa5\x8b\x81\xf8\x0b\xb6\x6d\x67\xef\xa2\x5d\x00\xb0\x6c\xdb\xf6\x54\x34\x3c\xbf\xa9\x2d\x9e\x7e\x24\x64\xfc\xa2\xc2\xf7\xe5\x0a\x62\x4c\xe1\x1b\x9c\xb9\x39\xc7\x74\x87\x6f\x2f\x62\x5e\xb3\x0c\xb5\x91\x5f\x90\xe7\x79\x6a\x35\xf3\x3c\xbf\x38\xea\x47\x88\x7e\x81\x93\x24\xd7\x7f\x91\xed\xff\xdc\xea\xfa\x9c\x94\x2f\xe2\xb9\xb8\xe7\x93\xfd\x0e\xd7\xb5\x05\x60\xd6\x59\xa7\x24\x6e\xb6\x00\xe3\x17\x02\x54\x63\xbc\xdd\x21\xf1\x62\x50\xba\xba\xdf\x3c\xdf\x07\x81\xfd\x7c\xb0\xe6\x79\x45\xc8\x79\xa1\x2e\xce\xfb\x4a\x98\xef\xd4\x5d\xb6\x08\xf3\x8d\xfa\xe3\x69\x37\x9f\x04\x74\x9f\xac\xa1\x9b\xc5\x7a\x8b\x15\x6f\xc5\xf3\x58\x24\x75\x11\xac\xbb\xc5\x35\xd7\xc1\x37\x6c\x18\xc7\xf7\x61\xbf\x3c\xba\x18\xbe\x9e\xf5\xfa\xc9\xcd\x2f\xfb\x31\x47\xb7\x7e\xb4\x1a\xde\x5f\x53\x25\xe1\xfb\x90\xfd\x8c\x63\xb0\xee\x78\xc0\xdc\xfb\xb2\x93\xd9\x71\x57\xe7\x5f\xf7\xe5\x7b\x7c\x1f\x48\x5f\x66\x46\xf8\xbe\xd3\xfe\x5c\xb7\x1f\x25\xf7\xc5\xd3\xf7\xe3\x49\xcc\x83\xf7\x59\x66\x44\xd2\x97\xe3\xcf\xd0\x97\x45\x62\x5f\x3e\xb4\xd7\x3e\x1f\x5e\xd8\x9b\xb7\xcf\x79\xc1\x4e\x15\xf1\xf3\xd3\x2e\x3d\x6d\x9f\xf3\x81\x47\x17\x70\xfd\x51\x8c\x8f\x18\x8b\x6e\xcc\x3d\xb2\x7e\xbf\xf1\x7b\x65\xfd\x3a\xc2\xf3\xcb\xf1\xfe\x89\x41\x8c\x9f\x47\x89\x7f\x5c\x1b\x5f\x0e\xac\x7b\x79\xad\x7b\x0c\xfb\x3f\x16\x2d\x63\x98\x19\xc1\x3e\x1a\x0f\x7e\xaf\x3d\xd3\xfd\x95\x08\xdf\xc7\xfc\xfd\x95\xbf\xbf\xf2\xf7\xd7\xff\x7f\x7f\xe5\xfb\x2e\xdf\x77\xf9\xbe\xdb\x80\xbe\xab\xe2\x18\x6f\xe6\xf5\x37\x85\xfb\xd1\x5b\xcf\xdf\x0f\xb4\x7a\xde\x87\x99\x7a\xac\xc7\x7a\xac\x83\xfa\x05\x44\xac\x93\xfa\x39\xc4\x59\xc4\x19\xc4\x69\xc4\x49\xc4\x7b\x88\x13\x88\x63\x88\xa3\x88\x37\x10\xaf\x21\x0e\x21\xa6\x11\x13\x88\x17\x10\x3b\x11\x3b\x10\xdb\x10\x5b\x11\x5b\x10\x9b\x10\x0f\x23\xd6\x21\x56\x23\x56\x22\x46\x11\x01\x71\x79\x05\xd7\x8f\xb8\x88\xb8\x80\x38\x8f\x38\x87\x38\x8b\x38\x83\x38\x8d\x38\x89\x78\x0f\x71\x82\xe1\x87\x0a\x80\x6d\x03\x54\xe0\x39\x64\xbf\xc7\xf3\x2d\x02\x98\x58\xf1\xea\xd4\x39\x97\xf3\x0a\x3f\xdf\xd7\x42\xfa\x38\xdb\x45\xcf\xbf\x9c\x9f\xeb\x6a\xfd\xdc\x5a\x81\xd0\xbf\x6b\x8c\xad\xd0\xba\x1a\x5b\xf1\xdf\x2f\xb7\x56\x82\x75\x46\xef\x15\x5a\x57\x4e\xf7\x3a\x53\x2a\x31\x9f\xfc\xe3\x3d\x25\xb8\x2f\xac\xe3\x2a\x37\x3a\x9b\xcd\xf5\x44\xb0\x6e\xa2\xf8\x39\xff\x78\x4f\x31\xee\xcd\xb2\xf3\x1d\x75\x78\xa3\xb3\xd9\x5c\xcf\x96\x8d\x4e\x60\x13\x3f\xce\x7d\xe3\x7c\x77\xb0\x3d\xca\x37\x95\xef\x29\xc1\xda\x61\x7b\x93\xbf\x8b\x7d\xcf\xbb\x1d\xed\xb0\x62\xdb\xec\x3d\xc8\xdd\x1a\x65\xe4\x03\x88\x5e\x2d\x53\xb6\x02\x7f\x3f\xae\x16\xe6\x5f\x13\x6a\xab\x16\x00\x8e\x09\x63\x73\x24\xb6\x33\x7e\x56\xb0\x87\x0a\x82\xe3\x96\x60\xb7\x29\xc1\xf1\x2f\xd7\xf1\xbf\xbf\xce\xf8\x43\xc1\xee\xcc\xb1\x2f\x7e\x67\x7d\xf4\x87\x4d\xf9\x1a\xe6\x5f\x00\x69\x12\xe7\x4d\x66\x17\xc2\x0c\x99\x3f\xc2\xf8\x62\x00\x92\xd7\x59\xe4\x13\x84\x3f\x81\xfc\x24\x89\x53\x85\x7c\x1b\xd1\xd5\x98\x5d\x02\xa3\x64\xfe\x7e\xe4\x5b\xc8\x7e\x1a\x8c\x2f\x0b\xe4\xd3\x8e\x3c\xcd\xa7\x11\x79\x9a\xcf\x56\xe4\x69\x3e\x97\x98\x5d\x1e\x88\xff\x16\xf2\x34\xfe\x61\xe4\x69\xfc\x42\xe4\x69\xfc\x73\xcc\xae\x08\xf0\xc0\x4e\xa5\x20\x48\x32\xbe\x50\xc2\x17\x4b\xf8\x12\x09\x5f\x26\xe1\xcb\x25\x7c\x45\x80\xfb\x1a\x00\xb6\x09\xdd\xe4\x7e\x9f\x5d\x65\xfc\xee\x00\x7f\x93\xf1\x3b\x57\xf9\x5a\x3c\xcf\x53\x8c\xdf\x16\xe0\x15\xc9\xf5\x36\xae\x84\xf3\x8a\x02\xb0\x5d\xd8\x07\xb7\x7f\x96\xc0\xe1\xbd\x7d\x70\xfb\xf2\x0c\xd3\xad\x0a\xe8\x66\xd9\xfc\xf2\x40\x9c\x2f\x24\xba\xe7\x59\x9c\x1d\x81\x38\xcd\x8c\xdf\x1e\xe0\xf7\x31\x3e\x16\xe0\xd3\x8c\xdf\x15\xe0\xef\xb0\x75\x79\xfb\xef\xf6\xfd\x0f\x2c\xcf\xb2\x40\x9e\x3f\x33\xde\xab\x1f\xf7\x8e\xfb\x95\xf1\x5e\xfd\xb8\xf7\x5b\x2b\xa2\x82\xaf\x68\x09\xc1\x76\x72\x1d\x15\xec\x3d\xce\x7b\xae\x70\x3e\x2f\x39\x3e\x82\x7d\x10\x00\x6e\x08\xf6\x21\x00\x58\x10\xec\x97\x1d\xbd\x88\x67\x1f\x10\xef\x69\xd4\x6b\x21\x7a\x37\xc0\xaf\xd7\x26\xd8\x7b\xc9\xb8\x93\xff\x24\x89\x37\x4f\xe2\xb5\x91\xfc\x13\x24\xff\x39\x92\x7f\x5d\xc4\x6f\x0f\x45\xfc\xeb\x99\x27\xeb\x69\x29\x20\x36\xc9\x37\x4d\xf2\x1d\x25\xf9\x4e\x93\x7c\x41\x79\xbe\xd7\xff\xbc\xe5\x3b\x2d\xc4\xab\x5d\xbd\xd7\xbd\x7c\x6b\x85\xfc\xaa\xc8\xf9\x3a\xb7\x40\xa7\x60\x3b\x5d\x3e\x24\xd8\xce\xed\x37\x26\xd8\xbb\xc8\x79\x57\x93\xf5\xb3\x5b\xb4\x27\xd9\x67\xe9\x86\x3a\x68\xea\x86\xda\x95\x4c\xc5\x8d\x61\x68\xb0\xf4\x21\x0b\xfa\xe3\x69\xb3\xd1\x32\xe2\x9a\xae\xea\x97\xf5\x94\x65\x72\xc6\xb4\xe2\x86\xa5\xba\x96\x36\x30\x98\xb2\x74\x03\xad\xbe\x01\xd3\x52\x99\x8b\x37\x59\xeb\xf5\x31\xec\x73\xb7\x9a\x4e\x76\x9b\xa0\x5e\xd6\x0d\x33\x39\x90\xe2\x23\x5c\x5c\x4d\xc7\xad\x04\xa8\x7d\x49\x4d\x4f\x99\x3a\x34\x18\x7a\x5f\x83\x9e\x50\x7b\x8c\x78\xbf\x0e\xcc\x39\x3d\x90\x4c\x59\xea\x20\xfb\x0c\x69\xdd\xe8\xe1\xe9\xb9\xcc\x60\xda\x18\xe8\xd2\x57\x2d\x43\xb7\x7c\x44\x6f\x80\xf0\x59\x8e\x9e\xa7\xd2\x28\xb0\x9e\x92\xc8\x72\x35\x1f\xe3\x0a\x88\x64\x6f\x28\xe9\x63\x4c\x3d\xd5\xad\x0a\xfb\x05\x3d\x83\x29\x4d\x4d\x76\x8b\x8b\x36\xb5\x84\xde\x8d\xff\xa6\x8d\x01\x4d\x37\x4d\x55\x1f\xd2\x35\x9a\x36\x9b\xd1\x18\x32\xaf\x3f\x9e\x4c\x35\x68\xd0\x60\x5a\x86\x15\xef\x82\x06\x73\xb8\xdf\xc1\xf6\xd3\xa7\x8f\xab\xaf\x3b\x70\x8c\xc3\x11\x0e\x4d\xea\x09\x07\x8e\xaa\xc7\xf9\x94\xa3\x7c\xca\x51\x3e\x85\x41\x33\x87\xe3\x6a\x33\x0e\x36\xe3\x28\xc7\x13\xea\x11\x1e\xe1\x08\xce\x6a\xc2\x59\x4d\x38\xab\x09\xfe\x9b\xe7\x4f\x25\xf0\x2a\xc5\x9e\xa6\x03\x1c\x3f\x27\xef\x41\xf4\x6f\x70\x3b\x90\x2b\x22\x7c\xab\x44\x8f\xfe\xbe\x3e\xa5\xac\xed\x7f\x8d\xe8\x93\xff\x96\x81\x93\x0a\x84\xbe\x4d\x8d\xee\x45\x7f\xb4\x6b\x81\xaf\xd3\xf5\x77\xbf\xdf\xde\x97\xe8\xcf\xa0\x2e\xfd\xbd\x41\xf5\xcf\x48\xf4\x2b\xf7\x71\x6c\x12\xf4\x0b\x43\xf4\x33\x12\xfd\x51\x3c\x14\xfa\x7b\x83\xea\x5f\x91\xe8\xb7\xa2\xfe\xa4\xa0\x5f\x1c\xa2\xdf\x23\xd1\x8f\xe2\x2b\x1b\xfd\xbd\x44\xf5\xbb\x24\xfa\x4b\xa8\xdf\x21\xe8\x97\x84\xe8\x57\x49\xf4\x3b\x70\xe2\x7a\xeb\xaf\x90\xad\xbf\x8e\xa3\xb8\xfe\xb2\x10\xfd\x57\x24\xfa\xb3\x65\xb9\xe9\x1f\x92\xad\x3f\x44\xbf\x3c\x44\xff\x27\x59\xfd\xe3\xab\x25\xfd\x3d\x49\xf5\x7f\x94\xe8\x4f\xec\xe7\x58\x27\xe8\x57\x84\xe8\xbf\x81\xfd\x4b\xef\x80\x31\x7c\xf5\xaf\x25\xe7\x4f\xfb\xf7\x55\x89\xff\xfd\xed\xb9\xf9\xb7\x49\xfc\xe7\xaa\x72\xf3\xff\x48\xe2\xff\xdb\x8e\xdc\xfc\x6b\x24\xfe\xd1\x9d\xb9\xf9\x5f\x97\xf8\x1f\xdc\x95\x9b\x7f\xaf\xc4\xbf\xb5\x3a\x37\xff\x6f\x24\xfe\xe7\x76\x87\xcf\xa7\xf7\xf7\x27\x12\xff\x0b\x12\x7f\x6a\xdf\x45\x7f\xfa\x73\x39\x81\xfe\x4b\x84\xa7\xf5\xfb\x2d\x84\xd7\xef\x0c\xd6\xaf\xfb\x5e\x5e\x8b\x67\x45\xeb\xf7\x6f\x25\xfc\x4f\x91\x9d\x35\x1c\x5b\x30\x61\x85\xf8\xbb\xbf\x90\xff\x09\x00\x00\xff\xff\x75\x15\xca\x5c\xf8\x2b\x00\x00") 82 | 83 | func bindataProbeOBytes() ([]byte, error) { 84 | return bindataRead( 85 | _bindataProbeO, 86 | "/probe.o", 87 | ) 88 | } 89 | 90 | 91 | 92 | func bindataProbeO() (*asset, error) { 93 | bytes, err := bindataProbeOBytes() 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | info := bindataFileInfo{ 99 | name: "/probe.o", 100 | size: 11256, 101 | md5checksum: "", 102 | mode: os.FileMode(420), 103 | modTime: time.Unix(1642032777, 0), 104 | } 105 | 106 | a := &asset{bytes: bytes, info: info} 107 | 108 | return a, nil 109 | } 110 | 111 | 112 | // 113 | // Asset loads and returns the asset for the given name. 114 | // It returns an error if the asset could not be found or 115 | // could not be loaded. 116 | // 117 | func Asset(name string) ([]byte, error) { 118 | cannonicalName := strings.Replace(name, "\\", "/", -1) 119 | if f, ok := _bindata[cannonicalName]; ok { 120 | a, err := f() 121 | if err != nil { 122 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 123 | } 124 | return a.bytes, nil 125 | } 126 | return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} 127 | } 128 | 129 | // 130 | // MustAsset is like Asset but panics when Asset would return an error. 131 | // It simplifies safe initialization of global variables. 132 | // nolint: deadcode 133 | // 134 | func MustAsset(name string) []byte { 135 | a, err := Asset(name) 136 | if err != nil { 137 | panic("asset: Asset(" + name + "): " + err.Error()) 138 | } 139 | 140 | return a 141 | } 142 | 143 | // 144 | // AssetInfo loads and returns the asset info for the given name. 145 | // It returns an error if the asset could not be found or could not be loaded. 146 | // 147 | func AssetInfo(name string) (os.FileInfo, error) { 148 | cannonicalName := strings.Replace(name, "\\", "/", -1) 149 | if f, ok := _bindata[cannonicalName]; ok { 150 | a, err := f() 151 | if err != nil { 152 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 153 | } 154 | return a.info, nil 155 | } 156 | return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} 157 | } 158 | 159 | // 160 | // AssetNames returns the names of the assets. 161 | // nolint: deadcode 162 | // 163 | func AssetNames() []string { 164 | names := make([]string, 0, len(_bindata)) 165 | for name := range _bindata { 166 | names = append(names, name) 167 | } 168 | return names 169 | } 170 | 171 | // 172 | // _bindata is a table, holding each asset generator, mapped to its name. 173 | // 174 | var _bindata = map[string]func() (*asset, error){ 175 | "/probe.o": bindataProbeO, 176 | } 177 | 178 | // 179 | // AssetDir returns the file names below a certain 180 | // directory embedded in the file by go-bindata. 181 | // For example if you run go-bindata on data/... and data contains the 182 | // following hierarchy: 183 | // data/ 184 | // foo.txt 185 | // img/ 186 | // a.png 187 | // b.png 188 | // then AssetDir("data") would return []string{"foo.txt", "img"} 189 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 190 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 191 | // AssetDir("") will return []string{"data"}. 192 | // 193 | func AssetDir(name string) ([]string, error) { 194 | node := _bintree 195 | if len(name) != 0 { 196 | cannonicalName := strings.Replace(name, "\\", "/", -1) 197 | pathList := strings.Split(cannonicalName, "/") 198 | for _, p := range pathList { 199 | node = node.Children[p] 200 | if node == nil { 201 | return nil, &os.PathError{ 202 | Op: "open", 203 | Path: name, 204 | Err: os.ErrNotExist, 205 | } 206 | } 207 | } 208 | } 209 | if node.Func != nil { 210 | return nil, &os.PathError{ 211 | Op: "open", 212 | Path: name, 213 | Err: os.ErrNotExist, 214 | } 215 | } 216 | rv := make([]string, 0, len(node.Children)) 217 | for childName := range node.Children { 218 | rv = append(rv, childName) 219 | } 220 | return rv, nil 221 | } 222 | 223 | 224 | type bintree struct { 225 | Func func() (*asset, error) 226 | Children map[string]*bintree 227 | } 228 | 229 | var _bintree = &bintree{Func: nil, Children: map[string]*bintree{ 230 | "": {Func: nil, Children: map[string]*bintree{ 231 | "probe.o": {Func: bindataProbeO, Children: map[string]*bintree{}}, 232 | }}, 233 | }} 234 | 235 | // RestoreAsset restores an asset under the given directory 236 | func RestoreAsset(dir, name string) error { 237 | data, err := Asset(name) 238 | if err != nil { 239 | return err 240 | } 241 | info, err := AssetInfo(name) 242 | if err != nil { 243 | return err 244 | } 245 | err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) 246 | if err != nil { 247 | return err 248 | } 249 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 250 | if err != nil { 251 | return err 252 | } 253 | return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 254 | } 255 | 256 | // RestoreAssets restores an asset under the given directory recursively 257 | func RestoreAssets(dir, name string) error { 258 | children, err := AssetDir(name) 259 | // File 260 | if err != nil { 261 | return RestoreAsset(dir, name) 262 | } 263 | // Dir 264 | for _, child := range children { 265 | err = RestoreAssets(dir, filepath.Join(name, child)) 266 | if err != nil { 267 | return err 268 | } 269 | } 270 | return nil 271 | } 272 | 273 | func _filePath(dir, name string) string { 274 | cannonicalName := strings.Replace(name, "\\", "/", -1) 275 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 276 | } 277 | -------------------------------------------------------------------------------- /pkg/utrace/byteorder.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utrace 18 | 19 | import ( 20 | "encoding/binary" 21 | "unsafe" 22 | ) 23 | 24 | // GetHostByteOrder guesses the hosts byte order 25 | func GetHostByteOrder() binary.ByteOrder { 26 | var i int32 = 0x01020304 27 | u := unsafe.Pointer(&i) 28 | pb := (*byte)(u) 29 | b := *pb 30 | if b == 0x04 { 31 | return binary.LittleEndian 32 | } 33 | 34 | return binary.BigEndian 35 | } 36 | 37 | // ByteOrder holds the hosts byte order 38 | var ByteOrder binary.ByteOrder 39 | 40 | func init() { 41 | ByteOrder = GetHostByteOrder() 42 | } 43 | -------------------------------------------------------------------------------- /pkg/utrace/model.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utrace 18 | 19 | import ( 20 | "debug/elf" 21 | "regexp" 22 | "sort" 23 | "time" 24 | 25 | "github.com/pkg/errors" 26 | ) 27 | 28 | const ( 29 | // MaxUserSymbolsCount is the max number of symbols probed at the same time 30 | MaxUserSymbolsCount = uint32(100) 31 | // MaxKernelSymbolsCount is the max number of kernel symbols probed at the same time 32 | MaxKernelSymbolsCount = uint32(500) 33 | ) 34 | 35 | var ( 36 | // NotEnoughDataErr indicates that the data retrieved from the perf map is not long enough 37 | NotEnoughDataErr = errors.New("not enough data") 38 | // EmptyPatternsErr indicates that both the kernel function and userspace symbol patterns are empty 39 | EmptyPatternsErr = errors.New("empty patterns") 40 | // EmptyBinaryPathErr indicates that a userspace symbol pattern was provided but without a binary path 41 | EmptyBinaryPathErr = errors.New("empty binary path") 42 | // NoPatternProvidedErr indicates that no function pattern was provided 43 | NoPatternProvidedErr = errors.New("no function pattern or hook point was provided") 44 | ) 45 | 46 | // StackID is a unique identifier used to select a stack trace 47 | type StackID uint32 48 | 49 | // CombinedID is a unique identifier used to select a combined stack trace (user and kernel) 50 | type CombinedID uint64 51 | 52 | // FuncID is the id of a function traced in kernel space 53 | type FuncID int32 54 | 55 | // SymbolAddr is the address of a symbol 56 | type SymbolAddr uint64 57 | 58 | // BinaryCookie is a unique identifier used to select a TracedBinary 59 | type BinaryCookie uint32 60 | 61 | // PathMax - Maximum path length of the binary path handled by utrace 62 | const PathMax = 350 63 | 64 | // Options contains the parameters of UTrace 65 | type Options struct { 66 | FuncPattern *regexp.Regexp 67 | KernelFuncPattern *regexp.Regexp 68 | Tracepoints []string 69 | PerfEvents []string 70 | Latency bool 71 | StackTraces bool 72 | Binary []string 73 | PIDFilter []int 74 | } 75 | 76 | func (o Options) check() error { 77 | if o.FuncPattern == nil && o.KernelFuncPattern == nil { 78 | return EmptyPatternsErr 79 | } 80 | if o.FuncPattern != nil && len(o.Binary) == 0 { 81 | return EmptyBinaryPathErr 82 | } 83 | return nil 84 | } 85 | 86 | var ( 87 | // SymbolNotFound is used to notify that a symbol could not be resolved 88 | SymbolNotFound = elf.Symbol{Name: "[symbol_not_found]"} 89 | ) 90 | 91 | // Report contains the statistics generated by UTRace 92 | type Report struct { 93 | stackTracerCount StackTraceCount 94 | 95 | orderedByLatency map[BinaryCookie][]Func 96 | orderedByHits map[BinaryCookie][]Func 97 | functions map[FuncID]Func 98 | duration time.Duration 99 | } 100 | 101 | // NewReport instanciates a new Report 102 | func NewReport(duration time.Duration) Report { 103 | return Report{ 104 | orderedByLatency: make(map[BinaryCookie][]Func), 105 | orderedByHits: make(map[BinaryCookie][]Func), 106 | functions: make(map[FuncID]Func), 107 | duration: duration, 108 | } 109 | } 110 | 111 | // GetStackTraceCount returns the total number of stack traces collected from the kernel 112 | func (r *Report) GetStackTraceCount() StackTraceCount { 113 | return r.stackTracerCount 114 | } 115 | 116 | // GetDuration returns the duration of the trace 117 | func (r *Report) GetDuration() time.Duration { 118 | return r.duration 119 | } 120 | 121 | // GetFunc returns a Func by its FuncID 122 | func (r *Report) GetFunc(id FuncID) Func { 123 | if id == -1 { 124 | return NewFunc(SymbolNotFound, nil) 125 | } 126 | ret, ok := r.functions[id] 127 | if !ok { 128 | return NewFunc(SymbolNotFound, nil) 129 | } 130 | return ret 131 | } 132 | 133 | // GetFunctionsByHits returns the list of traced functions ordered by their hits count 134 | func (r *Report) GetFunctionsByHits() map[BinaryCookie][]Func { 135 | if len(r.orderedByHits) == 0 { 136 | for _, f := range r.functions { 137 | if f.Binary != nil { 138 | r.orderedByHits[f.Binary.Cookie] = append(r.orderedByHits[f.Binary.Cookie], f) 139 | } else { 140 | r.orderedByHits[0] = append(r.orderedByHits[0], f) 141 | 142 | // check if there is a user space part to the stack traces, if so, create a new Func entry in the 143 | // relevant binary pool 144 | cache := make(map[BinaryCookie]*Func) 145 | 146 | for combinedID, stack := range f.stackTraces { 147 | if stack.Binary == nil { 148 | continue 149 | } 150 | 151 | binaryFunc, ok := cache[stack.Binary.Cookie] 152 | if !ok { 153 | newFunc := NewFunc(f.Symbol, f.Binary) 154 | // copy average lagency ... it might not be the actual value for this binary, but that's all 155 | // we have. 156 | newFunc.AverageLatency = f.AverageLatency 157 | cache[stack.Binary.Cookie] = &newFunc 158 | binaryFunc = &newFunc 159 | } 160 | 161 | // add stack trace 162 | binaryFunc.stackTraces[combinedID] = stack 163 | binaryFunc.Count += uint64(stack.Count) 164 | } 165 | 166 | for cookie, newFunc := range cache { 167 | r.orderedByHits[cookie] = append(r.orderedByHits[cookie], *newFunc) 168 | } 169 | } 170 | } 171 | for key := range r.orderedByHits { 172 | sort.Sort(orderByHits(r.orderedByHits[key])) 173 | } 174 | } 175 | return r.orderedByHits 176 | } 177 | 178 | // GetFunctionsByLatency returns the list of traced functions ordered by their latency 179 | func (r *Report) GetFunctionsByLatency() map[BinaryCookie][]Func { 180 | if len(r.orderedByLatency) == 0 { 181 | for _, f := range r.functions { 182 | if f.Binary != nil { 183 | r.orderedByLatency[f.Binary.Cookie] = append(r.orderedByLatency[f.Binary.Cookie], f) 184 | } else { 185 | r.orderedByLatency[0] = append(r.orderedByLatency[0], f) 186 | 187 | // check if there is a user space part to the stack traces, if so, create a new Func entry in the 188 | // relevant binary pool 189 | cache := make(map[BinaryCookie]*Func) 190 | 191 | for combinedID, stack := range f.stackTraces { 192 | if stack.Binary == nil { 193 | continue 194 | } 195 | 196 | binaryFunc, ok := cache[stack.Binary.Cookie] 197 | if !ok { 198 | newFunc := NewFunc(f.Symbol, f.Binary) 199 | // copy average lagency ... it might not be the actual value for this binary, but that's all 200 | // we have. 201 | newFunc.AverageLatency = f.AverageLatency 202 | cache[stack.Binary.Cookie] = &newFunc 203 | binaryFunc = &newFunc 204 | } 205 | 206 | // add stack trace 207 | binaryFunc.stackTraces[combinedID] = stack 208 | binaryFunc.Count += uint64(stack.Count) 209 | } 210 | 211 | for cookie, newFunc := range cache { 212 | r.orderedByLatency[cookie] = append(r.orderedByLatency[cookie], *newFunc) 213 | } 214 | } 215 | } 216 | for key := range r.orderedByLatency { 217 | sort.Sort(orderByLatency(r.orderedByLatency[key])) 218 | } 219 | } 220 | return r.orderedByLatency 221 | } 222 | 223 | // StackTraceCount holds the amount of traces that were collected or lost 224 | type StackTraceCount struct { 225 | Kernel uint64 226 | LostKernel uint64 227 | User uint64 228 | LostUser uint64 229 | } 230 | 231 | // FuncType is the type of a traced function 232 | type FuncType string 233 | 234 | const ( 235 | // Kernel is used for kernel symbols 236 | Kernel FuncType = "kernel" 237 | // User is used for user space symbols 238 | User FuncType = "user" 239 | ) 240 | 241 | // Func contains the data collected by utrace for a function 242 | type Func struct { 243 | Type FuncType 244 | Symbol elf.Symbol 245 | Count uint64 246 | AverageLatency time.Duration 247 | Binary *TracedBinary 248 | 249 | stackTraces map[CombinedID]*StackTrace 250 | orderedByHits []*StackTrace 251 | } 252 | 253 | // NewFunc instanciates a new Func 254 | func NewFunc(symbol elf.Symbol, binary *TracedBinary) Func { 255 | f := Func{ 256 | Symbol: symbol, 257 | Binary: binary, 258 | stackTraces: make(map[CombinedID]*StackTrace), 259 | } 260 | if symbol.Value > 0xffffffff00000000 { 261 | f.Type = Kernel 262 | } else { 263 | f.Type = User 264 | } 265 | return f 266 | } 267 | 268 | // GetStackTrace returns a stack trace from its StackID 269 | func (f *Func) GetStackTrace(stackID CombinedID) *StackTrace { 270 | return f.stackTraces[stackID] 271 | } 272 | 273 | // GetStackTracesByHits returns the list of stack traces by hits count 274 | func (f *Func) GetStackTracesByHits() []*StackTrace { 275 | if len(f.orderedByHits) == 0 { 276 | for _, trace := range f.stackTraces { 277 | f.orderedByHits = append(f.orderedByHits, trace) 278 | } 279 | sort.Sort(orderTraceByHits(f.orderedByHits)) 280 | } 281 | return f.orderedByHits 282 | } 283 | 284 | type orderByHits []Func 285 | 286 | func (obh orderByHits) Len() int { return len(obh) } 287 | func (obh orderByHits) Swap(i, j int) { obh[i], obh[j] = obh[j], obh[i] } 288 | func (obh orderByHits) Less(i, j int) bool { return obh[i].Count > obh[j].Count } 289 | 290 | type orderByLatency []Func 291 | 292 | func (obl orderByLatency) Len() int { return len(obl) } 293 | func (obl orderByLatency) Swap(i, j int) { obl[i], obl[j] = obl[j], obl[i] } 294 | func (obl orderByLatency) Less(i, j int) bool { return obl[i].AverageLatency > obl[j].AverageLatency } 295 | 296 | // StackTrace contains the ordered list of symbols of a stack trace 297 | type StackTrace struct { 298 | Count int 299 | Binary *TracedBinary 300 | UserStacktrace []StackTraceNode 301 | KernelStackTrace []StackTraceNode 302 | } 303 | 304 | type orderTraceByHits []*StackTrace 305 | 306 | func (otbh orderTraceByHits) Len() int { return len(otbh) } 307 | func (otbh orderTraceByHits) Swap(i, j int) { otbh[i], otbh[j] = otbh[j], otbh[i] } 308 | func (otbh orderTraceByHits) Less(i, j int) bool { return otbh[i].Count > otbh[j].Count } 309 | 310 | // NewStackTrace returns a new StackTrace initialized with the provided count 311 | func NewStackTrace(count int, binary *TracedBinary) *StackTrace { 312 | return &StackTrace{ 313 | Count: count, 314 | Binary: binary, 315 | } 316 | } 317 | 318 | // StackTraceNode represents a node of a stack trace 319 | type StackTraceNode struct { 320 | Type FuncType 321 | FuncID FuncID 322 | Offset SymbolAddr 323 | Symbol elf.Symbol 324 | } 325 | 326 | type kernelCounter struct { 327 | CumulatedTime uint64 328 | Count uint64 329 | } 330 | 331 | // TraceEvent is a kernel trace event retrieved from a perf map 332 | type TraceEvent struct { 333 | Pid uint32 334 | Tid uint32 335 | UserStackID StackID 336 | KernelStackID StackID 337 | FuncID FuncID 338 | Cookie BinaryCookie 339 | } 340 | 341 | // UnmarshalBinary unmarshals the binary representation of a trace event 342 | func (te *TraceEvent) UnmarshalBinary(data []byte) (int, error) { 343 | if len(data) < 24 { 344 | return 0, NotEnoughDataErr 345 | } 346 | 347 | te.Pid = ByteOrder.Uint32(data[0:4]) 348 | te.Tid = ByteOrder.Uint32(data[4:8]) 349 | te.UserStackID = StackID(ByteOrder.Uint32(data[8:12])) 350 | te.KernelStackID = StackID(ByteOrder.Uint32(data[12:16])) 351 | te.FuncID = FuncID(ByteOrder.Uint32(data[16:20])) 352 | te.Cookie = BinaryCookie(ByteOrder.Uint32(data[20:24])) 353 | return 24, nil 354 | } 355 | 356 | type TracedBinary struct { 357 | Path string 358 | ResolvedPath string 359 | Inode uint64 360 | Size int64 361 | Cookie BinaryCookie 362 | Pids []int 363 | 364 | symbolsCache map[SymbolAddr]elf.Symbol 365 | symbolNameToFuncID map[string]FuncID 366 | file *elf.File 367 | } 368 | 369 | type TracedSymbol struct { 370 | symbol elf.Symbol 371 | binary *TracedBinary 372 | } 373 | -------------------------------------------------------------------------------- /pkg/utrace/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utrace 18 | 19 | import ( 20 | "math/rand" 21 | "strings" 22 | "time" 23 | ) 24 | 25 | func init() { 26 | rand.Seed(time.Now().UnixNano()) 27 | } 28 | 29 | func sanitizeFuncName(name string) string { 30 | escapedName := strings.ReplaceAll(name, "*", `\*`) 31 | escapedName = strings.ReplaceAll(escapedName, "(", `\(`) 32 | escapedName = strings.ReplaceAll(escapedName, ")", `\)`) 33 | escapedName = strings.ReplaceAll(escapedName, "[", `\[`) 34 | escapedName = strings.ReplaceAll(escapedName, "]", `\]`) 35 | return escapedName 36 | } 37 | 38 | var ( 39 | letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 40 | ) 41 | 42 | func randomStringFromSliceWithLen(runes []rune, n int) string { 43 | b := make([]rune, n) 44 | for i := range b { 45 | b[i] = letterRunes[rand.Intn(len(runes))] 46 | } 47 | return string(b) 48 | } 49 | 50 | // RandomStringWithLen returns a random string of specified length containing 51 | // upper- and lowercase runes. 52 | func RandomStringWithLen(n int) string { 53 | return randomStringFromSliceWithLen(letterRunes, n) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/utrace/utrace.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utrace 18 | 19 | import ( 20 | "bytes" 21 | "debug/elf" 22 | "fmt" 23 | "io/ioutil" 24 | "math" 25 | "math/rand" 26 | "os" 27 | "runtime" 28 | "strconv" 29 | "strings" 30 | "sync/atomic" 31 | "syscall" 32 | "time" 33 | 34 | manager "github.com/DataDog/ebpf-manager" 35 | "github.com/cilium/ebpf" 36 | "github.com/pkg/errors" 37 | "github.com/sirupsen/logrus" 38 | "golang.org/x/sys/unix" 39 | 40 | "github.com/Gui774ume/utrace/pkg/assets" 41 | ) 42 | 43 | // UTrace is the main UTrace structure 44 | type UTrace struct { 45 | options Options 46 | kernelCountersMap *ebpf.Map 47 | stackTracesMap *ebpf.Map 48 | binaryPathMap *ebpf.Map 49 | lostCountMap *ebpf.Map 50 | tracedPIDsMap *ebpf.Map 51 | manager *manager.Manager 52 | managerOptions manager.Options 53 | startTime time.Time 54 | funcIDCursor FuncID 55 | 56 | // kallsymsCache contains the kernel symbols parsed from /proc/kallsyms 57 | kallsymsCache map[SymbolAddr]elf.Symbol 58 | // kernelSymbolNameToFuncID contains the FuncID attributed to each kernel symbol 59 | kernelSymbolNameToFuncID map[string]FuncID 60 | 61 | // funcCache holds the traced symbol associated to each FuncID (kernel and userspace) 62 | funcCache map[FuncID]TracedSymbol 63 | // stackTraces holds the list of collected stack traces for all FuncID (kernel and unserspace) 64 | stackTraces map[FuncID]map[CombinedID]*StackTrace 65 | 66 | // TracedBinaries is the list of userspace binaries for which we are collecting stack traces 67 | TracedBinaries map[BinaryCookie]*TracedBinary 68 | 69 | kernelStackTraceCounter uint64 70 | kernelStackTraceLost uint64 71 | userStackTraceCounter uint64 72 | userStackTraceLost uint64 73 | } 74 | 75 | // NewUTrace creates a new UTrace instance 76 | func NewUTrace(options Options) *UTrace { 77 | return &UTrace{ 78 | options: options, 79 | funcCache: make(map[FuncID]TracedSymbol), 80 | kernelSymbolNameToFuncID: make(map[string]FuncID), 81 | kallsymsCache: make(map[SymbolAddr]elf.Symbol), 82 | stackTraces: make(map[FuncID]map[CombinedID]*StackTrace), 83 | TracedBinaries: make(map[BinaryCookie]*TracedBinary), 84 | } 85 | } 86 | 87 | // Start hooks on the requested symbols and begins tracing 88 | func (u *UTrace) Start() error { 89 | // ensure that at least one function pattern was provided 90 | if u.options.FuncPattern == nil && u.options.KernelFuncPattern == nil && len(u.options.Tracepoints) == 0 && len(u.options.PerfEvents) == 0 { 91 | return NoPatternProvidedErr 92 | } 93 | 94 | if err := u.start(); err != nil { 95 | return err 96 | } 97 | 98 | logrus.Infof("Tracing started on %d symbols ... (Ctrl + C to stop)", len(u.funcCache)) 99 | return nil 100 | } 101 | 102 | // dump dumps the the statistiques collected by UTrace 103 | func (u *UTrace) dump() (Report, error) { 104 | report := NewReport(time.Now().Sub(u.startTime)) 105 | var id FuncID 106 | stats := make([]kernelCounter, runtime.NumCPU()) 107 | iterator := u.kernelCountersMap.Iterate() 108 | 109 | for iterator.Next(&id, &stats) { 110 | symbol, ok := u.funcCache[id] 111 | if !ok { 112 | continue 113 | } 114 | f := NewFunc(symbol.symbol, symbol.binary) 115 | for _, cpuStat := range stats { 116 | f.Count += cpuStat.Count 117 | f.AverageLatency += time.Duration(cpuStat.CumulatedTime) * time.Nanosecond 118 | } 119 | if f.Count > 0 { 120 | f.AverageLatency = time.Duration(float64(f.AverageLatency.Nanoseconds()) / float64(f.Count)) 121 | } 122 | 123 | f.stackTraces = u.stackTraces[id] 124 | 125 | report.functions[id] = f 126 | } 127 | 128 | // fetch counters 129 | report.stackTracerCount.Kernel = atomic.LoadUint64(&u.kernelStackTraceCounter) 130 | report.stackTracerCount.User = atomic.LoadUint64(&u.userStackTraceCounter) 131 | if err := u.lostCountMap.Lookup([4]byte{0}, &report.stackTracerCount.LostUser); err != nil { 132 | logrus.Warnf("failed to retrieve user stack trace lost count: %s", err) 133 | } 134 | if err := u.lostCountMap.Lookup([4]byte{1}, &report.stackTracerCount.LostKernel); err != nil { 135 | logrus.Warnf("failed to retrieve kernel stack trace lost count: %s", err) 136 | } 137 | report.stackTracerCount.LostUser += atomic.LoadUint64(&u.userStackTraceLost) 138 | report.stackTracerCount.LostKernel += atomic.LoadUint64(&u.kernelStackTraceLost) 139 | return report, iterator.Err() 140 | } 141 | 142 | // Stop shuts down UTrace 143 | func (u *UTrace) Stop() (Report, error) { 144 | // stop all probes 145 | for _, probe := range u.manager.Probes { 146 | _ = probe.Stop() 147 | } 148 | 149 | // sleep until the perf map is empty 150 | logrus.Infof("flushing the remaining events in the perf map ...") 151 | var done bool 152 | var lastCount uint64 153 | for !done { 154 | lastCount = atomic.LoadUint64(&u.kernelStackTraceCounter) + atomic.LoadUint64(&u.userStackTraceCounter) 155 | time.Sleep(1 * time.Second) 156 | if lastCount == atomic.LoadUint64(&u.kernelStackTraceCounter)+atomic.LoadUint64(&u.userStackTraceCounter) { 157 | done = true 158 | } 159 | } 160 | 161 | // dump 162 | dump, err := u.dump() 163 | // Close the manager 164 | _ = errors.Wrap(u.manager.Stop(manager.CleanAll), "couldn't stop manager") 165 | return dump, err 166 | } 167 | 168 | // nextFuncID returns the next funcID 169 | func (u *UTrace) nextFuncID() FuncID { 170 | id := u.funcIDCursor 171 | u.funcIDCursor++ 172 | return id 173 | } 174 | 175 | func (u *UTrace) setupDefaultManager() { 176 | execTracepoint := &manager.Probe{ 177 | ProbeIdentificationPair: manager.ProbeIdentificationPair{ 178 | UID: "utrace", 179 | EBPFSection: "tracepoint/sched/sched_process_exec", 180 | EBPFFuncName: "tracepoint_sched_sched_process_exec", 181 | }, 182 | } 183 | u.manager = &manager.Manager{ 184 | Probes: []*manager.Probe{execTracepoint}, 185 | PerfMaps: []*manager.PerfMap{ 186 | { 187 | Map: manager.Map{ 188 | Name: "trace_events", 189 | }, 190 | PerfMapOptions: manager.PerfMapOptions{ 191 | PerfRingBufferSize: 8192 * os.Getpagesize(), 192 | DataHandler: u.TraceEventsHandler, 193 | }, 194 | }, 195 | }, 196 | } 197 | u.managerOptions.DefaultKProbeMaxActive = 50 198 | u.managerOptions.ActivatedProbes = append(u.managerOptions.ActivatedProbes, &manager.OneOf{ 199 | Selectors: []manager.ProbesSelector{ 200 | &manager.ProbeSelector{ 201 | ProbeIdentificationPair: execTracepoint.ProbeIdentificationPair, 202 | }, 203 | }, 204 | }) 205 | u.managerOptions.MapSpecEditors = make(map[string]manager.MapSpecEditor) 206 | if u.options.StackTraces { 207 | u.managerOptions.ConstantEditors = append(u.managerOptions.ConstantEditors, manager.ConstantEditor{ 208 | Name: "send_stack_trace", 209 | Value: uint64(1), 210 | }) 211 | } 212 | u.managerOptions.RLimit = &unix.Rlimit{ 213 | Cur: math.MaxUint64, 214 | Max: math.MaxUint64, 215 | } 216 | } 217 | 218 | func (u *UTrace) selectMaps() error { 219 | var err error 220 | u.kernelCountersMap, _, err = u.manager.GetMap("counters") 221 | if err != nil { 222 | _ = u.manager.Stop(manager.CleanAll) 223 | return errors.Wrap(err, "couldn't find maps/counters") 224 | } 225 | 226 | u.stackTracesMap, _, err = u.manager.GetMap("stack_traces") 227 | if err != nil { 228 | _ = u.manager.Stop(manager.CleanAll) 229 | return errors.Wrap(err, "couldn't find maps/stack_traces") 230 | } 231 | 232 | u.binaryPathMap, _, err = u.manager.GetMap("binary_path") 233 | if err != nil { 234 | _ = u.manager.Stop(manager.CleanAll) 235 | return errors.Wrap(err, "couldn't find maps/binary_path") 236 | } 237 | 238 | u.lostCountMap, _, err = u.manager.GetMap("lost_traces") 239 | if err != nil { 240 | _ = u.manager.Stop(manager.CleanAll) 241 | return errors.Wrap(err, "couldn't find maps/lost_traces") 242 | } 243 | 244 | u.tracedPIDsMap, _, err = u.manager.GetMap("traced_pids") 245 | if err != nil { 246 | _ = u.manager.Stop(manager.CleanAll) 247 | return errors.Wrap(err, "couldn't find maps/traced_pids") 248 | } 249 | return nil 250 | } 251 | 252 | func (u *UTrace) insertTracedBinary(path string, pid int) error { 253 | // fetch the binary file inode 254 | fileinfo, err := os.Stat(path) 255 | if err != nil { 256 | return fmt.Errorf("couldn't load %s: %v", path, err) 257 | } 258 | 259 | resolvedPath, err := os.Readlink(path) 260 | if err != nil { 261 | resolvedPath = "" 262 | } 263 | 264 | stat, ok := fileinfo.Sys().(*syscall.Stat_t) 265 | if !ok { 266 | return fmt.Errorf("couldn't load %s: %v", path, err) 267 | } 268 | 269 | // check if the file has been seen before 270 | for _, tracedBinary := range u.TracedBinaries { 271 | // an inode conflict is technically possible between multiple mount points, but checking the binary size and 272 | // the inode makes it relatively unlikely, and is less overkill than hashing the file. (we don't want to check 273 | // the path, or even the resolved paths because of hard link collisions) 274 | if stat.Ino == tracedBinary.Inode && stat.Size == tracedBinary.Size { 275 | // if a pid is provided, this means that we filter the events from this binary by pid, add it to the list 276 | if pid != 0 { 277 | tracedBinary.Pids = append(tracedBinary.Pids, pid) 278 | } 279 | return nil 280 | } 281 | } 282 | 283 | // if we reach this point, this is a new entry, add it to the list and generate a cookie 284 | cookie := rand.Uint32() 285 | for _, ok = u.TracedBinaries[BinaryCookie(cookie)]; ok; { 286 | cookie = rand.Uint32() 287 | } 288 | entry := TracedBinary{ 289 | Path: path, 290 | ResolvedPath: resolvedPath, 291 | Inode: stat.Ino, 292 | Size: stat.Size, 293 | Cookie: BinaryCookie(cookie), 294 | symbolsCache: make(map[SymbolAddr]elf.Symbol), 295 | symbolNameToFuncID: make(map[string]FuncID), 296 | } 297 | if pid > 0 { 298 | entry.Pids = []int{pid} 299 | } 300 | 301 | // fetch the list of symbols of the binary 302 | f, syms, err := manager.OpenAndListSymbols(entry.Path) 303 | if err != nil { 304 | return err 305 | } 306 | 307 | entry.file = f 308 | for _, sym := range syms { 309 | entry.symbolsCache[SymbolAddr(sym.Value)] = sym 310 | } 311 | 312 | u.TracedBinaries[entry.Cookie] = &entry 313 | return nil 314 | } 315 | 316 | func (u *UTrace) generateTracedBinaries() error { 317 | var err error 318 | for _, binary := range u.options.Binary { 319 | if err = u.insertTracedBinary(binary, 0); err != nil { 320 | return err 321 | } 322 | } 323 | 324 | for _, pid := range u.options.PIDFilter { 325 | if err = u.insertTracedBinary(fmt.Sprintf("/proc/%d/exe", pid), pid); err != nil { 326 | return err 327 | } 328 | } 329 | return nil 330 | } 331 | 332 | func (u *UTrace) start() error { 333 | // fetch ebpf assets 334 | buf, err := assets.Asset("/probe.o") 335 | if err != nil { 336 | return fmt.Errorf("couldn't find asset: %w", err) 337 | } 338 | 339 | // setup a default manager 340 | u.setupDefaultManager() 341 | 342 | if err = u.generateTracedBinaries(); err != nil { 343 | return fmt.Errorf("couldn't generate the list of traced binaries: %w", err) 344 | } 345 | 346 | // generate uprobes if a binary file is provided 347 | if u.options.FuncPattern != nil { 348 | for _, binary := range u.TracedBinaries { 349 | if err = u.generateUProbes(binary); err != nil { 350 | return fmt.Errorf("couldn't generate uprobes: %w", err) 351 | } 352 | } 353 | } 354 | 355 | // setup kprobes if a kernel function pattern was provided 356 | if u.options.KernelFuncPattern != nil { 357 | if err = u.generateKProbes(); err != nil { 358 | return fmt.Errorf("couldn't generate kprobes: %w", err) 359 | } 360 | } 361 | 362 | // setup tracepoint probes 363 | if len(u.options.Tracepoints) > 0 { 364 | if err = u.generateTracepoints(); err != nil { 365 | return fmt.Errorf("couldn't generate tracepoints: %w", err) 366 | } 367 | } 368 | 369 | // setup perf events 370 | if len(u.options.PerfEvents) > 0 { 371 | if len(u.TracedBinaries) == 0 { 372 | if err = u.generatePerfEvents(nil); err != nil { 373 | return fmt.Errorf("couldn't generate perf events: %w", err) 374 | } 375 | } else { 376 | for _, binary := range u.TracedBinaries { 377 | if err = u.generatePerfEvents(binary); err != nil { 378 | return fmt.Errorf("couldn't generate perf events: %w", err) 379 | } 380 | } 381 | } 382 | } 383 | 384 | if len(u.funcCache) == 0 { 385 | return fmt.Errorf("nothing matched the provided pattern(s)") 386 | } 387 | 388 | u.managerOptions.MapSpecEditors["counters"] = manager.MapSpecEditor{ 389 | Type: ebpf.PerCPUArray, 390 | MaxEntries: uint32(len(u.funcCache)), 391 | EditorFlag: manager.EditMaxEntries, 392 | } 393 | 394 | // initialize the manager 395 | if err = u.manager.InitWithOptions(bytes.NewReader(buf), u.managerOptions); err != nil { 396 | return fmt.Errorf("couldn't init manager: %w", err) 397 | } 398 | 399 | // select kernel space maps 400 | if err = u.selectMaps(); err != nil { 401 | return err 402 | } 403 | 404 | // push kernel filters 405 | if err = u.pushKernelFilters(); err != nil { 406 | return err 407 | } 408 | 409 | // start the manager 410 | if err = u.manager.Start(); err != nil { 411 | return errors.Wrap(err, "couldn't start manager") 412 | } 413 | 414 | u.startTime = time.Now() 415 | return nil 416 | } 417 | 418 | func (u *UTrace) pushKernelFilters() error { 419 | // insert binary path in kernel space to track binary executions 420 | for cookie, binary := range u.TracedBinaries { 421 | if len(binary.Pids) == 0 { 422 | // track process executions using the binary path 423 | pathB := [PathMax]byte{} 424 | copy(pathB[:], binary.Path) 425 | if err := u.binaryPathMap.Put(pathB, uint32(cookie)); err != nil { 426 | _ = u.manager.Stop(manager.CleanAll) 427 | return fmt.Errorf("failed to insert binary path %s in kernel space: %w", binary.Path, err) 428 | } 429 | } else { 430 | // we're tracking specific pids, insert them now 431 | for _, pid := range binary.Pids { 432 | if err := u.tracedPIDsMap.Put(uint32(pid), uint32(binary.Cookie)); err != nil { 433 | _ = u.manager.Stop(manager.CleanAll) 434 | return fmt.Errorf("failed to insert PID filter for binary %s: %w", binary.Path, err) 435 | } 436 | } 437 | } 438 | } 439 | return nil 440 | } 441 | 442 | func (u *UTrace) generateUProbes(binary *TracedBinary) error { 443 | if u.options.FuncPattern == nil || binary == nil { 444 | return nil 445 | } 446 | 447 | // from the entire list of symbols, only keep the functions that match the provided pattern 448 | var matches []elf.Symbol 449 | for _, sym := range binary.symbolsCache { 450 | if elf.ST_TYPE(sym.Info) == elf.STT_FUNC && u.options.FuncPattern.MatchString(sym.Name) { 451 | matches = append(matches, sym) 452 | } 453 | } 454 | 455 | if uint32(len(matches)) > MaxUserSymbolsCount { 456 | logrus.Warnf("%d symbols matched the provided pattern, only the first %d symbols will be traced.", len(matches), MaxUserSymbolsCount) 457 | matches = matches[0:MaxUserSymbolsCount] 458 | } 459 | 460 | if len(matches) == 0 { 461 | return fmt.Errorf("no symbol in '%s' match the provided pattern '%s'", binary.Path, u.options.FuncPattern.String()) 462 | } 463 | 464 | // relocate the function address with the base address of the binary 465 | manager.SanitizeUprobeAddresses(binary.file, matches) 466 | 467 | // generate a probe for each traced PID, or a generic one that will match all pids 468 | tracedPIDs := binary.Pids[:] 469 | if len(tracedPIDs) == 0 { 470 | tracedPIDs = []int{0} 471 | } 472 | 473 | var oneOfSelector manager.OneOf 474 | var constantEditors []manager.ConstantEditor 475 | 476 | // configure a probe for each symbol we're going to hook onto 477 | for _, sym := range matches { 478 | escapedName := sanitizeFuncName(sym.Name) 479 | funcID := u.nextFuncID() 480 | 481 | for _, pid := range tracedPIDs { 482 | probeUID := RandomStringWithLen(10) 483 | probe := &manager.Probe{ 484 | ProbeIdentificationPair: manager.ProbeIdentificationPair{ 485 | UID: probeUID, 486 | EBPFSection: "uprobe/utrace", 487 | EBPFFuncName: "uprobe_utrace", 488 | }, 489 | CopyProgram: true, 490 | BinaryPath: binary.Path, 491 | UprobeOffset: sym.Value, 492 | MatchFuncName: fmt.Sprintf(`^%s$`, escapedName), 493 | } 494 | u.manager.Probes = append(u.manager.Probes, probe) 495 | oneOfSelector.Selectors = append(oneOfSelector.Selectors, &manager.ProbeSelector{ 496 | ProbeIdentificationPair: probe.ProbeIdentificationPair, 497 | }) 498 | constantEditors = append(constantEditors, manager.ConstantEditor{ 499 | Name: "func_id", 500 | Value: uint64(funcID), 501 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 502 | probe.ProbeIdentificationPair, 503 | }, 504 | }) 505 | if pid > 0 { 506 | probe.PerfEventPID = pid 507 | } 508 | 509 | if u.options.Latency { 510 | retProbe := &manager.Probe{ 511 | ProbeIdentificationPair: manager.ProbeIdentificationPair{ 512 | UID: probeUID, 513 | EBPFSection: "uretprobe/utrace", 514 | EBPFFuncName: "uretprobe_utrace", 515 | }, 516 | CopyProgram: true, 517 | BinaryPath: binary.Path, 518 | UprobeOffset: sym.Value, 519 | MatchFuncName: fmt.Sprintf(`^%s$`, escapedName), 520 | } 521 | u.manager.Probes = append(u.manager.Probes, retProbe) 522 | oneOfSelector.Selectors = append(oneOfSelector.Selectors, &manager.ProbeSelector{ 523 | ProbeIdentificationPair: retProbe.ProbeIdentificationPair, 524 | }) 525 | constantEditors = append(constantEditors, manager.ConstantEditor{ 526 | Name: "func_id", 527 | Value: uint64(funcID), 528 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 529 | retProbe.ProbeIdentificationPair, 530 | }, 531 | }) 532 | if pid > 0 { 533 | retProbe.PerfEventPID = pid 534 | } 535 | } 536 | 537 | u.funcCache[funcID] = TracedSymbol{symbol: sym, binary: binary} 538 | binary.symbolNameToFuncID[sym.Name] = funcID 539 | } 540 | } 541 | 542 | u.managerOptions.ActivatedProbes = append(u.managerOptions.ActivatedProbes, &oneOfSelector) 543 | u.managerOptions.ConstantEditors = append(u.managerOptions.ConstantEditors, constantEditors...) 544 | 545 | return nil 546 | } 547 | 548 | func (u *UTrace) parseKallsyms() error { 549 | if len(u.kallsymsCache) > 0 { 550 | // this has already been done 551 | return nil 552 | } 553 | 554 | kallsymsRaw, err := ioutil.ReadFile("/proc/kallsyms") 555 | if err != nil { 556 | return err 557 | } 558 | 559 | var kallsyms []*elf.Symbol 560 | for _, sym := range strings.Split(string(kallsymsRaw), "\n") { 561 | splittedSym := strings.Split(sym, " ") 562 | if len(splittedSym) != 3 { 563 | continue 564 | } 565 | if splittedSym[1] != "T" && splittedSym[1] != "t" { 566 | continue 567 | } 568 | addr, err := strconv.ParseUint(splittedSym[0], 16, 64) 569 | if err != nil { 570 | continue 571 | } 572 | splittedName := strings.Split(splittedSym[2], "\t") 573 | kallsyms = append(kallsyms, &elf.Symbol{ 574 | Name: splittedName[0], 575 | Value: addr, 576 | Info: uint8(elf.STT_FUNC), 577 | }) 578 | } 579 | 580 | // compute symbol sizes 581 | kallsymsLen := len(kallsyms) 582 | for i, sym := range kallsyms { 583 | var size uint64 584 | if i < kallsymsLen-1 { 585 | size = kallsyms[i+1].Value - sym.Value 586 | } 587 | sym.Size = size 588 | u.kallsymsCache[SymbolAddr(sym.Value)] = *sym 589 | } 590 | 591 | return nil 592 | } 593 | 594 | func (u *UTrace) generateKProbes() error { 595 | if err := u.parseKallsyms(); err != nil { 596 | return errors.Wrap(err, "couldn't parse /proc/kallsyms") 597 | } 598 | 599 | // from the list of kernel symbols, only keep the functions that match the provided pattern 600 | var matches []elf.Symbol 601 | for _, sym := range u.kallsymsCache { 602 | if elf.ST_TYPE(sym.Info) == elf.STT_FUNC && u.options.KernelFuncPattern.MatchString(sym.Name) { 603 | matches = append(matches, sym) 604 | } 605 | } 606 | 607 | if uint32(len(matches)) > MaxKernelSymbolsCount { 608 | logrus.Warnf("%d kernel symbols matched the provided pattern, only the first %d symbols will be traced.", len(matches), MaxKernelSymbolsCount) 609 | matches = matches[0:MaxKernelSymbolsCount] 610 | } 611 | 612 | // configure a probe for each symbol we're going to hook onto 613 | var oneOfSelector manager.OneOf 614 | var constantEditors []manager.ConstantEditor 615 | for _, sym := range matches { 616 | escapedName := sanitizeFuncName(sym.Name) 617 | funcID := u.nextFuncID() 618 | probeUID := RandomStringWithLen(10) 619 | probe := &manager.Probe{ 620 | ProbeIdentificationPair: manager.ProbeIdentificationPair{ 621 | UID: probeUID, 622 | EBPFSection: "kprobe/utrace", 623 | EBPFFuncName: "kprobe_utrace", 624 | }, 625 | CopyProgram: true, 626 | MatchFuncName: fmt.Sprintf(`^%s$`, escapedName), 627 | } 628 | u.manager.Probes = append(u.manager.Probes, probe) 629 | oneOfSelector.Selectors = append(oneOfSelector.Selectors, &manager.ProbeSelector{ 630 | ProbeIdentificationPair: probe.ProbeIdentificationPair, 631 | }) 632 | constantEditors = append(constantEditors, manager.ConstantEditor{ 633 | Name: "func_id", 634 | Value: uint64(funcID), 635 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 636 | probe.ProbeIdentificationPair, 637 | }, 638 | }) 639 | if len(u.options.Binary) > 0 || len(u.options.PIDFilter) > 0 { 640 | constantEditors = append(constantEditors, manager.ConstantEditor{ 641 | Name: "filter_user_binary", 642 | Value: uint64(1), 643 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 644 | probe.ProbeIdentificationPair, 645 | }, 646 | }) 647 | } 648 | 649 | if u.options.Latency { 650 | retProbe := &manager.Probe{ 651 | ProbeIdentificationPair: manager.ProbeIdentificationPair{ 652 | UID: probeUID, 653 | EBPFSection: "kretprobe/utrace", 654 | EBPFFuncName: "kretprobe_utrace", 655 | }, 656 | CopyProgram: true, 657 | MatchFuncName: fmt.Sprintf(`^%s$`, escapedName), 658 | } 659 | u.manager.Probes = append(u.manager.Probes, retProbe) 660 | oneOfSelector.Selectors = append(oneOfSelector.Selectors, &manager.ProbeSelector{ 661 | ProbeIdentificationPair: retProbe.ProbeIdentificationPair, 662 | }) 663 | constantEditors = append(constantEditors, manager.ConstantEditor{ 664 | Name: "func_id", 665 | Value: uint64(funcID), 666 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 667 | retProbe.ProbeIdentificationPair, 668 | }, 669 | }) 670 | if len(u.options.Binary) > 0 || len(u.options.PIDFilter) > 0 { 671 | constantEditors = append(constantEditors, manager.ConstantEditor{ 672 | Name: "filter_user_binary", 673 | Value: uint64(1), 674 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 675 | retProbe.ProbeIdentificationPair, 676 | }, 677 | }) 678 | } 679 | } 680 | 681 | u.funcCache[funcID] = TracedSymbol{symbol: sym} 682 | u.kernelSymbolNameToFuncID[sym.Name] = funcID 683 | } 684 | 685 | u.managerOptions.ActivatedProbes = append(u.managerOptions.ActivatedProbes, &oneOfSelector) 686 | u.managerOptions.ConstantEditors = append(u.managerOptions.ConstantEditors, constantEditors...) 687 | 688 | return nil 689 | } 690 | 691 | func (u *UTrace) generateTracepoints() error { 692 | if err := u.parseKallsyms(); err != nil { 693 | return errors.Wrap(err, "couldn't parse /proc/kallsyms") 694 | } 695 | 696 | if uint32(len(u.options.Tracepoints)) > MaxKernelSymbolsCount { 697 | logrus.Warnf("only the first %d tracepoints will be traced (out of %d)", MaxKernelSymbolsCount, len(u.options.Tracepoints)) 698 | u.options.Tracepoints = u.options.Tracepoints[0:MaxKernelSymbolsCount] 699 | } 700 | 701 | // configure a probe for each tracepoint we're going to hook onto 702 | var oneOfSelector manager.OneOf 703 | var constantEditors []manager.ConstantEditor 704 | 705 | for _, tracepoint := range u.options.Tracepoints { 706 | funcID := u.nextFuncID() 707 | probeUID := RandomStringWithLen(10) 708 | 709 | tpDef := strings.Split(tracepoint, ":") 710 | if len(tpDef) != 2 { 711 | return fmt.Errorf("'%s' isn't a valid tracepoint (expected format is [category]:[name])", tracepoint) 712 | } 713 | 714 | probe := &manager.Probe{ 715 | ProbeIdentificationPair: manager.ProbeIdentificationPair{ 716 | UID: probeUID, 717 | EBPFSection: "tracepoint/utrace", 718 | EBPFFuncName: "tracepoint_utrace", 719 | }, 720 | CopyProgram: true, 721 | TracepointCategory: tpDef[0], 722 | TracepointName: tpDef[1], 723 | } 724 | u.manager.Probes = append(u.manager.Probes, probe) 725 | oneOfSelector.Selectors = append(oneOfSelector.Selectors, &manager.ProbeSelector{ 726 | ProbeIdentificationPair: probe.ProbeIdentificationPair, 727 | }) 728 | constantEditors = append(constantEditors, manager.ConstantEditor{ 729 | Name: "func_id", 730 | Value: uint64(funcID), 731 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 732 | probe.ProbeIdentificationPair, 733 | }, 734 | }) 735 | if len(u.options.Binary) > 0 || len(u.options.PIDFilter) > 0 { 736 | constantEditors = append(constantEditors, manager.ConstantEditor{ 737 | Name: "filter_user_binary", 738 | Value: uint64(1), 739 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 740 | probe.ProbeIdentificationPair, 741 | }, 742 | }) 743 | } 744 | 745 | // 0xffffffff00000001 is a fake value inserted to make sure that the tracepoint is part of the kernel stack trace 746 | u.funcCache[funcID] = TracedSymbol{symbol: elf.Symbol{Name: tracepoint, Value: 0xffffffff00000001}} 747 | u.kernelSymbolNameToFuncID[tracepoint] = funcID 748 | } 749 | 750 | u.managerOptions.ActivatedProbes = append(u.managerOptions.ActivatedProbes, &oneOfSelector) 751 | u.managerOptions.ConstantEditors = append(u.managerOptions.ConstantEditors, constantEditors...) 752 | 753 | return nil 754 | } 755 | 756 | func (u *UTrace) generatePerfEvents(binary *TracedBinary) error { 757 | if err := u.parseKallsyms(); err != nil { 758 | return errors.Wrap(err, "couldn't parse /proc/kallsyms") 759 | } 760 | 761 | if uint32(len(u.options.PerfEvents)) > MaxKernelSymbolsCount { 762 | logrus.Warnf("only the first %d perf events will be traced (out of %d)", MaxKernelSymbolsCount, len(u.options.PerfEvents)) 763 | u.options.PerfEvents = u.options.PerfEvents[0:MaxKernelSymbolsCount] 764 | } 765 | 766 | // generate a probe for each traced PID, or a generic one that will match all pids 767 | var tracedPIDs []int 768 | if binary != nil { 769 | tracedPIDs = binary.Pids[:] 770 | } 771 | if len(tracedPIDs) == 0 { 772 | tracedPIDs = []int{0} 773 | } 774 | 775 | // configure a probe for each tracepoint we're going to hook onto 776 | var oneOfSelector manager.OneOf 777 | var constantEditors []manager.ConstantEditor 778 | 779 | for _, perfEvent := range u.options.PerfEvents { 780 | funcID := u.nextFuncID() 781 | peDefRaw := strings.Split(perfEvent, ":") 782 | if len(peDefRaw) != 3 { 783 | return fmt.Errorf("'%s' isn't a valid perf event (expected format is [perf_event_type]:[perf_event_name]:[frequency])", perfEvent) 784 | } 785 | peType, err := strconv.Atoi(peDefRaw[0]) 786 | if err != nil { 787 | return fmt.Errorf("'%s' isn't a valid perf event type (expected format is [perf_event_type]:[perf_event_name]:[frequency]): %w", peDefRaw[0], err) 788 | } 789 | peName, err := strconv.Atoi(peDefRaw[1]) 790 | if err != nil { 791 | return fmt.Errorf("'%s' isn't a valid perf event name (expected format is [perf_event_type]:[perf_event_name]:[frequency]): %w", peDefRaw[1], err) 792 | } 793 | peFrequency, err := strconv.Atoi(peDefRaw[2]) 794 | if err != nil { 795 | return fmt.Errorf("'%s' isn't a valid perf event frequency (expected format is [perf_event_type]:[perf_event_name]:[frequency]): %w", peDefRaw[2], err) 796 | } 797 | 798 | for _, pid := range tracedPIDs { 799 | probeUID := RandomStringWithLen(10) 800 | probe := &manager.Probe{ 801 | ProbeIdentificationPair: manager.ProbeIdentificationPair{ 802 | UID: probeUID, 803 | EBPFSection: "perf_event/utrace", 804 | EBPFFuncName: "perf_event_utrace", 805 | }, 806 | CopyProgram: true, 807 | PerfEventType: peType, 808 | PerfEventConfig: peName, 809 | SampleFrequency: peFrequency, 810 | } 811 | u.manager.Probes = append(u.manager.Probes, probe) 812 | oneOfSelector.Selectors = append(oneOfSelector.Selectors, &manager.ProbeSelector{ 813 | ProbeIdentificationPair: probe.ProbeIdentificationPair, 814 | }) 815 | constantEditors = append(constantEditors, manager.ConstantEditor{ 816 | Name: "func_id", 817 | Value: uint64(funcID), 818 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 819 | probe.ProbeIdentificationPair, 820 | }, 821 | }) 822 | if len(u.options.Binary) > 0 || len(u.options.PIDFilter) > 0 { 823 | if pid > 0 { 824 | probe.PerfEventPID = pid 825 | } 826 | constantEditors = append(constantEditors, manager.ConstantEditor{ 827 | Name: "filter_user_binary", 828 | Value: uint64(1), 829 | ProbeIdentificationPairs: []manager.ProbeIdentificationPair{ 830 | probe.ProbeIdentificationPair, 831 | }, 832 | }) 833 | } 834 | } 835 | 836 | // 0xffffffff00000001 is a fake value inserted to make sure that the tracepoint is part of the kernel stack trace 837 | u.funcCache[funcID] = TracedSymbol{symbol: elf.Symbol{Name: perfEvent, Value: 0xffffffff00000001}} 838 | u.kernelSymbolNameToFuncID[perfEvent] = funcID 839 | } 840 | 841 | u.managerOptions.ActivatedProbes = append(u.managerOptions.ActivatedProbes, &oneOfSelector) 842 | u.managerOptions.ConstantEditors = append(u.managerOptions.ConstantEditors, constantEditors...) 843 | 844 | return nil 845 | } 846 | 847 | // ResolveUserSymbolAndOffset returns the symbol of the function in which a given address lives, as well as the offset 848 | // inside that function 849 | func (u *UTrace) ResolveUserSymbolAndOffset(address SymbolAddr, binary *TracedBinary) StackTraceNode { 850 | if binary != nil { 851 | for symbolAddr, symbol := range binary.symbolsCache { 852 | if address >= symbolAddr && address < symbolAddr+SymbolAddr(symbol.Size) { 853 | funcID, ok := binary.symbolNameToFuncID[symbol.Name] 854 | if !ok { 855 | funcID = -1 856 | } 857 | return StackTraceNode{ 858 | Type: User, 859 | Symbol: symbol, 860 | FuncID: funcID, 861 | Offset: address - symbolAddr, 862 | } 863 | } 864 | } 865 | } 866 | 867 | return StackTraceNode{ 868 | Type: User, 869 | Symbol: SymbolNotFound, 870 | FuncID: -1, 871 | Offset: address, 872 | } 873 | } 874 | 875 | // ResolveKernelSymbolAndOffset returns the symbol of the kernel function in which a given address lives, as well as 876 | // the offset inside that function 877 | func (u *UTrace) ResolveKernelSymbolAndOffset(address SymbolAddr) StackTraceNode { 878 | for symbolAddr, symbol := range u.kallsymsCache { 879 | if address >= symbolAddr && address < symbolAddr+SymbolAddr(symbol.Size) { 880 | funcID, ok := u.kernelSymbolNameToFuncID[symbol.Name] 881 | if !ok { 882 | funcID = -1 883 | } 884 | return StackTraceNode{ 885 | Type: Kernel, 886 | Symbol: symbol, 887 | FuncID: funcID, 888 | Offset: address - symbolAddr, 889 | } 890 | } 891 | } 892 | return StackTraceNode{ 893 | Type: Kernel, 894 | Symbol: SymbolNotFound, 895 | FuncID: -1, 896 | Offset: address, 897 | } 898 | } 899 | 900 | // TraceEventsHandler handles perf events from the kernel 901 | func (u *UTrace) TraceEventsHandler(Cpu int, data []byte, perfMap *manager.PerfMap, m *manager.Manager) { 902 | var evt TraceEvent 903 | _, err := evt.UnmarshalBinary(data) 904 | if err != nil { 905 | logrus.Warnf("couldn't parse data (%d): %v", len(data), err) 906 | return 907 | } 908 | 909 | // parse the collected stack straces 910 | userTrace := make([]SymbolAddr, 127) 911 | kernelTrace := make([]SymbolAddr, 127) 912 | if evt.UserStackID > 0 { 913 | if err = u.stackTracesMap.Lookup(evt.UserStackID, userTrace); err != nil { 914 | logrus.Warnf("couldn't find stack trace %d: %v", evt.UserStackID, err) 915 | atomic.AddUint64(&u.userStackTraceLost, 1) 916 | } else { 917 | atomic.AddUint64(&u.userStackTraceCounter, 1) 918 | } 919 | } 920 | if evt.KernelStackID > 0 { 921 | if err = u.stackTracesMap.Lookup(evt.KernelStackID, kernelTrace); err != nil { 922 | logrus.Warnf("couldn't find stack trace %d: %v", evt.KernelStackID, err) 923 | atomic.AddUint64(&u.kernelStackTraceLost, 1) 924 | } else { 925 | atomic.AddUint64(&u.kernelStackTraceCounter, 1) 926 | } 927 | } 928 | 929 | // fetch existing stack traces 930 | stackTraces, ok := u.stackTraces[evt.FuncID] 931 | if !ok { 932 | stackTraces = make(map[CombinedID]*StackTrace) 933 | u.stackTraces[evt.FuncID] = stackTraces 934 | } 935 | 936 | // only resolve the stack trace if this is a new one 937 | combinedID := CombinedID(evt.UserStackID)<<32 + CombinedID(evt.KernelStackID) 938 | stackTrace, ok := stackTraces[combinedID] 939 | if ok { 940 | stackTrace.Count += 1 941 | return 942 | } 943 | 944 | // resolve binary 945 | binary := u.TracedBinaries[evt.Cookie] 946 | 947 | // create new stack trace 948 | stackTrace = NewStackTrace(1, binary) 949 | 950 | // resolve user stack trace 951 | for _, addr := range userTrace { 952 | if addr == 0 { 953 | break 954 | } 955 | stackTrace.UserStacktrace = append(stackTrace.UserStacktrace, u.ResolveUserSymbolAndOffset(addr, binary)) 956 | } 957 | 958 | // resolve kernel stack trace 959 | for _, addr := range kernelTrace { 960 | if addr == 0 { 961 | break 962 | } 963 | stackTrace.KernelStackTrace = append(stackTrace.KernelStackTrace, u.ResolveKernelSymbolAndOffset(addr)) 964 | } 965 | // sometimes the kernel does not prepend the traced function in the stack trace. Add it now. 966 | if len(stackTrace.KernelStackTrace) > 0 && stackTrace.KernelStackTrace[0].FuncID != evt.FuncID { 967 | stackTrace.KernelStackTrace = append([]StackTraceNode{ 968 | { 969 | Type: Kernel, 970 | FuncID: evt.FuncID, 971 | Symbol: u.funcCache[evt.FuncID].symbol, 972 | Offset: 0, 973 | }}, stackTrace.KernelStackTrace...) 974 | } 975 | 976 | stackTraces[combinedID] = stackTrace 977 | } 978 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 GUILLAUME FOURNIER 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // +build tools 18 | 19 | package tools 20 | 21 | // Those imports are used to track tool dependencies. 22 | // This is the currently recommended approach: https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 23 | 24 | import ( 25 | _ "github.com/shuLhan/go-bindata/cmd/go-bindata" 26 | ) 27 | --------------------------------------------------------------------------------