• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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