Commit 55d75ca8 authored by Vicent Marti's avatar Vicent Marti

lua: Use the new native Symbol resolver

The static `BPF.SymbolCache` now uses a native symbol resolver instead
of the Lua/binutils implementation. Likewise for the kernel symbol
resolver, and the `check_path_symbol` API to find a probe's hook
address.
parent 664249b7
...@@ -139,12 +139,12 @@ return function(BPF, utils) ...@@ -139,12 +139,12 @@ return function(BPF, utils)
bpf:attach_kprobe{event="kfree", fn_name="free_enter"} bpf:attach_kprobe{event="kfree", fn_name="free_enter"}
end end
local syms = args.pid and utils.sym.ProcSymbols:new(args.pid) or utils.sym.KSymbols:new() local syms = BPF.SymbolCache(args.pid)
local allocs = bpf:get_table("allocs") local allocs = bpf:get_table("allocs")
local stack_traces = bpf:get_table("stack_traces") local stack_traces = bpf:get_table("stack_traces")
local function resolve(addr) local function resolve(addr)
local sym = syms:lookup(addr) local sym = syms:resolve(addr)
if args.pid == nil then if args.pid == nil then
sym = sym .. " [kernel]" sym = sym .. " [kernel]"
end end
......
...@@ -79,7 +79,7 @@ return function(BPF, utils) ...@@ -79,7 +79,7 @@ return function(BPF, utils)
parser:option("-d --duration", "duration to trace for", 9999999):convert(tonumber) parser:option("-d --duration", "duration to trace for", 9999999):convert(tonumber)
local args = parser:parse() local args = parser:parse()
local ksym = utils.sym.KSymbols:new() local ksym = BPF.SymbolCache()
local filter = "1" local filter = "1"
local MAXDEPTH = 20 local MAXDEPTH = 20
...@@ -107,7 +107,7 @@ return function(BPF, utils) ...@@ -107,7 +107,7 @@ return function(BPF, utils)
for k, v in counts:items() do for k, v in counts:items() do
for addr in stack_traces:walk(tonumber(k.stack_id)) do for addr in stack_traces:walk(tonumber(k.stack_id)) do
print(" %-16p %s" % {addr, ksym:lookup(addr)}) print(" %-16p %s" % {addr, ksym:resolve(addr)})
end end
print(" %-16s %s" % {"-", ffi.string(k.name)}) print(" %-16s %s" % {"-", ffi.string(k.name)})
print(" %d\n" % tonumber(v)) print(" %d\n" % tonumber(v))
......
...@@ -18,13 +18,12 @@ local libbcc = require("bcc.libbcc") ...@@ -18,13 +18,12 @@ local libbcc = require("bcc.libbcc")
local TracerPipe = require("bcc.tracerpipe") local TracerPipe = require("bcc.tracerpipe")
local Table = require("bcc.table") local Table = require("bcc.table")
local LD = require("bcc.ld") local Sym = require("bcc.sym")
local Bpf = class("BPF") local Bpf = class("BPF")
Bpf.static.open_kprobes = {} Bpf.static.open_kprobes = {}
Bpf.static.open_uprobes = {} Bpf.static.open_uprobes = {}
Bpf.static.process_symbols = {}
Bpf.static.KPROBE_LIMIT = 1000 Bpf.static.KPROBE_LIMIT = 1000
Bpf.static.tracer_pipe = nil Bpf.static.tracer_pipe = nil
Bpf.static.DEFAULT_CFLAGS = { Bpf.static.DEFAULT_CFLAGS = {
...@@ -63,6 +62,10 @@ function Bpf.static.cleanup_probes() ...@@ -63,6 +62,10 @@ function Bpf.static.cleanup_probes()
end end
end end
function Bpf.static.SymbolCache(pid)
return Sym.create_cache(pid)
end
function Bpf.static.num_open_uprobes() function Bpf.static.num_open_uprobes()
return table.count(Bpf.static.open_uprobes) return table.count(Bpf.static.open_uprobes)
end end
...@@ -71,19 +74,6 @@ function Bpf.static.num_open_kprobes() ...@@ -71,19 +74,6 @@ function Bpf.static.num_open_kprobes()
return table.count(Bpf.static.open_kprobes) return table.count(Bpf.static.open_kprobes)
end end
function Bpf.static.usymaddr(pid, addr, refresh)
local proc_sym = Bpf.static.process_symbols[pid]
if proc_sym == nil then
proc_sym = ProcSymbols(pid)
Bpf.static.process_symbols[pid] = proc_sym
elseif refresh then
proc_sym.refresh()
end
return proc_sym.decode_addr(addr)
end
Bpf.static.SCRIPT_ROOT = "./" Bpf.static.SCRIPT_ROOT = "./"
function Bpf.static.script_root(root) function Bpf.static.script_root(root)
local dir, file = root:match'(.*/)(.*)' local dir, file = root:match'(.*/)(.*)'
...@@ -185,7 +175,7 @@ end ...@@ -185,7 +175,7 @@ end
function Bpf:attach_uprobe(args) function Bpf:attach_uprobe(args)
Bpf.check_probe_quota(1) Bpf.check_probe_quota(1)
local path, addr = LD.check_path_symbol(args.name, args.sym, args.addr) local path, addr = Sym.check_path_symbol(args.name, args.sym, args.addr)
local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE') local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE')
local ptype = args.retprobe and "r" or "p" local ptype = args.retprobe and "r" or "p"
local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr) local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr)
......
...@@ -13,166 +13,34 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ...@@ -13,166 +13,34 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
]] ]]
local posix = require("bcc.vendor.posix") local ffi = require("ffi")
local ProcSymbols = class("ProcSymbols") local libbcc = require("bcc.libbcc")
local SYM = ffi.typeof("struct bcc_symbol[1]")
function ProcSymbols:initialize(pid) local function create_cache(pid)
self.pid = pid return {
self:refresh() _CACHE = libbcc.bcc_symcache_new(pid or -1),
end resolve = function(self, addr)
local sym = SYM()
function ProcSymbols:_get_exe() if libbcc.bcc_symcache_resolve(self._CACHE, addr, sym) < 0 then
return os.spawn("readlink -f /proc/%d/exe", self.pid) return "[unknown]", 0x0
end
function ProcSymbols:_get_start_time()
return tonumber(os.spawn("cut -d' ' -f 22 /proc/%d/stat", self.pid))
end
function ProcSymbols:_get_code_ranges()
local function is_binary_segment(parts)
if #parts ~= 6 then return false end
if parts[6]:starts("[") then return false end
if parts[2]:find("x") == nil then return false end
return true
end
local ranges = {}
local cmd = string.format("/proc/%d/maps", self.pid)
for line in io.lines(cmd) do
local parts = line:split()
if is_binary_segment(parts) then
local binary = parts[6]
local range = parts[1]:split("-", true)
assert(#range == 2)
ranges[binary] = {posix.tonumber64(range[1], 16), posix.tonumber64(range[2], 16)}
end
end
return ranges
end
function ProcSymbols:refresh()
self.code_ranges = self:_get_code_ranges()
self.ranges_cache = {}
self.exe = self:_get_exe()
self.start_time = self:_get_start_time()
end
function ProcSymbols:_check_pid_wrap()
local new_exe = self:_get_exe()
local new_time = self:_get_start_time()
if self.exe ~= new_exe or self.start_time ~= new_time then
self:refresh()
end
end
function ProcSymbols:_get_sym_ranges(binary)
if self.ranges_cache[binary] ~= nil then
return self.ranges_cache[binary]
end
local function is_function_sym(parts)
return #parts == 6 and parts[4] == ".text" and parts[3] == "F"
end
local sym_ranges = {}
local proc = assert(io.popen("objdump -t "..binary))
for line in proc:lines() do
local parts = line:split()
if is_function_sym(parts) then
local sym_start = posix.tonumber64(parts[1], 16)
local sym_len = posix.tonumber64(parts[5], 16)
local sym_name = parts[6]
sym_ranges[sym_name] = {sym_start, sym_len}
end
end
proc:close()
self.ranges_cache[binary] = sym_ranges
return sym_ranges
end
function ProcSymbols:_decode_sym(binary, offset)
local sym_ranges = self:_get_sym_ranges(binary)
for name, range in pairs(sym_ranges) do
local start = range[1]
local length = range[2]
if offset >= start and offset <= (start + length) then
return string.format("%s+0x%p", name, offset - start)
end
end
return string.format("%p", offset)
end
function ProcSymbols:lookup(addr)
self:_check_pid_wrap()
for binary, range in pairs(self.code_ranges) do
local start = range[1]
local tend = range[2]
if addr >= start and addr <= tend then
local offset = binary:ends(".so") and (addr - start) or addr
return string.format("%s [%s]", self:_decode_sym(binary, offset), binary)
end end
return ffi.string(sym[0].name), sym[0].offset
end end
}
return string.format("%p", addr)
end end
local KSymbols = class("KSymbols") local function check_path_symbol(module, symname, addr)
local sym = SYM()
KSymbols.static.KALLSYMS = "/proc/kallsyms" if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, sym) < 0 then
if sym[0].module == nil then
function KSymbols:initialize() error("could not find library '%s' in the library path" % module)
self.ksyms = {}
self.ksym_names = {}
self.loaded = false
end
function KSymbols:_load()
if self.loaded then return end
local first_line = true
for line in io.lines(KSymbols.KALLSYMS) do
if not first_line then
local cols = line:split()
local name = cols[3]
local addr = posix.tonumber64(cols[1], 16)
table.insert(self.ksyms, {name, addr})
self.ksym_names[name] = #self.ksyms
end
first_line = false
end
self.loaded = true
end
function KSymbols:_addr2index(addr)
self:_load()
return table.bsearch(self.ksyms, addr, function(v) return v[2] end)
end
function KSymbols:lookup(addr, with_offset)
local idx = self:_addr2index(addr)
if idx == nil then
return "[unknown]"
end
if with_offset then
local offset = addr - self.ksyms[idx][2]
return "%s %p" % {self.ksyms[idx][1], offset}
else else
return self.ksyms[idx][1] error("failed to resolve symbol '%s' in '%s'" % {
symname, ffi.string(sym[0].module)})
end end
end
return ffi.string(sym[0].module), sym[0].offset
end end
function KSymbols:refresh() return { create_cache=create_cache, check_path_symbol=check_path_symbol }
-- NOOP
end
return { ProcSymbols=ProcSymbols, KSymbols=KSymbols }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment