• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Facebook, Inc.
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 #include <linux/bpf.h>
18 #include <linux/perf_event.h>
19 #include <unistd.h>
20 #include <cstdio>
21 #include <cstring>
22 #include <exception>
23 #include <iostream>
24 #include <memory>
25 #include <sstream>
26 #include <utility>
27 #include <vector>
28 
29 #include "bcc_exception.h"
30 #include "bcc_syms.h"
31 #include "bpf_module.h"
32 #include "common.h"
33 #include "libbpf.h"
34 #include "perf_reader.h"
35 #include "syms.h"
36 #include "table_storage.h"
37 #include "usdt.h"
38 
39 #include "BPF.h"
40 
41 namespace ebpf {
42 
uint_to_hex(uint64_t value)43 std::string uint_to_hex(uint64_t value) {
44   std::stringstream ss;
45   ss << std::hex << value;
46   return ss.str();
47 }
48 
sanitize_str(std::string str,bool (* validator)(char),char replacement='_')49 std::string sanitize_str(std::string str, bool (*validator)(char),
50                          char replacement = '_') {
51   for (size_t i = 0; i < str.length(); i++)
52     if (!validator(str[i]))
53       str[i] = replacement;
54   return str;
55 }
56 
init(const std::string & bpf_program,const std::vector<std::string> & cflags,const std::vector<USDT> & usdt)57 StatusTuple BPF::init(const std::string& bpf_program,
58                       const std::vector<std::string>& cflags,
59                       const std::vector<USDT>& usdt) {
60   std::string all_bpf_program;
61 
62   usdt_.reserve(usdt.size());
63   for (const auto& u : usdt) {
64     usdt_.emplace_back(u);
65   }
66   for (auto& u : usdt_) {
67     TRY2(u.init());
68     all_bpf_program += u.program_text_;
69   }
70 
71   auto flags_len = cflags.size();
72   const char* flags[flags_len];
73   for (size_t i = 0; i < flags_len; i++)
74     flags[i] = cflags[i].c_str();
75 
76   all_bpf_program += bpf_program;
77   if (bpf_module_->load_string(all_bpf_program, flags, flags_len) != 0)
78     return StatusTuple(-1, "Unable to initialize BPF program");
79 
80   return StatusTuple(0);
81 };
82 
~BPF()83 BPF::~BPF() {
84   auto res = detach_all();
85   if (res.code() != 0)
86     std::cerr << "Failed to detach all probes on destruction: " << std::endl
87               << res.msg() << std::endl;
88 }
89 
detach_all()90 StatusTuple BPF::detach_all() {
91   bool has_error = false;
92   std::string error_msg;
93 
94   for (auto& it : kprobes_) {
95     auto res = detach_kprobe_event(it.first, it.second);
96     if (res.code() != 0) {
97       error_msg += "Failed to detach kprobe event " + it.first + ": ";
98       error_msg += res.msg() + "\n";
99       has_error = true;
100     }
101   }
102 
103   for (auto& it : uprobes_) {
104     auto res = detach_uprobe_event(it.first, it.second);
105     if (res.code() != 0) {
106       error_msg += "Failed to detach uprobe event " + it.first + ": ";
107       error_msg += res.msg() + "\n";
108       has_error = true;
109     }
110   }
111 
112   for (auto& it : tracepoints_) {
113     auto res = detach_tracepoint_event(it.first, it.second);
114     if (res.code() != 0) {
115       error_msg += "Failed to detach Tracepoint " + it.first + ": ";
116       error_msg += res.msg() + "\n";
117       has_error = true;
118     }
119   }
120 
121   for (auto& it : perf_buffers_) {
122     auto res = it.second->close_all_cpu();
123     if (res.code() != 0) {
124       error_msg += "Failed to close perf buffer " + it.first + ": ";
125       error_msg += res.msg() + "\n";
126       has_error = true;
127     }
128     delete it.second;
129   }
130 
131   for (auto& it : perf_event_arrays_) {
132     auto res = it.second->close_all_cpu();
133     if (res.code() != 0) {
134       error_msg += "Failed to close perf event array " + it.first + ": ";
135       error_msg += res.msg() + "\n";
136       has_error = true;
137     }
138     delete it.second;
139   }
140 
141   for (auto& it : perf_events_) {
142     auto res = detach_perf_event_all_cpu(it.second);
143     if (res.code() != 0) {
144       error_msg += res.msg() + "\n";
145       has_error = true;
146     }
147   }
148 
149   for (auto& it : funcs_) {
150     int res = close(it.second);
151     if (res != 0) {
152       error_msg += "Failed to unload BPF program for " + it.first + ": ";
153       error_msg += std::string(std::strerror(errno)) + "\n";
154       has_error = true;
155     }
156   }
157 
158   if (has_error)
159     return StatusTuple(-1, error_msg);
160   else
161     return StatusTuple(0);
162 }
163 
attach_kprobe(const std::string & kernel_func,const std::string & probe_func,uint64_t kernel_func_offset,bpf_probe_attach_type attach_type)164 StatusTuple BPF::attach_kprobe(const std::string& kernel_func,
165                                const std::string& probe_func,
166                                uint64_t kernel_func_offset,
167                                bpf_probe_attach_type attach_type) {
168   std::string probe_event = get_kprobe_event(kernel_func, attach_type);
169   if (kprobes_.find(probe_event) != kprobes_.end())
170     return StatusTuple(-1, "kprobe %s already attached", probe_event.c_str());
171 
172   int probe_fd;
173   TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
174 
175   int res_fd = bpf_attach_kprobe(probe_fd, attach_type, probe_event.c_str(),
176                                  kernel_func.c_str(), kernel_func_offset);
177 
178   if (res_fd < 0) {
179     TRY2(unload_func(probe_func));
180     return StatusTuple(-1, "Unable to attach %skprobe for %s using %s",
181                        attach_type_debug(attach_type).c_str(),
182                        kernel_func.c_str(), probe_func.c_str());
183   }
184 
185   open_probe_t p = {};
186   p.perf_event_fd = res_fd;
187   p.func = probe_func;
188   kprobes_[probe_event] = std::move(p);
189   return StatusTuple(0);
190 }
191 
attach_uprobe(const std::string & binary_path,const std::string & symbol,const std::string & probe_func,uint64_t symbol_addr,bpf_probe_attach_type attach_type,pid_t pid)192 StatusTuple BPF::attach_uprobe(const std::string& binary_path,
193                                const std::string& symbol,
194                                const std::string& probe_func,
195                                uint64_t symbol_addr,
196                                bpf_probe_attach_type attach_type, pid_t pid) {
197   std::string module;
198   uint64_t offset;
199   TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset));
200 
201   std::string probe_event = get_uprobe_event(module, offset, attach_type, pid);
202   if (uprobes_.find(probe_event) != uprobes_.end())
203     return StatusTuple(-1, "uprobe %s already attached", probe_event.c_str());
204 
205   int probe_fd;
206   TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
207 
208   int res_fd = bpf_attach_uprobe(probe_fd, attach_type, probe_event.c_str(),
209                                  binary_path.c_str(), offset, pid);
210 
211   if (res_fd < 0) {
212     TRY2(unload_func(probe_func));
213     return StatusTuple(
214         -1,
215         "Unable to attach %suprobe for binary %s symbol %s addr %lx using %s\n",
216         attach_type_debug(attach_type).c_str(), binary_path.c_str(),
217         symbol.c_str(), symbol_addr, probe_func.c_str());
218   }
219 
220   open_probe_t p = {};
221   p.perf_event_fd = res_fd;
222   p.func = probe_func;
223   uprobes_[probe_event] = std::move(p);
224   return StatusTuple(0);
225 }
226 
attach_usdt(const USDT & usdt,pid_t pid)227 StatusTuple BPF::attach_usdt(const USDT& usdt, pid_t pid) {
228   for (const auto& u : usdt_) {
229     if (u == usdt) {
230       auto& probe = *static_cast<::USDT::Probe*>(u.probe_.get());
231       if (!probe.enable(u.probe_func_))
232         return StatusTuple(-1, "Unable to enable USDT " + u.print_name());
233 
234       bool failed = false;
235       std::string err_msg;
236       int cnt = 0;
237       for (const auto& loc : probe.locations_) {
238         auto res = attach_uprobe(loc.bin_path_, std::string(), u.probe_func_,
239                                  loc.address_, BPF_PROBE_ENTRY, pid);
240         if (res.code() != 0) {
241           failed = true;
242           err_msg += "USDT " + u.print_name() + " at " + loc.bin_path_ +
243                      " address " + std::to_string(loc.address_);
244           err_msg += ": " + res.msg() + "\n";
245           break;
246         }
247         cnt++;
248       }
249       if (failed) {
250         for (int i = 0; i < cnt; i++) {
251           auto res =
252               detach_uprobe(probe.locations_[i].bin_path_, std::string(),
253                             probe.locations_[i].address_, BPF_PROBE_ENTRY, pid);
254           if (res.code() != 0)
255             err_msg += "During clean up: " + res.msg() + "\n";
256         }
257         return StatusTuple(-1, err_msg);
258       } else {
259         return StatusTuple(0);
260       }
261     }
262   }
263 
264   return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str());
265 }
266 
attach_tracepoint(const std::string & tracepoint,const std::string & probe_func)267 StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
268                                    const std::string& probe_func) {
269   if (tracepoints_.find(tracepoint) != tracepoints_.end())
270     return StatusTuple(-1, "Tracepoint %s already attached",
271                        tracepoint.c_str());
272 
273   auto pos = tracepoint.find(":");
274   if ((pos == std::string::npos) || (pos != tracepoint.rfind(":")))
275     return StatusTuple(-1, "Unable to parse Tracepoint %s", tracepoint.c_str());
276   std::string tp_category = tracepoint.substr(0, pos);
277   std::string tp_name = tracepoint.substr(pos + 1);
278 
279   int probe_fd;
280   TRY2(load_func(probe_func, BPF_PROG_TYPE_TRACEPOINT, probe_fd));
281 
282   int res_fd =
283       bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str());
284 
285   if (res_fd < 0) {
286     TRY2(unload_func(probe_func));
287     return StatusTuple(-1, "Unable to attach Tracepoint %s using %s",
288                        tracepoint.c_str(), probe_func.c_str());
289   }
290 
291   open_probe_t p = {};
292   p.perf_event_fd = res_fd;
293   p.func = probe_func;
294   tracepoints_[tracepoint] = std::move(p);
295   return StatusTuple(0);
296 }
297 
attach_perf_event(uint32_t ev_type,uint32_t ev_config,const std::string & probe_func,uint64_t sample_period,uint64_t sample_freq,pid_t pid,int cpu,int group_fd)298 StatusTuple BPF::attach_perf_event(uint32_t ev_type, uint32_t ev_config,
299                                    const std::string& probe_func,
300                                    uint64_t sample_period, uint64_t sample_freq,
301                                    pid_t pid, int cpu, int group_fd) {
302   auto ev_pair = std::make_pair(ev_type, ev_config);
303   if (perf_events_.find(ev_pair) != perf_events_.end())
304     return StatusTuple(-1, "Perf event type %d config %d already attached",
305                        ev_type, ev_config);
306 
307   int probe_fd;
308   TRY2(load_func(probe_func, BPF_PROG_TYPE_PERF_EVENT, probe_fd));
309 
310   std::vector<int> cpus;
311   if (cpu >= 0)
312     cpus.push_back(cpu);
313   else
314     cpus = get_online_cpus();
315   auto fds = new std::vector<std::pair<int, int>>();
316   fds->reserve(cpus.size());
317   for (int i : cpus) {
318     int fd = bpf_attach_perf_event(probe_fd, ev_type, ev_config, sample_period,
319                                    sample_freq, pid, i, group_fd);
320     if (fd < 0) {
321       for (const auto& it : *fds)
322         close(it.second);
323       delete fds;
324       TRY2(unload_func(probe_func));
325       return StatusTuple(-1, "Failed to attach perf event type %d config %d",
326                          ev_type, ev_config);
327     }
328     fds->emplace_back(i, fd);
329   }
330 
331   open_probe_t p = {};
332   p.func = probe_func;
333   p.per_cpu_fd = fds;
334   perf_events_[ev_pair] = std::move(p);
335   return StatusTuple(0);
336 }
337 
attach_perf_event_raw(void * perf_event_attr,const std::string & probe_func,pid_t pid,int cpu,int group_fd,unsigned long extra_flags)338 StatusTuple BPF::attach_perf_event_raw(void* perf_event_attr,
339                                        const std::string& probe_func, pid_t pid,
340                                        int cpu, int group_fd,
341                                        unsigned long extra_flags) {
342   auto attr = static_cast<struct perf_event_attr*>(perf_event_attr);
343   auto ev_pair = std::make_pair(attr->type, attr->config);
344   if (perf_events_.find(ev_pair) != perf_events_.end())
345     return StatusTuple(-1, "Perf event type %d config %d already attached",
346                        attr->type, attr->config);
347 
348   int probe_fd;
349   TRY2(load_func(probe_func, BPF_PROG_TYPE_PERF_EVENT, probe_fd));
350 
351   std::vector<int> cpus;
352   if (cpu >= 0)
353     cpus.push_back(cpu);
354   else
355     cpus = get_online_cpus();
356   auto fds = new std::vector<std::pair<int, int>>();
357   fds->reserve(cpus.size());
358   for (int i : cpus) {
359     int fd = bpf_attach_perf_event_raw(probe_fd, attr, pid, i, group_fd,
360                                        extra_flags);
361     if (fd < 0) {
362       for (const auto& it : *fds)
363         close(it.second);
364       delete fds;
365       TRY2(unload_func(probe_func));
366       return StatusTuple(-1, "Failed to attach perf event type %d config %d",
367                          attr->type, attr->config);
368     }
369     fds->emplace_back(i, fd);
370   }
371 
372   open_probe_t p = {};
373   p.func = probe_func;
374   p.per_cpu_fd = fds;
375   perf_events_[ev_pair] = std::move(p);
376   return StatusTuple(0);
377 }
378 
detach_kprobe(const std::string & kernel_func,bpf_probe_attach_type attach_type)379 StatusTuple BPF::detach_kprobe(const std::string& kernel_func,
380                                bpf_probe_attach_type attach_type) {
381   std::string event = get_kprobe_event(kernel_func, attach_type);
382 
383   auto it = kprobes_.find(event);
384   if (it == kprobes_.end())
385     return StatusTuple(-1, "No open %skprobe for %s",
386                        attach_type_debug(attach_type).c_str(),
387                        kernel_func.c_str());
388 
389   TRY2(detach_kprobe_event(it->first, it->second));
390   kprobes_.erase(it);
391   return StatusTuple(0);
392 }
393 
detach_uprobe(const std::string & binary_path,const std::string & symbol,uint64_t symbol_addr,bpf_probe_attach_type attach_type,pid_t pid)394 StatusTuple BPF::detach_uprobe(const std::string& binary_path,
395                                const std::string& symbol, uint64_t symbol_addr,
396                                bpf_probe_attach_type attach_type, pid_t pid) {
397   std::string module;
398   uint64_t offset;
399   TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset));
400 
401   std::string event = get_uprobe_event(module, offset, attach_type, pid);
402   auto it = uprobes_.find(event);
403   if (it == uprobes_.end())
404     return StatusTuple(-1, "No open %suprobe for binary %s symbol %s addr %lx",
405                        attach_type_debug(attach_type).c_str(),
406                        binary_path.c_str(), symbol.c_str(), symbol_addr);
407 
408   TRY2(detach_uprobe_event(it->first, it->second));
409   uprobes_.erase(it);
410   return StatusTuple(0);
411 }
412 
detach_usdt(const USDT & usdt,pid_t pid)413 StatusTuple BPF::detach_usdt(const USDT& usdt, pid_t pid) {
414   for (const auto& u : usdt_) {
415     if (u == usdt) {
416       auto& probe = *static_cast<::USDT::Probe*>(u.probe_.get());
417       bool failed = false;
418       std::string err_msg;
419       for (const auto& loc : probe.locations_) {
420         auto res = detach_uprobe(loc.bin_path_, std::string(), loc.address_,
421                                  BPF_PROBE_ENTRY, pid);
422         if (res.code() != 0) {
423           failed = true;
424           err_msg += "USDT " + u.print_name() + " at " + loc.bin_path_ +
425                      " address " + std::to_string(loc.address_);
426           err_msg += ": " + res.msg() + "\n";
427         }
428       }
429 
430       if (!probe.disable()) {
431         failed = true;
432         err_msg += "Unable to disable USDT " + u.print_name();
433       }
434 
435       if (failed)
436         return StatusTuple(-1, err_msg);
437       else
438         return StatusTuple(0);
439     }
440   }
441 
442   return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str());
443 }
444 
detach_tracepoint(const std::string & tracepoint)445 StatusTuple BPF::detach_tracepoint(const std::string& tracepoint) {
446   auto it = tracepoints_.find(tracepoint);
447   if (it == tracepoints_.end())
448     return StatusTuple(-1, "No open Tracepoint %s", tracepoint.c_str());
449 
450   TRY2(detach_tracepoint_event(it->first, it->second));
451   tracepoints_.erase(it);
452   return StatusTuple(0);
453 }
454 
detach_perf_event(uint32_t ev_type,uint32_t ev_config)455 StatusTuple BPF::detach_perf_event(uint32_t ev_type, uint32_t ev_config) {
456   auto it = perf_events_.find(std::make_pair(ev_type, ev_config));
457   if (it == perf_events_.end())
458     return StatusTuple(-1, "Perf Event type %d config %d not attached", ev_type,
459                        ev_config);
460   TRY2(detach_perf_event_all_cpu(it->second));
461   perf_events_.erase(it);
462   return StatusTuple(0);
463 }
464 
detach_perf_event_raw(void * perf_event_attr)465 StatusTuple BPF::detach_perf_event_raw(void* perf_event_attr) {
466   auto attr = static_cast<struct perf_event_attr*>(perf_event_attr);
467   return detach_perf_event(attr->type, attr->config);
468 }
469 
open_perf_event(const std::string & name,uint32_t type,uint64_t config)470 StatusTuple BPF::open_perf_event(const std::string& name, uint32_t type,
471                                  uint64_t config) {
472   if (perf_event_arrays_.find(name) == perf_event_arrays_.end()) {
473     TableStorage::iterator it;
474     if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
475       return StatusTuple(-1, "open_perf_event: unable to find table_storage %s",
476                          name.c_str());
477     perf_event_arrays_[name] = new BPFPerfEventArray(it->second);
478   }
479   auto table = perf_event_arrays_[name];
480   TRY2(table->open_all_cpu(type, config));
481   return StatusTuple(0);
482 }
483 
close_perf_event(const std::string & name)484 StatusTuple BPF::close_perf_event(const std::string& name) {
485   auto it = perf_event_arrays_.find(name);
486   if (it == perf_event_arrays_.end())
487     return StatusTuple(-1, "Perf Event for %s not open", name.c_str());
488   TRY2(it->second->close_all_cpu());
489   return StatusTuple(0);
490 }
491 
open_perf_buffer(const std::string & name,perf_reader_raw_cb cb,perf_reader_lost_cb lost_cb,void * cb_cookie,int page_cnt)492 StatusTuple BPF::open_perf_buffer(const std::string& name,
493                                   perf_reader_raw_cb cb,
494                                   perf_reader_lost_cb lost_cb, void* cb_cookie,
495                                   int page_cnt) {
496   if (perf_buffers_.find(name) == perf_buffers_.end()) {
497     TableStorage::iterator it;
498     if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
499       return StatusTuple(-1,
500                          "open_perf_buffer: unable to find table_storage %s",
501                          name.c_str());
502     perf_buffers_[name] = new BPFPerfBuffer(it->second);
503   }
504   if ((page_cnt & (page_cnt - 1)) != 0)
505     return StatusTuple(-1, "open_perf_buffer page_cnt must be a power of two");
506   auto table = perf_buffers_[name];
507   TRY2(table->open_all_cpu(cb, lost_cb, cb_cookie, page_cnt));
508   return StatusTuple(0);
509 }
510 
close_perf_buffer(const std::string & name)511 StatusTuple BPF::close_perf_buffer(const std::string& name) {
512   auto it = perf_buffers_.find(name);
513   if (it == perf_buffers_.end())
514     return StatusTuple(-1, "Perf buffer for %s not open", name.c_str());
515   TRY2(it->second->close_all_cpu());
516   return StatusTuple(0);
517 }
518 
get_perf_buffer(const std::string & name)519 BPFPerfBuffer* BPF::get_perf_buffer(const std::string& name) {
520   auto it = perf_buffers_.find(name);
521   return (it == perf_buffers_.end()) ? nullptr : it->second;
522 }
523 
poll_perf_buffer(const std::string & name,int timeout_ms)524 int BPF::poll_perf_buffer(const std::string& name, int timeout_ms) {
525   auto it = perf_buffers_.find(name);
526   if (it == perf_buffers_.end())
527     return -1;
528   return it->second->poll(timeout_ms);
529 }
530 
load_func(const std::string & func_name,bpf_prog_type type,int & fd)531 StatusTuple BPF::load_func(const std::string& func_name, bpf_prog_type type,
532                            int& fd) {
533   if (funcs_.find(func_name) != funcs_.end()) {
534     fd = funcs_[func_name];
535     return StatusTuple(0);
536   }
537 
538   uint8_t* func_start = bpf_module_->function_start(func_name);
539   if (!func_start)
540     return StatusTuple(-1, "Can't find start of function %s",
541                        func_name.c_str());
542   size_t func_size = bpf_module_->function_size(func_name);
543 
544   int log_level = 0;
545   if (flag_ & DEBUG_BPF_REGISTER_STATE)
546     log_level = 2;
547   else if (flag_ & DEBUG_BPF)
548     log_level = 1;
549 
550   fd = bpf_prog_load(type, func_name.c_str(),
551                      reinterpret_cast<struct bpf_insn*>(func_start), func_size,
552                      bpf_module_->license(), bpf_module_->kern_version(),
553                      log_level, nullptr, 0);
554 
555   if (fd < 0)
556     return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd);
557 
558   bpf_module_->annotate_prog_tag(
559       func_name, fd, reinterpret_cast<struct bpf_insn*>(func_start), func_size);
560   funcs_[func_name] = fd;
561   return StatusTuple(0);
562 }
563 
unload_func(const std::string & func_name)564 StatusTuple BPF::unload_func(const std::string& func_name) {
565   auto it = funcs_.find(func_name);
566   if (it == funcs_.end())
567     return StatusTuple(0);
568 
569   int res = close(it->second);
570   if (res != 0)
571     return StatusTuple(-1, "Can't close FD for %s: %d", it->first.c_str(), res);
572 
573   funcs_.erase(it);
574   return StatusTuple(0);
575 }
576 
get_syscall_fnname(const std::string & name)577 std::string BPF::get_syscall_fnname(const std::string& name) {
578   if (syscall_prefix_ == nullptr) {
579     KSyms ksym;
580     uint64_t addr;
581 
582     if (ksym.resolve_name(nullptr, "sys_bpf", &addr))
583       syscall_prefix_.reset(new std::string("sys_"));
584     else if (ksym.resolve_name(nullptr, "__x64_sys_bpf", &addr))
585       syscall_prefix_.reset(new std::string("__x64_sys_"));
586     else
587       syscall_prefix_.reset(new std::string());
588   }
589 
590   return *syscall_prefix_ + name;
591 }
592 
check_binary_symbol(const std::string & binary_path,const std::string & symbol,uint64_t symbol_addr,std::string & module_res,uint64_t & offset_res)593 StatusTuple BPF::check_binary_symbol(const std::string& binary_path,
594                                      const std::string& symbol,
595                                      uint64_t symbol_addr,
596                                      std::string& module_res,
597                                      uint64_t& offset_res) {
598   bcc_symbol output;
599   int res = bcc_resolve_symname(binary_path.c_str(), symbol.c_str(),
600                                 symbol_addr, -1, nullptr, &output);
601   if (res < 0)
602     return StatusTuple(
603         -1, "Unable to find offset for binary %s symbol %s address %lx",
604         binary_path.c_str(), symbol.c_str(), symbol_addr);
605 
606   if (output.module) {
607     module_res = output.module;
608     ::free(const_cast<char*>(output.module));
609   } else {
610     module_res = "";
611   }
612   offset_res = output.offset;
613   return StatusTuple(0);
614 }
615 
get_kprobe_event(const std::string & kernel_func,bpf_probe_attach_type type)616 std::string BPF::get_kprobe_event(const std::string& kernel_func,
617                                   bpf_probe_attach_type type) {
618   std::string res = attach_type_prefix(type) + "_";
619   res += sanitize_str(kernel_func, &BPF::kprobe_event_validator);
620   return res;
621 }
622 
get_prog_table(const std::string & name)623 BPFProgTable BPF::get_prog_table(const std::string& name) {
624   TableStorage::iterator it;
625   if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
626     return BPFProgTable(it->second);
627   return BPFProgTable({});
628 }
629 
get_cgroup_array(const std::string & name)630 BPFCgroupArray BPF::get_cgroup_array(const std::string& name) {
631   TableStorage::iterator it;
632   if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
633     return BPFCgroupArray(it->second);
634   return BPFCgroupArray({});
635 }
636 
get_devmap_table(const std::string & name)637 BPFDevmapTable BPF::get_devmap_table(const std::string& name) {
638   TableStorage::iterator it;
639   if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
640     return BPFDevmapTable(it->second);
641   return BPFDevmapTable({});
642 }
643 
get_stack_table(const std::string & name,bool use_debug_file,bool check_debug_file_crc)644 BPFStackTable BPF::get_stack_table(const std::string& name, bool use_debug_file,
645                                    bool check_debug_file_crc) {
646   TableStorage::iterator it;
647   if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
648     return BPFStackTable(it->second, use_debug_file, check_debug_file_crc);
649   return BPFStackTable({}, use_debug_file, check_debug_file_crc);
650 }
651 
get_uprobe_event(const std::string & binary_path,uint64_t offset,bpf_probe_attach_type type,pid_t pid)652 std::string BPF::get_uprobe_event(const std::string& binary_path,
653                                   uint64_t offset, bpf_probe_attach_type type,
654                                   pid_t pid) {
655   std::string res = attach_type_prefix(type) + "_";
656   res += sanitize_str(binary_path, &BPF::uprobe_path_validator);
657   res += "_0x" + uint_to_hex(offset);
658   if (pid != -1)
659     res += "_" + std::to_string(pid);
660   return res;
661 }
662 
detach_kprobe_event(const std::string & event,open_probe_t & attr)663 StatusTuple BPF::detach_kprobe_event(const std::string& event,
664                                      open_probe_t& attr) {
665   bpf_close_perf_event_fd(attr.perf_event_fd);
666   TRY2(unload_func(attr.func));
667   if (bpf_detach_kprobe(event.c_str()) < 0)
668     return StatusTuple(-1, "Unable to detach kprobe %s", event.c_str());
669   return StatusTuple(0);
670 }
671 
detach_uprobe_event(const std::string & event,open_probe_t & attr)672 StatusTuple BPF::detach_uprobe_event(const std::string& event,
673                                      open_probe_t& attr) {
674   bpf_close_perf_event_fd(attr.perf_event_fd);
675   TRY2(unload_func(attr.func));
676   if (bpf_detach_uprobe(event.c_str()) < 0)
677     return StatusTuple(-1, "Unable to detach uprobe %s", event.c_str());
678   return StatusTuple(0);
679 }
680 
detach_tracepoint_event(const std::string & tracepoint,open_probe_t & attr)681 StatusTuple BPF::detach_tracepoint_event(const std::string& tracepoint,
682                                          open_probe_t& attr) {
683   bpf_close_perf_event_fd(attr.perf_event_fd);
684   TRY2(unload_func(attr.func));
685 
686   // TODO: bpf_detach_tracepoint currently does nothing.
687   return StatusTuple(0);
688 }
689 
detach_perf_event_all_cpu(open_probe_t & attr)690 StatusTuple BPF::detach_perf_event_all_cpu(open_probe_t& attr) {
691   bool has_error = false;
692   std::string err_msg;
693   for (const auto& it : *attr.per_cpu_fd) {
694     int res = bpf_close_perf_event_fd(it.second);
695     if (res != 0) {
696       has_error = true;
697       err_msg += "Failed to close perf event FD " + std::to_string(it.second) +
698                  " For CPU " + std::to_string(it.first) + ": ";
699       err_msg += std::string(std::strerror(errno)) + "\n";
700     }
701   }
702   delete attr.per_cpu_fd;
703   TRY2(unload_func(attr.func));
704 
705   if (has_error)
706     return StatusTuple(-1, err_msg);
707   return StatusTuple(0);
708 }
709 
USDT(const std::string & binary_path,const std::string & provider,const std::string & name,const std::string & probe_func)710 USDT::USDT(const std::string& binary_path, const std::string& provider,
711            const std::string& name, const std::string& probe_func)
712     : initialized_(false),
713       binary_path_(binary_path),
714       pid_(-1),
715       provider_(provider),
716       name_(name),
717       probe_func_(probe_func) {}
718 
USDT(pid_t pid,const std::string & provider,const std::string & name,const std::string & probe_func)719 USDT::USDT(pid_t pid, const std::string& provider, const std::string& name,
720            const std::string& probe_func)
721     : initialized_(false),
722       binary_path_(),
723       pid_(pid),
724       provider_(provider),
725       name_(name),
726       probe_func_(probe_func) {}
727 
USDT(const std::string & binary_path,pid_t pid,const std::string & provider,const std::string & name,const std::string & probe_func)728 USDT::USDT(const std::string& binary_path, pid_t pid,
729            const std::string& provider, const std::string& name,
730            const std::string& probe_func)
731     : initialized_(false),
732       binary_path_(binary_path),
733       pid_(pid),
734       provider_(provider),
735       name_(name),
736       probe_func_(probe_func) {}
737 
USDT(const USDT & usdt)738 USDT::USDT(const USDT& usdt)
739     : initialized_(false),
740       binary_path_(usdt.binary_path_),
741       pid_(usdt.pid_),
742       provider_(usdt.provider_),
743       name_(usdt.name_),
744       probe_func_(usdt.probe_func_) {}
745 
USDT(USDT && usdt)746 USDT::USDT(USDT&& usdt) noexcept
747     : initialized_(usdt.initialized_),
748       binary_path_(std::move(usdt.binary_path_)),
749       pid_(usdt.pid_),
750       provider_(std::move(usdt.provider_)),
751       name_(std::move(usdt.name_)),
752       probe_func_(std::move(usdt.probe_func_)),
753       probe_(std::move(usdt.probe_)),
754       program_text_(std::move(usdt.program_text_)) {
755   usdt.initialized_ = false;
756 }
757 
operator ==(const USDT & other) const758 bool USDT::operator==(const USDT& other) const {
759   return (provider_ == other.provider_) && (name_ == other.name_) &&
760          (binary_path_ == other.binary_path_) && (pid_ == other.pid_) &&
761          (probe_func_ == other.probe_func_);
762 }
763 
init()764 StatusTuple USDT::init() {
765   std::unique_ptr<::USDT::Context> ctx;
766   if (!binary_path_.empty() && pid_ > 0)
767     ctx.reset(new ::USDT::Context(pid_, binary_path_));
768   else if (!binary_path_.empty())
769     ctx.reset(new ::USDT::Context(binary_path_));
770   else if (pid_ > 0)
771     ctx.reset(new ::USDT::Context(pid_));
772   else
773     return StatusTuple(-1, "No valid Binary Path or PID provided");
774 
775   if (!ctx->loaded())
776     return StatusTuple(-1, "Unable to load USDT " + print_name());
777 
778   auto deleter = [](void* probe) { delete static_cast<::USDT::Probe*>(probe); };
779   for (auto& p : ctx->probes_) {
780     if (p->provider_ == provider_ && p->name_ == name_) {
781       // Take ownership of the probe that we are interested in, and avoid it
782       // being destrcuted when we destruct the USDT::Context instance
783       probe_ = std::unique_ptr<void, std::function<void(void*)>>(p.release(),
784                                                                  deleter);
785       p.swap(ctx->probes_.back());
786       ctx->probes_.pop_back();
787       break;
788     }
789   }
790   if (!probe_)
791     return StatusTuple(-1, "Unable to find USDT " + print_name());
792   ctx.reset(nullptr);
793   auto& probe = *static_cast<::USDT::Probe*>(probe_.get());
794 
795   std::ostringstream stream;
796   if (!probe.usdt_getarg(stream, probe_func_))
797     return StatusTuple(
798         -1, "Unable to generate program text for USDT " + print_name());
799   program_text_ = ::USDT::USDT_PROGRAM_HEADER + stream.str();
800 
801   initialized_ = true;
802   return StatusTuple(0);
803 }
804 
805 }  // namespace ebpf
806