Skip to content

Commit

Permalink
lua: Use the new native Symbol resolver
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vmg committed Apr 20, 2016
1 parent 664249b commit 55d75ca
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 176 deletions.
4 changes: 2 additions & 2 deletions examples/lua/memleak.lua
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,12 @@ return function(BPF, utils)
bpf:attach_kprobe{event="kfree", fn_name="free_enter"}
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 stack_traces = bpf:get_table("stack_traces")

local function resolve(addr)
local sym = syms:lookup(addr)
local sym = syms:resolve(addr)
if args.pid == nil then
sym = sym .. " [kernel]"
end
Expand Down
4 changes: 2 additions & 2 deletions examples/lua/offcputime.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ return function(BPF, utils)
parser:option("-d --duration", "duration to trace for", 9999999):convert(tonumber)

local args = parser:parse()
local ksym = utils.sym.KSymbols:new()
local ksym = BPF.SymbolCache()
local filter = "1"
local MAXDEPTH = 20

Expand Down Expand Up @@ -107,7 +107,7 @@ return function(BPF, utils)

for k, v in counts:items() 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
print(" %-16s %s" % {"-", ffi.string(k.name)})
print(" %d\n" % tonumber(v))
Expand Down
22 changes: 6 additions & 16 deletions src/lua/bcc/bpf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ local libbcc = require("bcc.libbcc")

local TracerPipe = require("bcc.tracerpipe")
local Table = require("bcc.table")
local LD = require("bcc.ld")
local Sym = require("bcc.sym")

local Bpf = class("BPF")

Bpf.static.open_kprobes = {}
Bpf.static.open_uprobes = {}
Bpf.static.process_symbols = {}
Bpf.static.KPROBE_LIMIT = 1000
Bpf.static.tracer_pipe = nil
Bpf.static.DEFAULT_CFLAGS = {
Expand Down Expand Up @@ -63,6 +62,10 @@ function Bpf.static.cleanup_probes()
end
end

function Bpf.static.SymbolCache(pid)
return Sym.create_cache(pid)
end

function Bpf.static.num_open_uprobes()
return table.count(Bpf.static.open_uprobes)
end
Expand All @@ -71,19 +74,6 @@ function Bpf.static.num_open_kprobes()
return table.count(Bpf.static.open_kprobes)
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 = "./"
function Bpf.static.script_root(root)
local dir, file = root:match'(.*/)(.*)'
Expand Down Expand Up @@ -185,7 +175,7 @@ end
function Bpf:attach_uprobe(args)
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 ptype = args.retprobe and "r" or "p"
local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr)
Expand Down
180 changes: 24 additions & 156 deletions src/lua/bcc/sym.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
limitations under the License.
]]
local posix = require("bcc.vendor.posix")
local ProcSymbols = class("ProcSymbols")

function ProcSymbols:initialize(pid)
self.pid = pid
self:refresh()
end

function ProcSymbols:_get_exe()
return os.spawn("readlink -f /proc/%d/exe", self.pid)
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)
local ffi = require("ffi")
local libbcc = require("bcc.libbcc")
local SYM = ffi.typeof("struct bcc_symbol[1]")

local function create_cache(pid)
return {
_CACHE = libbcc.bcc_symcache_new(pid or -1),
resolve = function(self, addr)
local sym = SYM()
if libbcc.bcc_symcache_resolve(self._CACHE, addr, sym) < 0 then
return "[unknown]", 0x0
end
return ffi.string(sym[0].name), sym[0].offset
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)
local function check_path_symbol(module, symname, addr)
local sym = SYM()
if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, sym) < 0 then
if sym[0].module == nil then
error("could not find library '%s' in the library path" % module)
else
error("failed to resolve symbol '%s' in '%s'" % {
symname, ffi.string(sym[0].module)})
end
end

return string.format("%p", addr)
end

local KSymbols = class("KSymbols")

KSymbols.static.KALLSYMS = "/proc/kallsyms"

function KSymbols:initialize()
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
return self.ksyms[idx][1]
end
end

function KSymbols:refresh()
-- NOOP
return ffi.string(sym[0].module), sym[0].offset
end

return { ProcSymbols=ProcSymbols, KSymbols=KSymbols }
return { create_cache=create_cache, check_path_symbol=check_path_symbol }

0 comments on commit 55d75ca

Please sign in to comment.