xref: /aosp_15_r20/external/bcc/src/lua/bcc/bpf.lua (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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