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