• 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 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