• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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