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 #pragma once 17 18 #include <algorithm> 19 #include <memory> 20 #include <string> 21 #include <sys/types.h> 22 #include <unordered_map> 23 #include <unordered_set> 24 #include <vector> 25 26 #include "bcc_proc.h" 27 #include "bcc_syms.h" 28 #include "file_desc.h" 29 30 class ProcStat { 31 std::string procfs_; 32 std::string root_symlink_; 33 std::string mount_ns_symlink_; 34 // file descriptor of /proc/<pid>/root open with O_PATH used to get into root 35 // of process after it exits; unlike a dereferenced root symlink, *at calls 36 // to this use the process's mount namespace 37 int root_fd_ = -1; 38 // store also root path and mount namespace pair to detect its changes 39 std::string root_, mount_ns_; 40 ino_t inode_; 41 bool getinode_(ino_t &inode); 42 43 public: 44 ProcStat(int pid); ~ProcStat()45 ~ProcStat() { 46 if (root_fd_ > 0) 47 close(root_fd_); 48 } 49 bool refresh_root(); get_root_fd()50 int get_root_fd() { return root_fd_; } 51 bool is_stale(); reset()52 void reset() { getinode_(inode_); } 53 }; 54 55 class SymbolCache { 56 public: 57 virtual ~SymbolCache() = default; 58 59 virtual void refresh() = 0; 60 virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle = true) = 0; 61 virtual bool resolve_name(const char *module, const char *name, 62 uint64_t *addr) = 0; 63 }; 64 65 class KSyms : SymbolCache { 66 struct Symbol { SymbolSymbol67 Symbol(const char *name, const char *mod, uint64_t addr) : name(name), mod(mod), addr(addr) {} 68 std::string name; 69 std::string mod; 70 uint64_t addr; 71 72 bool operator<(const Symbol &rhs) const { return addr < rhs.addr; } 73 }; 74 75 std::vector<Symbol> syms_; 76 std::unordered_map<std::string, uint64_t> symnames_; 77 static void _add_symbol(const char *, const char *, uint64_t, void *); 78 79 public: 80 virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle = true) override; 81 virtual bool resolve_name(const char *unused, const char *name, 82 uint64_t *addr) override; 83 virtual void refresh() override; 84 }; 85 86 class ProcSyms : SymbolCache { 87 struct NameIdx { 88 size_t section_idx; 89 size_t str_table_idx; 90 size_t str_len; 91 bool debugfile; 92 }; 93 94 struct Symbol { SymbolSymbol95 Symbol(const std::string *name, uint64_t start, uint64_t size) 96 : is_name_resolved(true), start(start), size(size) { 97 data.name = name; 98 } SymbolSymbol99 Symbol(size_t section_idx, size_t str_table_idx, size_t str_len, uint64_t start, 100 uint64_t size, bool debugfile) 101 : is_name_resolved(false), start(start), size(size) { 102 data.name_idx.section_idx = section_idx; 103 data.name_idx.str_table_idx = str_table_idx; 104 data.name_idx.str_len = str_len; 105 data.name_idx.debugfile = debugfile; 106 } 107 bool is_name_resolved; 108 union { 109 struct NameIdx name_idx; 110 const std::string *name{nullptr}; 111 } data; 112 uint64_t start; 113 uint64_t size; 114 115 bool operator<(const struct Symbol& rhs) const { 116 return start < rhs.start; 117 } 118 }; 119 120 enum class ModuleType { 121 UNKNOWN, 122 EXEC, 123 SO, 124 PERF_MAP, 125 VDSO 126 }; 127 128 class ModulePath { 129 // helper class to get a usable module path independent of the running 130 // process by storing a file descriptor created from openat(2) if possible 131 // if openat fails, falls back to process-dependent path with /proc/.../root 132 private: 133 int fd_; 134 std::string proc_root_path_; 135 std::string path_; 136 137 public: 138 ModulePath(const std::string &ns_path, int root_fd, int pid, bool enter_ns); alt_path()139 const char *alt_path() { return proc_root_path_.c_str(); } path()140 const char *path() { 141 if (path_ == proc_root_path_ || access(proc_root_path_.c_str(), F_OK) < 0) 142 // cannot stat /proc/.../root/<path>, pid might not exist anymore; use /proc/self/fd/... 143 return path_.c_str(); 144 return proc_root_path_.c_str(); 145 } ~ModulePath()146 ~ModulePath() { 147 if (fd_ > 0) 148 close(fd_); 149 } 150 }; 151 152 struct Module { 153 struct Range { 154 uint64_t start; 155 uint64_t end; 156 uint64_t file_offset; RangeModule::Range157 Range(uint64_t s, uint64_t e, uint64_t f) 158 : start(s), end(e), file_offset(f) {} 159 }; 160 161 Module(const char *name, std::shared_ptr<ModulePath> path, 162 struct bcc_symbol_option *option); 163 164 std::string name_; 165 std::shared_ptr<ModulePath> path_; 166 std::vector<Range> ranges_; 167 bool loaded_; 168 bcc_symbol_option *symbol_option_; 169 ModuleType type_; 170 171 // The file offset within the ELF of the SO's first text section. 172 uint64_t elf_so_offset_; 173 uint64_t elf_so_addr_; 174 175 std::unordered_set<std::string> symnames_; 176 std::vector<Symbol> syms_; 177 178 void load_sym_table(); 179 180 bool contains(uint64_t addr, uint64_t &offset) const; startModule181 uint64_t start() const { return ranges_.begin()->start; } 182 183 bool find_addr(uint64_t offset, struct bcc_symbol *sym); 184 bool find_name(const char *symname, uint64_t *addr); 185 186 static int _add_symbol(const char *symname, uint64_t start, uint64_t size, 187 void *p); 188 static int _add_symbol_lazy(size_t section_idx, size_t str_table_idx, 189 size_t str_len, uint64_t start, uint64_t size, 190 int debugfile, void *p); 191 }; 192 193 int pid_; 194 std::vector<Module> modules_; 195 ProcStat procstat_; 196 bcc_symbol_option symbol_option_; 197 198 static int _add_module(mod_info *, int, void *); 199 void load_modules(); 200 201 public: 202 ProcSyms(int pid, struct bcc_symbol_option *option = nullptr); 203 virtual void refresh() override; 204 virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle = true) override; 205 virtual bool resolve_name(const char *module, const char *name, 206 uint64_t *addr) override; 207 }; 208 209 class BuildSyms { 210 struct Symbol { SymbolSymbol211 Symbol(const std::string *name, uint64_t start, uint64_t size) 212 :name(name), start(start), size(size) {} 213 const std::string *name; 214 uint64_t start; 215 uint64_t size; 216 217 bool operator<(const struct Symbol &rhs) const { 218 return start < rhs.start; 219 } 220 }; 221 222 struct Module { ModuleModule223 Module(const char *module_name): 224 module_name_(module_name), 225 loaded_(false) {} 226 const std::string module_name_; 227 const std::string build_id_; 228 bool loaded_; 229 std::unordered_set<std::string> symnames_; 230 std::vector<Symbol> syms_; 231 bcc_symbol_option symbol_option_; 232 233 bool load_sym_table(); 234 static int _add_symbol(const char *symname, uint64_t start, uint64_t size, 235 void *p); 236 bool resolve_addr(uint64_t offset, struct bcc_symbol*, bool demangle=true); 237 }; 238 239 std::unordered_map<std::string, std::unique_ptr<Module> > buildmap_; 240 241 public: BuildSyms()242 BuildSyms() {} 243 virtual ~BuildSyms() = default; 244 virtual bool add_module(const std::string module_name); 245 virtual bool resolve_addr(std::string build_id, uint64_t offset, struct bcc_symbol *sym, bool demangle = true); 246 }; 247