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