/* * Copyright (c) 2016 GitHub, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include "bcc_proc.h" #include "syms.h" #include "vendor/optional.hpp" struct bcc_usdt; namespace ebpf { class BPF; class USDT; } namespace USDT { using std::experimental::optional; using std::experimental::nullopt; class ArgumentParser; static const std::string USDT_PROGRAM_HEADER = "#include \n"; static const std::string COMPILER_BARRIER = "__asm__ __volatile__(\"\": : :\"memory\");"; class Argument { private: optional arg_size_; optional constant_; optional deref_offset_; optional deref_ident_; optional base_register_name_; optional index_register_name_; optional scale_; bool get_global_address(uint64_t *address, const std::string &binpath, const optional &pid) const; public: Argument(); ~Argument(); bool assign_to_local(std::ostream &stream, const std::string &local_name, const std::string &binpath, const optional &pid = nullopt) const; int arg_size() const { return arg_size_.value_or(sizeof(void *)); } std::string ctype() const; const char *ctype_name() const; const optional &deref_ident() const { return deref_ident_; } const optional &base_register_name() const { return base_register_name_; } const optional &index_register_name() const { return index_register_name_; } const optional scale() const { return scale_; } const optional constant() const { return constant_; } const optional deref_offset() const { return deref_offset_; } friend class ArgumentParser; friend class ArgumentParser_aarch64; friend class ArgumentParser_loongarch64; friend class ArgumentParser_powerpc64; friend class ArgumentParser_s390x; friend class ArgumentParser_riscv64; friend class ArgumentParser_x64; }; class ArgumentParser { protected: const char *arg_; ssize_t cur_pos_; void skip_whitespace_from(size_t pos); void skip_until_whitespace_from(size_t pos); void print_error(ssize_t pos); ssize_t parse_number(ssize_t pos, optional *result) { char *endp; int number = strtol(arg_ + pos, &endp, 0); if (endp > arg_ + pos) *result = number; return endp - arg_; } ssize_t parse_number(ssize_t pos, optional *result) { char *endp; long long number = (long long)strtoull(arg_ + pos, &endp, 0); if (endp > arg_ + pos) *result = number; return endp - arg_; } bool error_return(ssize_t error_start, ssize_t skip_start) { print_error(error_start); if (isspace(arg_[skip_start])) skip_start++; // Make sure we skip at least one character skip_until_whitespace_from(skip_start); return false; } public: virtual bool parse(Argument *dest) = 0; bool done() { return cur_pos_ < 0 || arg_[cur_pos_] == '\0'; } ArgumentParser(const char *arg) : arg_(arg), cur_pos_(0) {} }; class ArgumentParser_aarch64 : public ArgumentParser { private: bool parse_register(ssize_t pos, ssize_t &new_pos, std::string ®_name); bool parse_size(ssize_t pos, ssize_t &new_pos, optional *arg_size); bool parse_mem(ssize_t pos, ssize_t &new_pos, Argument *dest); public: bool parse(Argument *dest); ArgumentParser_aarch64(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_loongarch64 : public ArgumentParser { private: bool parse_register(ssize_t pos, ssize_t &new_pos, std::string ®_name); bool parse_size(ssize_t pos, ssize_t &new_pos, optional *arg_size); bool parse_mem(ssize_t pos, ssize_t &new_pos, Argument *dest); public: bool parse(Argument *dest); ArgumentParser_loongarch64(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_powerpc64 : public ArgumentParser { public: bool parse(Argument *dest); ArgumentParser_powerpc64(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_s390x : public ArgumentParser { public: bool parse(Argument *dest); ArgumentParser_s390x(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_riscv64 : public ArgumentParser { public: bool parse(Argument *dest); ArgumentParser_riscv64(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_x64 : public ArgumentParser { private: enum Register { X64_REG_A, X64_REG_B, X64_REG_C, X64_REG_D, X64_REG_SI, X64_REG_DI, X64_REG_BP, X64_REG_SP, X64_REG_8, X64_REG_9, X64_REG_10, X64_REG_11, X64_REG_12, X64_REG_13, X64_REG_14, X64_REG_15, X64_REG_RIP, X64_REG_XMM0, X64_REG_XMM1, X64_REG_XMM2, X64_REG_XMM3, X64_REG_XMM4, X64_REG_XMM5, X64_REG_XMM6, X64_REG_XMM7, X64_REG_XMM8, X64_REG_XMM9, X64_REG_XMM10, X64_REG_XMM11, X64_REG_XMM12, X64_REG_XMM13, X64_REG_XMM14, X64_REG_XMM15, }; struct RegInfo { Register reg; int size; }; static const std::unordered_map registers_; bool normalize_register(std::string *reg, int *reg_size); void reg_to_name(std::string *norm, Register reg); ssize_t parse_register(ssize_t pos, std::string &name, int &size); ssize_t parse_identifier(ssize_t pos, optional *ident); ssize_t parse_base_register(ssize_t pos, Argument *dest); ssize_t parse_index_register(ssize_t pos, Argument *dest); ssize_t parse_scale(ssize_t pos, Argument *dest); ssize_t parse_expr(ssize_t pos, Argument *dest); ssize_t parse_1(ssize_t pos, Argument *dest); public: bool parse(Argument *dest); ArgumentParser_x64(const char *arg) : ArgumentParser(arg) {} }; struct Location { uint64_t address_; std::string bin_path_; std::vector arguments_; Location(uint64_t addr, const std::string &bin_path, const char *arg_fmt); }; class Probe { std::string bin_path_; // initial bin_path when Probe is created std::string provider_; std::string name_; uint64_t semaphore_; uint64_t semaphore_offset_; std::vector locations_; optional pid_; std::unordered_map object_type_map_; // bin_path => is shared lib? optional attached_to_; optional attached_semaphore_; uint8_t mod_match_inode_only_; const char *largest_arg_type(size_t arg_n); bool add_to_semaphore(int16_t val); bool resolve_global_address(uint64_t *global, const std::string &bin_path, const uint64_t addr); bool lookup_semaphore_addr(uint64_t *address); void add_location(uint64_t addr, const std::string &bin_path, const char *fmt); public: Probe(const char *bin_path, const char *provider, const char *name, uint64_t semaphore, uint64_t semaphore_offset, const optional &pid, uint8_t mod_match_inode_only = 1); size_t num_locations() const { return locations_.size(); } size_t num_arguments() const { return locations_.front().arguments_.size(); } uint64_t semaphore() const { return semaphore_; } uint64_t semaphore_offset() const { return semaphore_offset_; } uint64_t address(size_t n = 0) const { return locations_[n].address_; } const char *location_bin_path(size_t n = 0) const { return locations_[n].bin_path_.c_str(); } const Location &location(size_t n) const { return locations_[n]; } bool usdt_getarg(std::ostream &stream); bool usdt_getarg(std::ostream &stream, const std::string& probe_func); std::string get_arg_ctype(int arg_index) { return largest_arg_type(arg_index); } const char *get_arg_ctype_name(int arg_index) { return largest_arg_type(arg_index); } void finalize_locations(); bool need_enable() const { return semaphore_ != 0x0; } bool enable(const std::string &fn_name); bool disable(); bool enabled() const { return !!attached_to_; } bool in_shared_object(const std::string &bin_path); const std::string &name() { return name_; } const std::string &bin_path() { return bin_path_; } const std::string &provider() { return provider_; } friend class Context; friend class ::ebpf::BPF; friend class ::ebpf::USDT; }; class Context { std::vector> probes_; std::unordered_set modules_; optional pid_; optional pid_stat_; std::string cmd_bin_path_; bool loaded_; static void _each_probe(const char *binpath, const struct bcc_elf_usdt *probe, void *p); static int _each_module(mod_info *, int enter_ns, void *p); void add_probe(const char *binpath, const struct bcc_elf_usdt *probe); std::string resolve_bin_path(const std::string &bin_path); Probe *get_checked(const std::string &provider_name, const std::string &probe_name); private: uint8_t mod_match_inode_only_; public: Context(const std::string &bin_path, uint8_t mod_match_inode_only = 1); Context(int pid, uint8_t mod_match_inode_only = 1); Context(int pid, const std::string &bin_path, uint8_t mod_match_inode_only = 1); ~Context(); optional pid() const { return pid_; } bool loaded() const { return loaded_; } size_t num_probes() const { return probes_.size(); } const std::string & cmd_bin_path() const { return cmd_bin_path_; } Probe *get(const std::string &probe_name); Probe *get(const std::string &provider_name, const std::string &probe_name); Probe *get(int pos) { return probes_[pos].get(); } bool enable_probe(const std::string &probe_name, const std::string &fn_name); bool enable_probe(const std::string &provider_name, const std::string &probe_name, const std::string &fn_name); bool addsem_probe(const std::string &provider_name, const std::string &probe_name, const std::string &fn_name, int16_t val); typedef void (*each_cb)(struct bcc_usdt *); void each(each_cb callback); typedef void (*each_uprobe_cb)(const char *, const char *, uint64_t, int); void each_uprobe(each_uprobe_cb callback); friend class ::ebpf::BPF; friend class ::ebpf::USDT; }; }