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, true) 125 elseif args.src_file then 126 local src = _find_file(Bpf.SCRIPT_ROOT, args.src_file) 127 128 self.module = libbcc.bpf_module_create_c(src, llvm_debug, cflags_ary, #cflags, true) 129 end 130 131 assert(self.module ~= nil, "failed to compile BPF module") 132 133 if args.usdt then 134 args.usdt:_attach_uprobes(self) 135 end 136end 137 138function Bpf:load_funcs(prog_type) 139 prog_type = prog_type or "BPF_PROG_TYPE_KPROBE" 140 141 local result = {} 142 local fn_count = tonumber(libbcc.bpf_num_functions(self.module)) 143 144 for i = 0,fn_count-1 do 145 local name = ffi.string(libbcc.bpf_function_name(self.module, i)) 146 table.insert(result, self:load_func(name, prog_type)) 147 end 148 149 return result 150end 151 152function Bpf:load_func(fn_name, prog_type) 153 if self.funcs[fn_name] ~= nil then 154 return self.funcs[fn_name] 155 end 156 157 assert(libbcc.bpf_function_start(self.module, fn_name) ~= nil, 158 "unknown program: "..fn_name) 159 160 local fd = libbcc.bcc_prog_load(prog_type, 161 fn_name, 162 libbcc.bpf_function_start(self.module, fn_name), 163 libbcc.bpf_function_size(self.module, fn_name), 164 libbcc.bpf_module_license(self.module), 165 libbcc.bpf_module_kern_version(self.module), 166 0, nil, 0) 167 168 assert(fd >= 0, "failed to load BPF program "..fn_name) 169 log.info("loaded %s (%d)", fn_name, fd) 170 171 local fn = {bpf=self, name=fn_name, fd=fd} 172 self.funcs[fn_name] = fn 173 return fn 174end 175 176function Bpf:dump_func(fn_name) 177 local start = libbcc.bpf_function_start(self.module, fn_name) 178 assert(start ~= nil, "unknown program") 179 180 local len = libbcc.bpf_function_size(self.module, fn_name) 181 return ffi.string(start, tonumber(len)) 182end 183 184function Bpf:attach_uprobe(args) 185 Bpf.check_probe_quota(1) 186 187 local path, addr = Sym.check_path_symbol(args.name, args.sym, args.addr, args.pid, args.sym_off) 188 local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE') 189 local ptype = args.retprobe and "r" or "p" 190 local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr) 191 local retprobe = args.retprobe and 1 or 0 192 193 local res = libbcc.bpf_attach_uprobe(fn.fd, retprobe, ev_name, path, addr, 194 args.pid or -1) 195 196 assert(res >= 0, "failed to attach BPF to uprobe") 197 self:probe_store("uprobe", ev_name, res) 198 return self 199end 200 201function Bpf:attach_kprobe(args) 202 -- TODO: allow the caller to glob multiple functions together 203 Bpf.check_probe_quota(1) 204 205 local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE') 206 local event = args.event or "" 207 local ptype = args.retprobe and "r" or "p" 208 local ev_name = string.format("%s_%s", ptype, event:gsub("[%+%.]", "_")) 209 local offset = args.fn_offset or 0 210 local retprobe = args.retprobe and 1 or 0 211 local maxactive = args.maxactive or 0 212 213 local res = libbcc.bpf_attach_kprobe(fn.fd, retprobe, ev_name, event, offset, maxactive) 214 215 assert(res >= 0, "failed to attach BPF to kprobe") 216 self:probe_store("kprobe", ev_name, res) 217 return self 218end 219 220function Bpf:pipe() 221 if Bpf.tracer_pipe == nil then 222 Bpf.tracer_pipe = TracerPipe:new() 223 end 224 return Bpf.tracer_pipe 225end 226 227function Bpf:get_table(name, key_type, leaf_type) 228 if self.tables[name] == nil then 229 self.tables[name] = Table(self, name, key_type, leaf_type) 230 end 231 return self.tables[name] 232end 233 234function Bpf:probe_store(t, id, fd) 235 if t == "kprobe" then 236 Bpf.open_kprobes[id] = fd 237 elseif t == "uprobe" then 238 Bpf.open_uprobes[id] = fd 239 else 240 error("unknown probe type '%s'" % t) 241 end 242 243 log.info("%s -> %s", id, fd) 244end 245 246function Bpf:perf_buffer_store(id, reader) 247 Bpf.perf_buffers[id] = reader 248 249 log.info("%s -> %s", id, reader) 250end 251 252function Bpf:probe_lookup(t, id) 253 if t == "kprobe" then 254 return Bpf.open_kprobes[id] 255 elseif t == "uprobe" then 256 return Bpf.open_uprobes[id] 257 else 258 return nil 259 end 260end 261 262function Bpf:_perf_buffer_array() 263 local perf_buffer_count = table.count(Bpf.perf_buffers) 264 local readers = ffi.new("struct perf_reader*[?]", perf_buffer_count) 265 local n = 0 266 267 for _, r in pairs(Bpf.perf_buffers) do 268 readers[n] = r 269 n = n + 1 270 end 271 272 assert(n == perf_buffer_count) 273 return readers, n 274end 275 276function Bpf:perf_buffer_poll_loop() 277 local perf_buffers, perf_buffer_count = self:_perf_buffer_array() 278 return pcall(function() 279 while true do 280 libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, -1) 281 end 282 end) 283end 284 285function Bpf:kprobe_poll_loop() 286 return self:perf_buffer_poll_loop() 287end 288 289function Bpf:perf_buffer_poll(timeout) 290 local perf_buffers, perf_buffer_count = self:_perf_buffer_array() 291 libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, timeout or -1) 292end 293 294function Bpf:kprobe_poll(timeout) 295 self:perf_buffer_poll(timeout) 296end 297 298return Bpf 299