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
Location(uint64_t addr,const std::string & bin_path,const char * arg_fmt)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
Probe(const char * bin_path,const char * provider,const char * name,uint64_t semaphore,const optional<int> & pid,ProcMountNS * ns)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
in_shared_object(const std::string & bin_path)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
resolve_global_address(uint64_t * global,const std::string & bin_path,const uint64_t addr)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
add_to_semaphore(int16_t val)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
enable(const std::string & fn_name)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
disable()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
largest_arg_type(size_t arg_n)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
usdt_getarg(std::ostream & stream)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
usdt_getarg(std::ostream & stream,const std::string & probe_func)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
add_location(uint64_t addr,const std::string & bin_path,const char * fmt)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
finalize_locations()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
_each_probe(const char * binpath,const struct bcc_elf_usdt * probe,void * p)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
_each_module(const char * modpath,uint64_t,uint64_t,uint64_t,bool,void * p)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
add_probe(const char * binpath,const struct bcc_elf_usdt * probe)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
resolve_bin_path(const std::string & bin_path)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
get(const std::string & probe_name)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
get(const std::string & provider_name,const std::string & probe_name)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
enable_probe(const std::string & probe_name,const std::string & fn_name)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
each(each_cb callback)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
each_uprobe(each_uprobe_cb callback)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
Context(const std::string & bin_path)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
Context(int pid)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
Context(int pid,const std::string & bin_path)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
~Context()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
bcc_usdt_new_frompid(int pid,const char * path)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
bcc_usdt_new_frompath(const char * path)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
bcc_usdt_close(void * usdt)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
bcc_usdt_enable_probe(void * usdt,const char * probe_name,const char * fn_name)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
bcc_usdt_genargs(void ** usdt_array,int len)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
bcc_usdt_get_probe_argctype(void * ctx,const char * probe_name,const int arg_index)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
bcc_usdt_foreach(void * usdt,bcc_usdt_cb callback)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
bcc_usdt_get_location(void * usdt,const char * provider_name,const char * probe_name,int index,struct bcc_usdt_location * location)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
bcc_usdt_get_argument(void * usdt,const char * provider_name,const char * probe_name,int location_index,int argument_index,struct bcc_usdt_argument * argument)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
bcc_usdt_foreach_uprobe(void * usdt,bcc_usdt_uprobe_cb callback)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