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