1 /* 2 * Copyright (c) 2016 GitHub, 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 #include <algorithm> 17 #include <cstring> 18 #include <sstream> 19 #include <unordered_set> 20 21 #include <fcntl.h> 22 #include <sys/stat.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 26 #include "bcc_elf.h" 27 #include "bcc_proc.h" 28 #include "common.h" 29 #include "usdt.h" 30 #include "vendor/tinyformat.hpp" 31 #include "bcc_usdt.h" 32 33 namespace USDT { 34 35 Location::Location(uint64_t addr, const std::string &bin_path, const char *arg_fmt) 36 : address_(addr), 37 bin_path_(bin_path) { 38 39 #ifdef __aarch64__ 40 ArgumentParser_aarch64 parser(arg_fmt); 41 #elif __powerpc64__ 42 ArgumentParser_powerpc64 parser(arg_fmt); 43 #else 44 ArgumentParser_x64 parser(arg_fmt); 45 #endif 46 while (!parser.done()) { 47 Argument arg; 48 if (!parser.parse(&arg)) 49 continue; 50 arguments_.push_back(std::move(arg)); 51 } 52 } 53 54 Probe::Probe(const char *bin_path, const char *provider, const char *name, 55 uint64_t semaphore, const optional<int> &pid, ProcMountNS *ns) 56 : bin_path_(bin_path), 57 provider_(provider), 58 name_(name), 59 semaphore_(semaphore), 60 pid_(pid), 61 mount_ns_(ns) {} 62 63 bool Probe::in_shared_object(const std::string &bin_path) { 64 if (object_type_map_.find(bin_path) == object_type_map_.end()) { 65 ProcMountNSGuard g(mount_ns_); 66 return (object_type_map_[bin_path] = bcc_elf_is_shared_obj(bin_path.c_str())); 67 } 68 return object_type_map_[bin_path]; 69 } 70 71 bool Probe::resolve_global_address(uint64_t *global, const std::string &bin_path, 72 const uint64_t addr) { 73 if (in_shared_object(bin_path)) { 74 return (pid_ && 75 !bcc_resolve_global_addr(*pid_, bin_path.c_str(), addr, global)); 76 } 77 78 *global = addr; 79 return true; 80 } 81 82 bool Probe::add_to_semaphore(int16_t val) { 83 assert(pid_); 84 85 if (!attached_semaphore_) { 86 uint64_t addr; 87 if (!resolve_global_address(&addr, bin_path_, semaphore_)) 88 return false; 89 attached_semaphore_ = addr; 90 } 91 92 off_t address = static_cast<off_t>(attached_semaphore_.value()); 93 94 std::string procmem = tfm::format("/proc/%d/mem", pid_.value()); 95 int memfd = ::open(procmem.c_str(), O_RDWR); 96 if (memfd < 0) 97 return false; 98 99 int16_t original; 100 101 if (::lseek(memfd, address, SEEK_SET) < 0 || 102 ::read(memfd, &original, 2) != 2) { 103 ::close(memfd); 104 return false; 105 } 106 107 original = original + val; 108 109 if (::lseek(memfd, address, SEEK_SET) < 0 || 110 ::write(memfd, &original, 2) != 2) { 111 ::close(memfd); 112 return false; 113 } 114 115 ::close(memfd); 116 return true; 117 } 118 119 bool Probe::enable(const std::string &fn_name) { 120 if (attached_to_) 121 return false; 122 123 if (need_enable()) { 124 if (!pid_) 125 return false; 126 127 if (!add_to_semaphore(+1)) 128 return false; 129 } 130 131 attached_to_ = fn_name; 132 return true; 133 } 134 135 bool Probe::disable() { 136 if (!attached_to_) 137 return false; 138 139 attached_to_ = nullopt; 140 141 if (need_enable()) { 142 assert(pid_); 143 return add_to_semaphore(-1); 144 } 145 return true; 146 } 147 148 std::string Probe::largest_arg_type(size_t arg_n) { 149 Argument *largest = nullptr; 150 for (Location &location : locations_) { 151 Argument *candidate = &location.arguments_[arg_n]; 152 if (!largest || 153 std::abs(candidate->arg_size()) > std::abs(largest->arg_size())) 154 largest = candidate; 155 } 156 157 assert(largest); 158 return largest->ctype(); 159 } 160 161 bool Probe::usdt_getarg(std::ostream &stream) { 162 if (!attached_to_ || attached_to_->empty()) 163 return false; 164 165 return usdt_getarg(stream, attached_to_.value()); 166 } 167 168 bool Probe::usdt_getarg(std::ostream &stream, const std::string& probe_func) { 169 const size_t arg_count = locations_[0].arguments_.size(); 170 171 if (arg_count == 0) 172 return true; 173 174 for (size_t arg_n = 0; arg_n < arg_count; ++arg_n) { 175 std::string ctype = largest_arg_type(arg_n); 176 std::string cptr = tfm::format("*((%s *)dest)", ctype); 177 178 tfm::format(stream, 179 "static __always_inline int _bpf_readarg_%s_%d(" 180 "struct pt_regs *ctx, void *dest, size_t len) {\n" 181 " if (len != sizeof(%s)) return -1;\n", 182 probe_func, arg_n + 1, ctype); 183 184 if (locations_.size() == 1) { 185 Location &location = locations_.front(); 186 stream << " "; 187 if (!location.arguments_[arg_n].assign_to_local(stream, cptr, location.bin_path_, 188 pid_)) 189 return false; 190 stream << "\n return 0;\n}\n"; 191 } else { 192 stream << " switch(PT_REGS_IP(ctx)) {\n"; 193 for (Location &location : locations_) { 194 uint64_t global_address; 195 196 if (!resolve_global_address(&global_address, location.bin_path_, 197 location.address_)) 198 return false; 199 200 tfm::format(stream, " case 0x%xULL: ", global_address); 201 if (!location.arguments_[arg_n].assign_to_local(stream, cptr, location.bin_path_, 202 pid_)) 203 return false; 204 205 stream << " return 0;\n"; 206 } 207 stream << " }\n"; 208 stream << " return -1;\n}\n"; 209 } 210 } 211 return true; 212 } 213 214 void Probe::add_location(uint64_t addr, const std::string &bin_path, const char *fmt) { 215 locations_.emplace_back(addr, bin_path, fmt); 216 } 217 218 void Probe::finalize_locations() { 219 std::sort(locations_.begin(), locations_.end(), 220 [](const Location &a, const Location &b) { 221 return a.bin_path_ < b.bin_path_ || a.address_ < b.address_; 222 }); 223 auto last = std::unique(locations_.begin(), locations_.end(), 224 [](const Location &a, const Location &b) { 225 return a.bin_path_ == b.bin_path_ && a.address_ == b.address_; 226 }); 227 locations_.erase(last, locations_.end()); 228 } 229 230 void Context::_each_probe(const char *binpath, const struct bcc_elf_usdt *probe, 231 void *p) { 232 Context *ctx = static_cast<Context *>(p); 233 ctx->add_probe(binpath, probe); 234 } 235 236 int Context::_each_module(const char *modpath, uint64_t, uint64_t, uint64_t, 237 bool, void *p) { 238 Context *ctx = static_cast<Context *>(p); 239 // Modules may be reported multiple times if they contain more than one 240 // executable region. We are going to parse the ELF on disk anyway, so we 241 // don't need these duplicates. 242 if (ctx->modules_.insert(modpath).second /*inserted new?*/) { 243 ProcMountNSGuard g(ctx->mount_ns_instance_.get()); 244 bcc_elf_foreach_usdt(modpath, _each_probe, p); 245 } 246 return 0; 247 } 248 249 void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) { 250 for (auto &p : probes_) { 251 if (p->provider_ == probe->provider && p->name_ == probe->name) { 252 p->add_location(probe->pc, binpath, probe->arg_fmt); 253 return; 254 } 255 } 256 257 probes_.emplace_back( 258 new Probe(binpath, probe->provider, probe->name, probe->semaphore, pid_, 259 mount_ns_instance_.get())); 260 probes_.back()->add_location(probe->pc, binpath, probe->arg_fmt); 261 } 262 263 std::string Context::resolve_bin_path(const std::string &bin_path) { 264 std::string result; 265 266 if (char *which = bcc_procutils_which(bin_path.c_str())) { 267 result = which; 268 ::free(which); 269 } else if (char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) { 270 result = which_so; 271 ::free(which_so); 272 } 273 274 return result; 275 } 276 277 Probe *Context::get(const std::string &probe_name) { 278 for (auto &p : probes_) { 279 if (p->name_ == probe_name) 280 return p.get(); 281 } 282 return nullptr; 283 } 284 285 Probe *Context::get(const std::string &provider_name, 286 const std::string &probe_name) { 287 for (auto &p : probes_) { 288 if (p->provider_ == provider_name && p->name_ == probe_name) 289 return p.get(); 290 } 291 return nullptr; 292 } 293 294 bool Context::enable_probe(const std::string &probe_name, 295 const std::string &fn_name) { 296 if (pid_stat_ && pid_stat_->is_stale()) 297 return false; 298 299 // FIXME: we may have issues here if the context has two same probes's 300 // but different providers. For example, libc:setjmp and rtld:setjmp, 301 // libc:lll_futex_wait and rtld:lll_futex_wait. 302 Probe *found_probe = nullptr; 303 for (auto &p : probes_) { 304 if (p->name_ == probe_name) { 305 if (found_probe != nullptr) { 306 fprintf(stderr, "Two same-name probes (%s) but different providers\n", 307 probe_name.c_str()); 308 return false; 309 } 310 found_probe = p.get(); 311 } 312 } 313 314 if (found_probe != nullptr) 315 return found_probe->enable(fn_name); 316 317 return false; 318 } 319 320 void Context::each(each_cb callback) { 321 for (const auto &probe : probes_) { 322 struct bcc_usdt info = {0}; 323 info.provider = probe->provider().c_str(); 324 info.bin_path = probe->bin_path().c_str(); 325 info.name = probe->name().c_str(); 326 info.semaphore = probe->semaphore(); 327 info.num_locations = probe->num_locations(); 328 info.num_arguments = probe->num_arguments(); 329 callback(&info); 330 } 331 } 332 333 void Context::each_uprobe(each_uprobe_cb callback) { 334 for (auto &p : probes_) { 335 if (!p->enabled()) 336 continue; 337 338 for (Location &loc : p->locations_) { 339 callback(loc.bin_path_.c_str(), p->attached_to_->c_str(), loc.address_, 340 pid_.value_or(-1)); 341 } 342 } 343 } 344 345 Context::Context(const std::string &bin_path) 346 : mount_ns_instance_(new ProcMountNS(-1)), loaded_(false) { 347 std::string full_path = resolve_bin_path(bin_path); 348 if (!full_path.empty()) { 349 if (bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this) == 0) { 350 cmd_bin_path_ = full_path; 351 loaded_ = true; 352 } 353 } 354 for (const auto &probe : probes_) 355 probe->finalize_locations(); 356 } 357 358 Context::Context(int pid) : pid_(pid), pid_stat_(pid), 359 mount_ns_instance_(new ProcMountNS(pid)), loaded_(false) { 360 if (bcc_procutils_each_module(pid, _each_module, this) == 0) { 361 cmd_bin_path_ = ebpf::get_pid_exe(pid); 362 if (cmd_bin_path_.empty()) 363 return; 364 365 loaded_ = true; 366 } 367 for (const auto &probe : probes_) 368 probe->finalize_locations(); 369 } 370 371 Context::Context(int pid, const std::string &bin_path) 372 : pid_(pid), pid_stat_(pid), 373 mount_ns_instance_(new ProcMountNS(pid)), loaded_(false) { 374 std::string full_path = resolve_bin_path(bin_path); 375 if (!full_path.empty()) { 376 if (bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this) == 0) { 377 cmd_bin_path_ = ebpf::get_pid_exe(pid); 378 if (cmd_bin_path_.empty()) 379 return; 380 loaded_ = true; 381 } 382 } 383 for (const auto &probe : probes_) 384 probe->finalize_locations(); 385 } 386 387 Context::~Context() { 388 if (pid_stat_ && !pid_stat_->is_stale()) { 389 for (auto &p : probes_) p->disable(); 390 } 391 } 392 } 393 394 extern "C" { 395 396 void *bcc_usdt_new_frompid(int pid, const char *path) { 397 USDT::Context *ctx; 398 399 if (!path) { 400 ctx = new USDT::Context(pid); 401 } else { 402 struct stat buffer; 403 if (strlen(path) >= 1 && path[0] != '/') { 404 fprintf(stderr, "HINT: Binary path should be absolute.\n\n"); 405 return nullptr; 406 } else if (stat(path, &buffer) == -1) { 407 fprintf(stderr, "HINT: Specified binary doesn't exist.\n\n"); 408 return nullptr; 409 } 410 ctx = new USDT::Context(pid, path); 411 } 412 if (!ctx->loaded()) { 413 delete ctx; 414 return nullptr; 415 } 416 return static_cast<void *>(ctx); 417 } 418 419 void *bcc_usdt_new_frompath(const char *path) { 420 USDT::Context *ctx = new USDT::Context(path); 421 if (!ctx->loaded()) { 422 delete ctx; 423 return nullptr; 424 } 425 return static_cast<void *>(ctx); 426 } 427 428 void bcc_usdt_close(void *usdt) { 429 if (usdt) { 430 USDT::Context *ctx = static_cast<USDT::Context *>(usdt); 431 delete ctx; 432 } 433 } 434 435 int bcc_usdt_enable_probe(void *usdt, const char *probe_name, 436 const char *fn_name) { 437 USDT::Context *ctx = static_cast<USDT::Context *>(usdt); 438 return ctx->enable_probe(probe_name, fn_name) ? 0 : -1; 439 } 440 441 const char *bcc_usdt_genargs(void **usdt_array, int len) { 442 static std::string storage_; 443 std::ostringstream stream; 444 445 if (!len) 446 return ""; 447 448 stream << USDT::USDT_PROGRAM_HEADER; 449 // Generate genargs codes for an array of USDT Contexts. 450 // 451 // Each mnt_point + cmd_bin_path + probe_provider + probe_name 452 // uniquely identifies a probe. 453 std::unordered_set<std::string> generated_probes; 454 for (int i = 0; i < len; i++) { 455 USDT::Context *ctx = static_cast<USDT::Context *>(usdt_array[i]); 456 457 for (size_t j = 0; j < ctx->num_probes(); j++) { 458 USDT::Probe *p = ctx->get(j); 459 if (p->enabled()) { 460 std::string key = std::to_string(ctx->inode()) + "*" 461 + ctx->cmd_bin_path() + "*" + p->provider() + "*" + p->name(); 462 if (generated_probes.find(key) != generated_probes.end()) 463 continue; 464 if (!p->usdt_getarg(stream)) 465 return nullptr; 466 generated_probes.insert(key); 467 } 468 } 469 } 470 471 storage_ = stream.str(); 472 return storage_.c_str(); 473 } 474 475 const char *bcc_usdt_get_probe_argctype( 476 void *ctx, const char* probe_name, const int arg_index 477 ) { 478 USDT::Probe *p = static_cast<USDT::Context *>(ctx)->get(probe_name); 479 if (p) 480 return p->get_arg_ctype(arg_index).c_str(); 481 return ""; 482 } 483 484 void bcc_usdt_foreach(void *usdt, bcc_usdt_cb callback) { 485 USDT::Context *ctx = static_cast<USDT::Context *>(usdt); 486 ctx->each(callback); 487 } 488 489 int bcc_usdt_get_location(void *usdt, const char *provider_name, 490 const char *probe_name, 491 int index, struct bcc_usdt_location *location) { 492 USDT::Context *ctx = static_cast<USDT::Context *>(usdt); 493 USDT::Probe *probe = ctx->get(provider_name, probe_name); 494 if (!probe) 495 return -1; 496 if (index < 0 || (size_t)index >= probe->num_locations()) 497 return -1; 498 location->address = probe->address(index); 499 location->bin_path = probe->location_bin_path(index); 500 return 0; 501 } 502 503 int bcc_usdt_get_argument(void *usdt, const char *provider_name, 504 const char *probe_name, 505 int location_index, int argument_index, 506 struct bcc_usdt_argument *argument) { 507 USDT::Context *ctx = static_cast<USDT::Context *>(usdt); 508 USDT::Probe *probe = ctx->get(provider_name, probe_name); 509 if (!probe) 510 return -1; 511 if (argument_index < 0 || (size_t)argument_index >= probe->num_arguments()) 512 return -1; 513 if (location_index < 0 || (size_t)location_index >= probe->num_locations()) 514 return -1; 515 auto const &location = probe->location(location_index); 516 auto const &arg = location.arguments_[argument_index]; 517 argument->size = arg.arg_size(); 518 argument->valid = BCC_USDT_ARGUMENT_NONE; 519 if (arg.constant()) { 520 argument->valid |= BCC_USDT_ARGUMENT_CONSTANT; 521 argument->constant = *(arg.constant()); 522 } 523 if (arg.deref_offset()) { 524 argument->valid |= BCC_USDT_ARGUMENT_DEREF_OFFSET; 525 argument->deref_offset = *(arg.deref_offset()); 526 } 527 if (arg.deref_ident()) { 528 argument->valid |= BCC_USDT_ARGUMENT_DEREF_IDENT; 529 argument->deref_ident = arg.deref_ident()->c_str(); 530 } 531 if (arg.base_register_name()) { 532 argument->valid |= BCC_USDT_ARGUMENT_BASE_REGISTER_NAME; 533 argument->base_register_name = arg.base_register_name()->c_str(); 534 } 535 if (arg.index_register_name()) { 536 argument->valid |= BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME; 537 argument->index_register_name = arg.index_register_name()->c_str(); 538 } 539 if (arg.scale()) { 540 argument->valid |= BCC_USDT_ARGUMENT_SCALE; 541 argument->scale = *(arg.scale()); 542 } 543 return 0; 544 } 545 546 void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback) { 547 USDT::Context *ctx = static_cast<USDT::Context *>(usdt); 548 ctx->each_uprobe(callback); 549 } 550 } 551