• 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 #elif __s390x__
44   ArgumentParser_s390x parser(arg_fmt);
45 #else
46   ArgumentParser_x64 parser(arg_fmt);
47 #endif
48   while (!parser.done()) {
49     Argument arg;
50     if (!parser.parse(&arg))
51       continue;
52     arguments_.push_back(std::move(arg));
53   }
54 }
55 
Probe(const char * bin_path,const char * provider,const char * name,uint64_t semaphore,uint64_t semaphore_offset,const optional<int> & pid,uint8_t mod_match_inode_only)56 Probe::Probe(const char *bin_path, const char *provider, const char *name,
57              uint64_t semaphore, uint64_t semaphore_offset,
58              const optional<int> &pid, uint8_t mod_match_inode_only)
59     : bin_path_(bin_path),
60       provider_(provider),
61       name_(name),
62       semaphore_(semaphore),
63       semaphore_offset_(semaphore_offset),
64       pid_(pid),
65       mod_match_inode_only_(mod_match_inode_only)
66       {}
67 
in_shared_object(const std::string & bin_path)68 bool Probe::in_shared_object(const std::string &bin_path) {
69     if (object_type_map_.find(bin_path) == object_type_map_.end()) {
70       return (object_type_map_[bin_path] = bcc_elf_is_shared_obj(bin_path.c_str()));
71     }
72     return object_type_map_[bin_path];
73 }
74 
resolve_global_address(uint64_t * global,const std::string & bin_path,const uint64_t addr)75 bool Probe::resolve_global_address(uint64_t *global, const std::string &bin_path,
76                                    const uint64_t addr) {
77   if (in_shared_object(bin_path)) {
78     return (pid_ &&
79             !bcc_resolve_global_addr(*pid_, bin_path.c_str(), addr, mod_match_inode_only_, global));
80   }
81 
82   *global = addr;
83   return true;
84 }
85 
add_to_semaphore(int16_t val)86 bool Probe::add_to_semaphore(int16_t val) {
87   assert(pid_);
88 
89   if (!attached_semaphore_) {
90     uint64_t addr;
91     if (!resolve_global_address(&addr, bin_path_, semaphore_))
92       return false;
93     attached_semaphore_ = addr;
94   }
95 
96   off_t address = static_cast<off_t>(attached_semaphore_.value());
97 
98   std::string procmem = tfm::format("/proc/%d/mem", pid_.value());
99   int memfd = ::open(procmem.c_str(), O_RDWR);
100   if (memfd < 0)
101     return false;
102 
103   int16_t original;
104 
105   if (::lseek(memfd, address, SEEK_SET) < 0 ||
106       ::read(memfd, &original, 2) != 2) {
107     ::close(memfd);
108     return false;
109   }
110 
111   original = original + val;
112 
113   if (::lseek(memfd, address, SEEK_SET) < 0 ||
114       ::write(memfd, &original, 2) != 2) {
115     ::close(memfd);
116     return false;
117   }
118 
119   ::close(memfd);
120   return true;
121 }
122 
enable(const std::string & fn_name)123 bool Probe::enable(const std::string &fn_name) {
124   if (attached_to_)
125     return false;
126 
127   if (need_enable()) {
128     if (!pid_)
129       return false;
130 
131     if (!add_to_semaphore(+1))
132       return false;
133   }
134 
135   attached_to_ = fn_name;
136   return true;
137 }
138 
disable()139 bool Probe::disable() {
140   if (!attached_to_)
141     return false;
142 
143   attached_to_ = nullopt;
144 
145   if (need_enable()) {
146     assert(pid_);
147     return add_to_semaphore(-1);
148   }
149   return true;
150 }
151 
largest_arg_type(size_t arg_n)152 std::string Probe::largest_arg_type(size_t arg_n) {
153   Argument *largest = nullptr;
154   for (Location &location : locations_) {
155     Argument *candidate = &location.arguments_[arg_n];
156     if (!largest ||
157         std::abs(candidate->arg_size()) > std::abs(largest->arg_size()))
158       largest = candidate;
159   }
160 
161   assert(largest);
162   return largest->ctype();
163 }
164 
usdt_getarg(std::ostream & stream)165 bool Probe::usdt_getarg(std::ostream &stream) {
166   if (!attached_to_ || attached_to_->empty())
167     return false;
168 
169   return usdt_getarg(stream, attached_to_.value());
170 }
171 
usdt_getarg(std::ostream & stream,const std::string & probe_func)172 bool Probe::usdt_getarg(std::ostream &stream, const std::string& probe_func) {
173   const size_t arg_count = locations_[0].arguments_.size();
174 
175   if (arg_count == 0)
176     return true;
177 
178   uint64_t page_size = sysconf(_SC_PAGESIZE);
179   std::unordered_set<int> page_offsets;
180   for (Location &location : locations_)
181     page_offsets.insert(location.address_ % page_size);
182 
183   for (size_t arg_n = 0; arg_n < arg_count; ++arg_n) {
184     std::string ctype = largest_arg_type(arg_n);
185     std::string cptr = tfm::format("*((%s *)dest)", ctype);
186 
187     tfm::format(stream,
188                 "static __always_inline int _bpf_readarg_%s_%d("
189                 "struct pt_regs *ctx, void *dest, size_t len) {\n"
190                 "  if (len != sizeof(%s)) return -1;\n",
191                 probe_func, arg_n + 1, ctype);
192 
193     if (locations_.size() == 1) {
194       Location &location = locations_.front();
195       stream << "  ";
196       if (!location.arguments_[arg_n].assign_to_local(stream, cptr, location.bin_path_,
197                                                       pid_))
198         return false;
199       stream << "\n  return 0;\n}\n";
200     } else {
201       if (page_offsets.size() == locations_.size())
202         tfm::format(stream, "  switch (PT_REGS_IP(ctx) %% 0x%xULL) {\n", page_size);
203       else
204         stream << "  switch (PT_REGS_IP(ctx)) {\n";
205       for (Location &location : locations_) {
206         if (page_offsets.size() == locations_.size()) {
207           tfm::format(stream, "  case 0x%xULL: ", location.address_ % page_size);
208         } else {
209           uint64_t global_address;
210 
211           if (!resolve_global_address(&global_address, location.bin_path_,
212                                       location.address_))
213             return false;
214 
215           tfm::format(stream, "  case 0x%xULL: ", global_address);
216         }
217         if (!location.arguments_[arg_n].assign_to_local(stream, cptr, location.bin_path_,
218                                                         pid_))
219           return false;
220 
221         stream << " return 0;\n";
222       }
223       stream << "  }\n";
224       stream << "  return -1;\n}\n";
225     }
226   }
227   return true;
228 }
229 
add_location(uint64_t addr,const std::string & bin_path,const char * fmt)230 void Probe::add_location(uint64_t addr, const std::string &bin_path, const char *fmt) {
231   locations_.emplace_back(addr, bin_path, fmt);
232 }
233 
finalize_locations()234 void Probe::finalize_locations() {
235   // The following comparator needs to establish a strict weak ordering relation. Such
236   // that when x < y == true, y < x == false. Otherwise it leads to undefined behavior.
237   // To guarantee this, it uses std::tie which allows the lambda to have a lexicographical
238   // comparison and hence, guarantee the strict weak ordering.
239   std::sort(locations_.begin(), locations_.end(),
240             [](const Location &a, const Location &b) {
241               return std::tie(a.bin_path_, a.address_) < std::tie(b.bin_path_, b.address_);
242             });
243   auto last = std::unique(locations_.begin(), locations_.end(),
244                           [](const Location &a, const Location &b) {
245                             return a.bin_path_ == b.bin_path_ && a.address_ == b.address_;
246                           });
247   locations_.erase(last, locations_.end());
248 }
249 
_each_probe(const char * binpath,const struct bcc_elf_usdt * probe,void * p)250 void Context::_each_probe(const char *binpath, const struct bcc_elf_usdt *probe,
251                           void *p) {
252   Context *ctx = static_cast<Context *>(p);
253   ctx->add_probe(binpath, probe);
254 }
255 
_each_module(mod_info * mod,int enter_ns,void * p)256 int Context::_each_module(mod_info *mod, int enter_ns, void *p) {
257   Context *ctx = static_cast<Context *>(p);
258 
259   std::string path = mod->name;
260   if (ctx->pid_ && *ctx->pid_ != -1 && enter_ns) {
261     path = tfm::format("/proc/%d/root%s", *ctx->pid_, path);
262   }
263 
264   // Modules may be reported multiple times if they contain more than one
265   // executable region. We are going to parse the ELF on disk anyway, so we
266   // don't need these duplicates.
267   if (ctx->modules_.insert(path).second /*inserted new?*/) {
268     bcc_elf_foreach_usdt(path.c_str(), _each_probe, p);
269   }
270   return 0;
271 }
272 
add_probe(const char * binpath,const struct bcc_elf_usdt * probe)273 void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) {
274   for (auto &p : probes_) {
275     if (p->provider_ == probe->provider && p->name_ == probe->name) {
276       p->add_location(probe->pc, binpath, probe->arg_fmt);
277       return;
278     }
279   }
280 
281   probes_.emplace_back(
282     new Probe(binpath, probe->provider, probe->name, probe->semaphore,
283               probe->semaphore_offset, pid_, mod_match_inode_only_)
284   );
285   probes_.back()->add_location(probe->pc, binpath, probe->arg_fmt);
286 }
287 
resolve_bin_path(const std::string & bin_path)288 std::string Context::resolve_bin_path(const std::string &bin_path) {
289   std::string result;
290 
291   if (char *which = bcc_procutils_which(bin_path.c_str())) {
292     result = which;
293     ::free(which);
294   } else if (char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) {
295     result = which_so;
296     ::free(which_so);
297   }
298 
299   if (!result.empty() && pid_ && *pid_ != -1 && result.find("/proc") != 0) {
300     result = tfm::format("/proc/%d/root%s", *pid_, result);
301   }
302 
303   return result;
304 }
305 
get(const std::string & probe_name)306 Probe *Context::get(const std::string &probe_name) {
307   for (auto &p : probes_) {
308     if (p->name_ == probe_name)
309       return p.get();
310   }
311   return nullptr;
312 }
313 
get(const std::string & provider_name,const std::string & probe_name)314 Probe *Context::get(const std::string &provider_name,
315                     const std::string &probe_name) {
316   for (auto &p : probes_) {
317     if (p->provider_ == provider_name && p->name_ == probe_name)
318       return p.get();
319   }
320   return nullptr;
321 }
322 
enable_probe(const std::string & probe_name,const std::string & fn_name)323 bool Context::enable_probe(const std::string &probe_name,
324                            const std::string &fn_name) {
325   return enable_probe("", probe_name, fn_name);
326 }
327 
get_checked(const std::string & provider_name,const std::string & probe_name)328 Probe *Context::get_checked(const std::string &provider_name,
329                             const std::string &probe_name) {
330   if (pid_stat_ && pid_stat_->is_stale())
331     return nullptr;
332 
333   Probe *found_probe = nullptr;
334   for (auto &p : probes_) {
335     if (p->name_ == probe_name &&
336         (provider_name.empty() || p->provider() == provider_name)) {
337       if (found_probe != nullptr) {
338         fprintf(stderr, "Two same-name probes (%s) but different providers\n",
339                 probe_name.c_str());
340         return nullptr;
341       }
342       found_probe = p.get();
343     }
344   }
345 
346   return found_probe;
347 }
348 
enable_probe(const std::string & provider_name,const std::string & probe_name,const std::string & fn_name)349 bool Context::enable_probe(const std::string &provider_name,
350                            const std::string &probe_name,
351                            const std::string &fn_name) {
352   Probe *found_probe = get_checked(provider_name, probe_name);
353 
354   if (found_probe != nullptr)
355     return found_probe->enable(fn_name);
356 
357   return false;
358 }
359 
each(each_cb callback)360 void Context::each(each_cb callback) {
361   for (const auto &probe : probes_) {
362     struct bcc_usdt info = {0};
363     info.provider = probe->provider().c_str();
364     info.bin_path = probe->bin_path().c_str();
365     info.name = probe->name().c_str();
366     info.semaphore = probe->semaphore();
367     info.semaphore_offset = probe->semaphore_offset();
368     info.num_locations = probe->num_locations();
369     info.num_arguments = probe->num_arguments();
370     callback(&info);
371   }
372 }
373 
addsem_probe(const std::string & provider_name,const std::string & probe_name,const std::string & fn_name,int16_t val)374 bool Context::addsem_probe(const std::string &provider_name,
375                            const std::string &probe_name,
376                            const std::string &fn_name,
377                            int16_t val) {
378   Probe *found_probe = get_checked(provider_name, probe_name);
379 
380   if (found_probe != nullptr) {
381     if (found_probe->need_enable())
382       return found_probe->add_to_semaphore(val);
383 
384     return true;
385   }
386 
387   return false;
388 }
389 
each_uprobe(each_uprobe_cb callback)390 void Context::each_uprobe(each_uprobe_cb callback) {
391   for (auto &p : probes_) {
392     if (!p->enabled())
393       continue;
394 
395     for (Location &loc : p->locations_) {
396       callback(loc.bin_path_.c_str(), p->attached_to_->c_str(), loc.address_,
397                pid_.value_or(-1));
398     }
399   }
400 }
401 
Context(const std::string & bin_path,uint8_t mod_match_inode_only)402 Context::Context(const std::string &bin_path, uint8_t mod_match_inode_only)
403     : loaded_(false), mod_match_inode_only_(mod_match_inode_only) {
404   std::string full_path = resolve_bin_path(bin_path);
405   if (!full_path.empty()) {
406     if (bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this) == 0) {
407       cmd_bin_path_ = full_path;
408       loaded_ = true;
409     }
410   }
411   for (const auto &probe : probes_)
412     probe->finalize_locations();
413 }
414 
Context(int pid,uint8_t mod_match_inode_only)415 Context::Context(int pid, uint8_t mod_match_inode_only)
416     : pid_(pid), pid_stat_(pid), loaded_(false),
417     mod_match_inode_only_(mod_match_inode_only) {
418   if (bcc_procutils_each_module(pid, _each_module, this) == 0) {
419     cmd_bin_path_ = ebpf::get_pid_exe(pid);
420     if (cmd_bin_path_.empty())
421       return;
422 
423     loaded_ = true;
424   }
425   for (const auto &probe : probes_)
426     probe->finalize_locations();
427 }
428 
Context(int pid,const std::string & bin_path,uint8_t mod_match_inode_only)429 Context::Context(int pid, const std::string &bin_path,
430                  uint8_t mod_match_inode_only)
431     : pid_(pid), pid_stat_(pid), loaded_(false),
432       mod_match_inode_only_(mod_match_inode_only) {
433   std::string full_path = resolve_bin_path(bin_path);
434   if (!full_path.empty()) {
435     int res = bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this);
436     if (res == 0) {
437       cmd_bin_path_ = ebpf::get_pid_exe(pid);
438       if (cmd_bin_path_.empty())
439         return;
440       loaded_ = true;
441     }
442   }
443   for (const auto &probe : probes_)
444     probe->finalize_locations();
445 }
446 
~Context()447 Context::~Context() {
448   if (pid_stat_ && !pid_stat_->is_stale()) {
449     for (auto &p : probes_) p->disable();
450   }
451 }
452 }
453 
454 extern "C" {
455 
bcc_usdt_new_frompid(int pid,const char * path)456 void *bcc_usdt_new_frompid(int pid, const char *path) {
457   USDT::Context *ctx;
458 
459   if (!path) {
460     ctx = new USDT::Context(pid);
461   } else {
462     struct stat buffer;
463     if (strlen(path) >= 1 && path[0] != '/') {
464       fprintf(stderr, "HINT: Binary path %s should be absolute.\n\n", path);
465       return nullptr;
466     } else if (stat(path, &buffer) == -1) {
467       fprintf(stderr, "HINT: Specified binary %s doesn't exist.\n\n", path);
468       return nullptr;
469     }
470     ctx = new USDT::Context(pid, path);
471   }
472   if (!ctx->loaded()) {
473     delete ctx;
474     return nullptr;
475   }
476   return static_cast<void *>(ctx);
477 }
478 
bcc_usdt_new_frompath(const char * path)479 void *bcc_usdt_new_frompath(const char *path) {
480   USDT::Context *ctx = new USDT::Context(path);
481   if (!ctx->loaded()) {
482     delete ctx;
483     return nullptr;
484   }
485   return static_cast<void *>(ctx);
486 }
487 
bcc_usdt_close(void * usdt)488 void bcc_usdt_close(void *usdt) {
489   if (usdt) {
490     USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
491     delete ctx;
492   }
493 }
494 
bcc_usdt_enable_probe(void * usdt,const char * probe_name,const char * fn_name)495 int bcc_usdt_enable_probe(void *usdt, const char *probe_name,
496                           const char *fn_name) {
497   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
498   return ctx->enable_probe(probe_name, fn_name) ? 0 : -1;
499 }
500 
bcc_usdt_addsem_probe(void * usdt,const char * probe_name,const char * fn_name,int16_t val)501 int bcc_usdt_addsem_probe(void *usdt, const char *probe_name,
502                           const char *fn_name, int16_t val) {
503   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
504   return ctx->addsem_probe("", probe_name, fn_name, val) ? 0 : -1;
505 }
506 
bcc_usdt_enable_fully_specified_probe(void * usdt,const char * provider_name,const char * probe_name,const char * fn_name)507 int bcc_usdt_enable_fully_specified_probe(void *usdt, const char *provider_name,
508                                           const char *probe_name,
509                                           const char *fn_name) {
510   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
511   return ctx->enable_probe(provider_name, probe_name, fn_name) ? 0 : -1;
512 }
513 
bcc_usdt_addsem_fully_specified_probe(void * usdt,const char * provider_name,const char * probe_name,const char * fn_name,int16_t val)514 int bcc_usdt_addsem_fully_specified_probe(void *usdt, const char *provider_name,
515                                           const char *probe_name,
516                                           const char *fn_name, int16_t val) {
517   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
518   return ctx->addsem_probe(provider_name, probe_name, fn_name, val) ? 0 : -1;
519 }
520 
bcc_usdt_genargs(void ** usdt_array,int len)521 const char *bcc_usdt_genargs(void **usdt_array, int len) {
522   static std::string storage_;
523   std::ostringstream stream;
524 
525   if (!len)
526     return "";
527 
528   stream << USDT::USDT_PROGRAM_HEADER;
529   // Generate genargs codes for an array of USDT Contexts.
530   //
531   // Each cmd_bin_path + probe_provider + probe_name
532   // uniquely identifies a probe.
533   std::unordered_set<std::string> generated_probes;
534   for (int i = 0; i < len; i++) {
535     USDT::Context *ctx = static_cast<USDT::Context *>(usdt_array[i]);
536 
537     for (size_t j = 0; j < ctx->num_probes(); j++) {
538       USDT::Probe *p = ctx->get(j);
539       if (p->enabled()) {
540         std::string key = ctx->cmd_bin_path() + "*" + p->provider() + "*" + p->name();
541         if (generated_probes.find(key) != generated_probes.end())
542           continue;
543         if (!p->usdt_getarg(stream))
544           return nullptr;
545         generated_probes.insert(key);
546       }
547     }
548   }
549 
550   storage_ = stream.str();
551   return storage_.c_str();
552 }
553 
bcc_usdt_get_probe_argctype(void * ctx,const char * probe_name,const int arg_index)554 const char *bcc_usdt_get_probe_argctype(
555   void *ctx, const char* probe_name, const int arg_index
556 ) {
557   USDT::Probe *p = static_cast<USDT::Context *>(ctx)->get(probe_name);
558   if (p)
559     return p->get_arg_ctype(arg_index).c_str();
560   return "";
561 }
562 
bcc_usdt_get_fully_specified_probe_argctype(void * ctx,const char * provider_name,const char * probe_name,const int arg_index)563 const char *bcc_usdt_get_fully_specified_probe_argctype(
564   void *ctx, const char* provider_name, const char* probe_name, const int arg_index
565 ) {
566   USDT::Probe *p = static_cast<USDT::Context *>(ctx)->get(provider_name, probe_name);
567   if (p)
568     return p->get_arg_ctype(arg_index).c_str();
569   return "";
570 }
571 
bcc_usdt_foreach(void * usdt,bcc_usdt_cb callback)572 void bcc_usdt_foreach(void *usdt, bcc_usdt_cb callback) {
573   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
574   ctx->each(callback);
575 }
576 
bcc_usdt_get_location(void * usdt,const char * provider_name,const char * probe_name,int index,struct bcc_usdt_location * location)577 int bcc_usdt_get_location(void *usdt, const char *provider_name,
578                           const char *probe_name,
579                           int index, struct bcc_usdt_location *location) {
580   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
581   USDT::Probe *probe = ctx->get(provider_name, probe_name);
582   if (!probe)
583     return -1;
584   if (index < 0 || (size_t)index >= probe->num_locations())
585     return -1;
586   location->address = probe->address(index);
587   location->bin_path = probe->location_bin_path(index);
588   return 0;
589 }
590 
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)591 int bcc_usdt_get_argument(void *usdt, const char *provider_name,
592                           const char *probe_name,
593                           int location_index, int argument_index,
594                           struct bcc_usdt_argument *argument) {
595   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
596   USDT::Probe *probe = ctx->get(provider_name, probe_name);
597   if (!probe)
598     return -1;
599   if (argument_index < 0 || (size_t)argument_index >= probe->num_arguments())
600     return -1;
601   if (location_index < 0 || (size_t)location_index >= probe->num_locations())
602     return -1;
603   auto const &location = probe->location(location_index);
604   auto const &arg = location.arguments_[argument_index];
605   argument->size = arg.arg_size();
606   argument->valid = BCC_USDT_ARGUMENT_NONE;
607   if (arg.constant()) {
608     argument->valid |= BCC_USDT_ARGUMENT_CONSTANT;
609     argument->constant = *(arg.constant());
610   }
611   if (arg.deref_offset()) {
612     argument->valid |= BCC_USDT_ARGUMENT_DEREF_OFFSET;
613     argument->deref_offset = *(arg.deref_offset());
614   }
615   if (arg.deref_ident()) {
616     argument->valid |= BCC_USDT_ARGUMENT_DEREF_IDENT;
617     argument->deref_ident = arg.deref_ident()->c_str();
618   }
619   if (arg.base_register_name()) {
620     argument->valid |= BCC_USDT_ARGUMENT_BASE_REGISTER_NAME;
621     argument->base_register_name = arg.base_register_name()->c_str();
622   }
623   if (arg.index_register_name()) {
624     argument->valid |= BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME;
625     argument->index_register_name = arg.index_register_name()->c_str();
626   }
627   if (arg.scale()) {
628     argument->valid |= BCC_USDT_ARGUMENT_SCALE;
629     argument->scale = *(arg.scale());
630   }
631   return 0;
632 }
633 
bcc_usdt_foreach_uprobe(void * usdt,bcc_usdt_uprobe_cb callback)634 void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback) {
635   USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
636   ctx->each_uprobe(callback);
637 }
638 }
639