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 offset = args.fn_offset or 0 215 local retprobe = args.retprobe and 1 or 0 216 217 local res = libbcc.bpf_attach_kprobe(fn.fd, retprobe, ev_name, event, offset) 218 219 assert(res >= 0, "failed to attach BPF to kprobe") 220 self:probe_store("kprobe", ev_name, res) 221 return self 222end 223 224function Bpf:pipe() 225 if Bpf.tracer_pipe == nil then 226 Bpf.tracer_pipe = TracerPipe:new() 227 end 228 return Bpf.tracer_pipe 229end 230 231function Bpf:get_table(name, key_type, leaf_type) 232 if self.tables[name] == nil then 233 self.tables[name] = Table(self, name, key_type, leaf_type) 234 end 235 return self.tables[name] 236end 237 238function Bpf:probe_store(t, id, fd) 239 if t == "kprobe" then 240 Bpf.open_kprobes[id] = fd 241 elseif t == "uprobe" then 242 Bpf.open_uprobes[id] = fd 243 else 244 error("unknown probe type '%s'" % t) 245 end 246 247 log.info("%s -> %s", id, fd) 248end 249 250function Bpf:perf_buffer_store(id, reader) 251 Bpf.perf_buffers[id] = reader 252 253 log.info("%s -> %s", id, reader) 254end 255 256function Bpf:probe_lookup(t, id) 257 if t == "kprobe" then 258 return Bpf.open_kprobes[id] 259 elseif t == "uprobe" then 260 return Bpf.open_uprobes[id] 261 else 262 return nil 263 end 264end 265 266function Bpf:_perf_buffer_array() 267 local perf_buffer_count = table.count(Bpf.perf_buffers) 268 local readers = ffi.new("struct perf_reader*[?]", perf_buffer_count) 269 local n = 0 270 271 for _, r in pairs(Bpf.perf_buffers) do 272 readers[n] = r 273 n = n + 1 274 end 275 276 assert(n == perf_buffer_count) 277 return readers, n 278end 279 280function Bpf:perf_buffer_poll_loop() 281 local perf_buffers, perf_buffer_count = self:_perf_buffer_array() 282 return pcall(function() 283 while true do 284 libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, -1) 285 end 286 end) 287end 288 289function Bpf:kprobe_poll_loop() 290 return self:perf_buffer_poll_loop() 291end 292 293function Bpf:perf_buffer_poll(timeout) 294 local perf_buffers, perf_buffer_count = self:_perf_buffer_array() 295 libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, timeout or -1) 296end 297 298function Bpf:kprobe_poll(timeout) 299 self:perf_buffer_poll(timeout) 300end 301 302return Bpf 303