1--[[ 2Copyright 2016 GitHub, Inc 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15]] 16local ffi = require("ffi") 17local libbcc = require("bcc.libbcc") 18 19local TracerPipe = require("bcc.tracerpipe") 20local Table = require("bcc.table") 21local Sym = require("bcc.sym") 22 23local Bpf = class("BPF") 24 25Bpf.static.open_kprobes = {} 26Bpf.static.open_uprobes = {} 27Bpf.static.perf_buffers = {} 28Bpf.static.KPROBE_LIMIT = 1000 29Bpf.static.tracer_pipe = nil 30Bpf.static.DEFAULT_CFLAGS = { 31 '-D__HAVE_BUILTIN_BSWAP16__', 32 '-D__HAVE_BUILTIN_BSWAP32__', 33 '-D__HAVE_BUILTIN_BSWAP64__', 34} 35 36function Bpf.static.check_probe_quota(n) 37 local cur = table.count(Bpf.static.open_kprobes) + table.count(Bpf.static.open_uprobes) 38 assert(cur + n <= Bpf.static.KPROBE_LIMIT, "number of open probes would exceed quota") 39end 40 41function Bpf.static.cleanup() 42 local function detach_all(probe_type, all_probes) 43 for key, fd in pairs(all_probes) do 44 libbcc.bpf_close_perf_event_fd(fd) 45 -- skip bcc-specific kprobes 46 if not key:starts("bcc:") then 47 if probe_type == "kprobes" then 48 libbcc.bpf_detach_kprobe(key) 49 elseif probe_type == "uprobes" then 50 libbcc.bpf_detach_uprobe(key) 51 end 52 end 53 all_probes[key] = nil 54 end 55 end 56 57 detach_all("kprobes", Bpf.static.open_kprobes) 58 detach_all("uprobes", Bpf.static.open_uprobes) 59 60 for key, perf_buffer in pairs(Bpf.static.perf_buffers) do 61 libbcc.perf_reader_free(perf_buffer) 62 Bpf.static.perf_buffers[key] = nil 63 end 64 65 if Bpf.static.tracer_pipe ~= nil then 66 Bpf.static.tracer_pipe:close() 67 end 68end 69 70function Bpf.static.SymbolCache(pid) 71 return Sym.create_cache(pid) 72end 73 74function Bpf.static.num_open_uprobes() 75 return table.count(Bpf.static.open_uprobes) 76end 77 78function Bpf.static.num_open_kprobes() 79 return table.count(Bpf.static.open_kprobes) 80end 81 82Bpf.static.SCRIPT_ROOT = "./" 83function Bpf.static.script_root(root) 84 local dir, file = root:match'(.*/)(.*)' 85 Bpf.static.SCRIPT_ROOT = dir or "./" 86 return Bpf 87end 88 89local function _find_file(script_root, filename) 90 if filename == nil then 91 return nil 92 end 93 94 if os.exists(filename) then 95 return filename 96 end 97 98 if not filename:starts("/") then 99 filename = script_root .. filename 100 if os.exists(filename) then 101 return filename 102 end 103 end 104 105 assert(nil, "failed to find file "..filename.." (root="..script_root..")") 106end 107 108function Bpf:initialize(args) 109 self.funcs = {} 110 self.tables = {} 111 112 if args.usdt and args.text then 113 args.text = args.usdt:_get_text() .. args.text 114 end 115 116 local cflags = table.join(Bpf.DEFAULT_CFLAGS, args.cflags) 117 local cflags_ary = ffi.new("const char *[?]", #cflags, cflags) 118 119 local llvm_debug = rawget(_G, "LIBBCC_LLVM_DEBUG") or args.debug or 0 120 assert(type(llvm_debug) == "number") 121 122 if args.text then 123 log.info("\n%s\n", args.text) 124 self.module = libbcc.bpf_module_create_c_from_string(args.text, llvm_debug, cflags_ary, #cflags) 125 elseif args.src_file then 126 local src = _find_file(Bpf.SCRIPT_ROOT, args.src_file) 127 128 if src:ends(".b") then 129 local hdr = _find_file(Bpf.SCRIPT_ROOT, args.hdr_file) 130 self.module = libbcc.bpf_module_create_b(src, hdr, llvm_debug) 131 else 132 self.module = libbcc.bpf_module_create_c(src, llvm_debug, cflags_ary, #cflags) 133 end 134 end 135 136 assert(self.module ~= nil, "failed to compile BPF module") 137 138 if args.usdt then 139 args.usdt:_attach_uprobes(self) 140 end 141end 142 143function Bpf:load_funcs(prog_type) 144 prog_type = prog_type or "BPF_PROG_TYPE_KPROBE" 145 146 local result = {} 147 local fn_count = tonumber(libbcc.bpf_num_functions(self.module)) 148 149 for i = 0,fn_count-1 do 150 local name = ffi.string(libbcc.bpf_function_name(self.module, i)) 151 table.insert(result, self:load_func(name, prog_type)) 152 end 153 154 return result 155end 156 157function Bpf:load_func(fn_name, prog_type) 158 if self.funcs[fn_name] ~= nil then 159 return self.funcs[fn_name] 160 end 161 162 assert(libbcc.bpf_function_start(self.module, fn_name) ~= nil, 163 "unknown program: "..fn_name) 164 165 local fd = libbcc.bpf_prog_load(prog_type, 166 fn_name, 167 libbcc.bpf_function_start(self.module, fn_name), 168 libbcc.bpf_function_size(self.module, fn_name), 169 libbcc.bpf_module_license(self.module), 170 libbcc.bpf_module_kern_version(self.module), 171 0, nil, 0) 172 173 assert(fd >= 0, "failed to load BPF program "..fn_name) 174 log.info("loaded %s (%d)", fn_name, fd) 175 176 local fn = {bpf=self, name=fn_name, fd=fd} 177 self.funcs[fn_name] = fn 178 return fn 179end 180 181function Bpf:dump_func(fn_name) 182 local start = libbcc.bpf_function_start(self.module, fn_name) 183 assert(start ~= nil, "unknown program") 184 185 local len = libbcc.bpf_function_size(self.module, fn_name) 186 return ffi.string(start, tonumber(len)) 187end 188 189function Bpf:attach_uprobe(args) 190 Bpf.check_probe_quota(1) 191 192 local path, addr = Sym.check_path_symbol(args.name, args.sym, args.addr, args.pid) 193 local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE') 194 local ptype = args.retprobe and "r" or "p" 195 local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr) 196 local retprobe = args.retprobe and 1 or 0 197 198 local res = libbcc.bpf_attach_uprobe(fn.fd, retprobe, ev_name, path, addr, 199 args.pid or -1) 200 201 assert(res >= 0, "failed to attach BPF to uprobe") 202 self:probe_store("uprobe", ev_name, res) 203 return self 204end 205 206function Bpf:attach_kprobe(args) 207 -- TODO: allow the caller to glob multiple functions together 208 Bpf.check_probe_quota(1) 209 210 local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE') 211 local event = args.event or "" 212 local ptype = args.retprobe and "r" or "p" 213 local ev_name = string.format("%s_%s", ptype, event:gsub("[%+%.]", "_")) 214 local retprobe = args.retprobe and 1 or 0 215 216 local res = libbcc.bpf_attach_kprobe(fn.fd, retprobe, ev_name, event) 217 218 assert(res >= 0, "failed to attach BPF to kprobe") 219 self:probe_store("kprobe", ev_name, res) 220 return self 221end 222 223function Bpf:pipe() 224 if Bpf.tracer_pipe == nil then 225 Bpf.tracer_pipe = TracerPipe:new() 226 end 227 return Bpf.tracer_pipe 228end 229 230function Bpf:get_table(name, key_type, leaf_type) 231 if self.tables[name] == nil then 232 self.tables[name] = Table(self, name, key_type, leaf_type) 233 end 234 return self.tables[name] 235end 236 237function Bpf:probe_store(t, id, fd) 238 if t == "kprobe" then 239 Bpf.open_kprobes[id] = fd 240 elseif t == "uprobe" then 241 Bpf.open_uprobes[id] = fd 242 else 243 error("unknown probe type '%s'" % t) 244 end 245 246 log.info("%s -> %s", id, fd) 247end 248 249function Bpf:perf_buffer_store(id, reader) 250 Bpf.perf_buffers[id] = reader 251 252 log.info("%s -> %s", id, reader) 253end 254 255function Bpf:probe_lookup(t, id) 256 if t == "kprobe" then 257 return Bpf.open_kprobes[id] 258 elseif t == "uprobe" then 259 return Bpf.open_uprobes[id] 260 else 261 return nil 262 end 263end 264 265function Bpf:_perf_buffer_array() 266 local perf_buffer_count = table.count(Bpf.perf_buffers) 267 local readers = ffi.new("struct perf_reader*[?]", perf_buffer_count) 268 local n = 0 269 270 for _, r in pairs(Bpf.perf_buffers) do 271 readers[n] = r 272 n = n + 1 273 end 274 275 assert(n == perf_buffer_count) 276 return readers, n 277end 278 279function Bpf:perf_buffer_poll_loop() 280 local perf_buffers, perf_buffer_count = self:_perf_buffer_array() 281 return pcall(function() 282 while true do 283 libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, -1) 284 end 285 end) 286end 287 288function Bpf:kprobe_poll_loop() 289 return self:perf_buffer_poll_loop() 290end 291 292function Bpf:perf_buffer_poll(timeout) 293 local perf_buffers, perf_buffer_count = self:_perf_buffer_array() 294 libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, timeout or -1) 295end 296 297function Bpf:kprobe_poll(timeout) 298 self:perf_buffer_poll(timeout) 299end 300 301return Bpf 302