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