Commit fbf7193c authored by Vicent Marti's avatar Vicent Marti

lua: Properly support high-range 64 addresses

Lua's native Number type is a 64-bit double with 52-bit precision, which
was causing rounding errors when storing and working with high-range
memory addresses (namely, addresses from the kernel, which are all in
the `0xffffffff........` range).

To work around this, we've made sure to never call `tonumber` on any
variables that represent memory addresses, and instead continue
operating on them with their native types: LuaJIT can work with the
underlying `uint64_t` type for these values and transparently perform
all kinds of numeric operations.

The only limitation of working with native 64-bit types in LuaJIT is
that they cannot be printed with the language's default `string.format`
API. To give better UX to probe writers, these APIs have been
monkeypatched so the `%p` format specifier will now properly handle
64-bit addresses and print them in an appropriate format.
parent b51da5e5
...@@ -148,7 +148,7 @@ return function(BPF, utils) ...@@ -148,7 +148,7 @@ return function(BPF, utils)
if args.pid == nil then if args.pid == nil then
sym = sym .. " [kernel]" sym = sym .. " [kernel]"
end end
return string.format("%s (%x)", sym, addr) return string.format("%s (%p)", sym, addr)
end end
local function print_outstanding() local function print_outstanding()
...@@ -174,7 +174,7 @@ return function(BPF, utils) ...@@ -174,7 +174,7 @@ return function(BPF, utils)
end end
if args.show_allocs then if args.show_allocs then
print("\taddr = %x size = %s" % {tonumber(address), tonumber(info.size)}) print("\taddr = %p size = %s" % {address, tonumber(info.size)})
end end
end end
end end
......
...@@ -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(" %-16x %s" % {addr, ksym:lookup(addr)}) print(" %-16p %s" % {addr, ksym:lookup(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))
......
...@@ -188,8 +188,8 @@ function Bpf:attach_uprobe(args) ...@@ -188,8 +188,8 @@ function Bpf:attach_uprobe(args)
local path, addr = LD.check_path_symbol(args.name, args.sym, args.addr) local path, addr = LD.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%x", ptype, path:gsub("[^%a%d]", "_"), addr) local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr)
local desc = string.format("%s:uprobes/%s %s:0x%x", ptype, ev_name, path, addr) local desc = string.format("%s:uprobes/%s %s:0x%p", ptype, ev_name, path, addr)
log.info(desc) log.info(desc)
......
...@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and ...@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
]] ]]
local ffi = require("ffi") local ffi = require("ffi")
local posix = require("bcc.vendor.posix")
local _find_library_cache = {} local _find_library_cache = {}
local function _find_library(name) local function _find_library(name)
...@@ -60,7 +61,7 @@ local function _find_load_address(path) ...@@ -60,7 +61,7 @@ local function _find_load_address(path)
path) path)
if addr then if addr then
addr = tonumber(addr, 16) addr = posix.tonumber64(addr, 16)
_find_load_address_cache[path] = addr _find_load_address_cache[path] = addr
end end
...@@ -85,7 +86,7 @@ local function _find_symbol(path, sym) ...@@ -85,7 +86,7 @@ local function _find_symbol(path, sym)
path, sym) path, sym)
if addr then if addr then
addr = tonumber(addr, 16) addr = posix.tonumber64(addr, 16)
symbols[sym] = addr symbols[sym] = addr
end end
......
...@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ...@@ -13,6 +13,7 @@ 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 ProcSymbols = class("ProcSymbols") local ProcSymbols = class("ProcSymbols")
function ProcSymbols:initialize(pid) function ProcSymbols:initialize(pid)
...@@ -46,7 +47,7 @@ function ProcSymbols:_get_code_ranges() ...@@ -46,7 +47,7 @@ function ProcSymbols:_get_code_ranges()
local range = parts[1]:split("-", true) local range = parts[1]:split("-", true)
assert(#range == 2) assert(#range == 2)
ranges[binary] = {tonumber(range[1], 16), tonumber(range[2], 16)} ranges[binary] = {posix.tonumber64(range[1], 16), posix.tonumber64(range[2], 16)}
end end
end end
...@@ -83,8 +84,8 @@ function ProcSymbols:_get_sym_ranges(binary) ...@@ -83,8 +84,8 @@ function ProcSymbols:_get_sym_ranges(binary)
for line in proc:lines() do for line in proc:lines() do
local parts = line:split() local parts = line:split()
if is_function_sym(parts) then if is_function_sym(parts) then
local sym_start = tonumber(parts[1], 16) local sym_start = posix.tonumber64(parts[1], 16)
local sym_len = tonumber(parts[5], 16) local sym_len = posix.tonumber64(parts[5], 16)
local sym_name = parts[6] local sym_name = parts[6]
sym_ranges[sym_name] = {sym_start, sym_len} sym_ranges[sym_name] = {sym_start, sym_len}
end end
...@@ -102,10 +103,10 @@ function ProcSymbols:_decode_sym(binary, offset) ...@@ -102,10 +103,10 @@ function ProcSymbols:_decode_sym(binary, offset)
local start = range[1] local start = range[1]
local length = range[2] local length = range[2]
if offset >= start and offset <= (start + length) then if offset >= start and offset <= (start + length) then
return string.format("%s+0x%x", name, offset - start) return string.format("%s+0x%p", name, offset - start)
end end
end end
return string.format("%x", offset) return string.format("%p", offset)
end end
function ProcSymbols:lookup(addr) function ProcSymbols:lookup(addr)
...@@ -121,7 +122,7 @@ function ProcSymbols:lookup(addr) ...@@ -121,7 +122,7 @@ function ProcSymbols:lookup(addr)
end end
end end
return string.format("%x", addr) return string.format("%p", addr)
end end
local KSymbols = class("KSymbols") local KSymbols = class("KSymbols")
...@@ -142,7 +143,7 @@ function KSymbols:_load() ...@@ -142,7 +143,7 @@ function KSymbols:_load()
if not first_line then if not first_line then
local cols = line:split() local cols = line:split()
local name = cols[3] local name = cols[3]
local addr = tonumber(cols[1], 16) local addr = posix.tonumber64(cols[1], 16)
table.insert(self.ksyms, {name, addr}) table.insert(self.ksyms, {name, addr})
self.ksym_names[name] = #self.ksyms self.ksym_names[name] = #self.ksyms
end end
...@@ -164,7 +165,7 @@ function KSymbols:lookup(addr, with_offset) ...@@ -164,7 +165,7 @@ function KSymbols:lookup(addr, with_offset)
if with_offset then if with_offset then
local offset = addr - self.ksyms[idx][2] local offset = addr - self.ksyms[idx][2]
return "%s %x" % {self.ksyms[idx][1], offset} return "%s %p" % {self.ksyms[idx][1], offset}
else else
return self.ksyms[idx][1] return self.ksyms[idx][1]
end end
......
...@@ -286,7 +286,7 @@ function StackTrace:walk(id) ...@@ -286,7 +286,7 @@ function StackTrace:walk(id)
return nil return nil
end end
local addr = tonumber(pstack[0].ip[i]) local addr = pstack[0].ip[i]
if addr == 0 then if addr == 0 then
return nil return nil
end end
......
do
local ffi = require("ffi")
local ptrtype = ffi.typeof("uint64_t")
local strformat = string.format
function string.format(format, ...)
local args = {...}
local match_no = 1
local newfmt, count = string.gsub(format, "()%%(.-)(%a)",
function(_, mods, t)
local n = match_no
match_no = match_no + 1
if t == 'p' and ffi.istype(ptrtype, args[n]) then
local lo = tonumber(args[n] % 4294967296ULL)
local hi = tonumber(args[n] / 4294967296ULL)
args[n] = (hi == 0) and strformat("%x", lo) or strformat("%x%08x", hi, lo)
return "%"..mods.."s"
end
end)
if count == 0 then
return strformat(format, ...)
else
return strformat(newfmt, unpack(args,1,select('#',...)))
end
end
end
function string.starts(s, p) function string.starts(s, p)
return string.sub(s, 1, string.len(p)) == p return string.sub(s, 1, string.len(p)) == p
end end
...@@ -66,7 +92,7 @@ function table.bsearch(list, value, mkval) ...@@ -66,7 +92,7 @@ function table.bsearch(list, value, mkval)
return mid return mid
end end
end end
return nil return low - 1
end end
function table.join(a, b) function table.join(a, b)
......
...@@ -29,6 +29,8 @@ int clock_nanosleep(clockid_t clock_id, int flags, ...@@ -29,6 +29,8 @@ int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *request, struct timespec *remain); const struct timespec *request, struct timespec *remain);
int get_nprocs(void); int get_nprocs(void);
uint64_t strtoull(const char *nptr, char **endptr, int base);
]] ]]
local CLOCK = { local CLOCK = {
...@@ -62,9 +64,15 @@ local function cpu_count() ...@@ -62,9 +64,15 @@ local function cpu_count()
return tonumber(ffi.C.get_nprocs()) return tonumber(ffi.C.get_nprocs())
end end
local function tonumber64(n, base)
assert(type(n) == "string")
return ffi.C.strtoull(n, nil, base or 10)
end
return { return {
time_ns=time_ns, time_ns=time_ns,
sleep=sleep, sleep=sleep,
CLOCK=CLOCK, CLOCK=CLOCK,
cpu_count=cpu_count, cpu_count=cpu_count,
tonumber64=tonumber64,
} }
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