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)
if args.pid == nil then
sym = sym .. " [kernel]"
end
return string.format("%s (%x)", sym, addr)
return string.format("%s (%p)", sym, addr)
end
local function print_outstanding()
......@@ -174,7 +174,7 @@ return function(BPF, utils)
end
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
......
......@@ -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(" %-16x %s" % {addr, ksym:lookup(addr)})
print(" %-16p %s" % {addr, ksym:lookup(addr)})
end
print(" %-16s %s" % {"-", ffi.string(k.name)})
print(" %d\n" % tonumber(v))
......
......@@ -188,8 +188,8 @@ function Bpf:attach_uprobe(args)
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 ptype = args.retprobe and "r" or "p"
local ev_name = string.format("%s_%s_0x%x", ptype, path:gsub("[^%a%d]", "_"), addr)
local desc = string.format("%s:uprobes/%s %s:0x%x", ptype, ev_name, path, 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%p", ptype, ev_name, path, addr)
log.info(desc)
......
......@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
]]
local ffi = require("ffi")
local posix = require("bcc.vendor.posix")
local _find_library_cache = {}
local function _find_library(name)
......@@ -60,7 +61,7 @@ local function _find_load_address(path)
path)
if addr then
addr = tonumber(addr, 16)
addr = posix.tonumber64(addr, 16)
_find_load_address_cache[path] = addr
end
......@@ -85,7 +86,7 @@ local function _find_symbol(path, sym)
path, sym)
if addr then
addr = tonumber(addr, 16)
addr = posix.tonumber64(addr, 16)
symbols[sym] = addr
end
......
......@@ -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
limitations under the License.
]]
local posix = require("bcc.vendor.posix")
local ProcSymbols = class("ProcSymbols")
function ProcSymbols:initialize(pid)
......@@ -46,7 +47,7 @@ function ProcSymbols:_get_code_ranges()
local range = parts[1]:split("-", true)
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
......@@ -83,8 +84,8 @@ function ProcSymbols:_get_sym_ranges(binary)
for line in proc:lines() do
local parts = line:split()
if is_function_sym(parts) then
local sym_start = tonumber(parts[1], 16)
local sym_len = tonumber(parts[5], 16)
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
......@@ -102,10 +103,10 @@ function ProcSymbols:_decode_sym(binary, offset)
local start = range[1]
local length = range[2]
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
return string.format("%x", offset)
return string.format("%p", offset)
end
function ProcSymbols:lookup(addr)
......@@ -121,7 +122,7 @@ function ProcSymbols:lookup(addr)
end
end
return string.format("%x", addr)
return string.format("%p", addr)
end
local KSymbols = class("KSymbols")
......@@ -142,7 +143,7 @@ function KSymbols:_load()
if not first_line then
local cols = line:split()
local name = cols[3]
local addr = tonumber(cols[1], 16)
local addr = posix.tonumber64(cols[1], 16)
table.insert(self.ksyms, {name, addr})
self.ksym_names[name] = #self.ksyms
end
......@@ -164,7 +165,7 @@ function KSymbols:lookup(addr, with_offset)
if with_offset then
local offset = addr - self.ksyms[idx][2]
return "%s %x" % {self.ksyms[idx][1], offset}
return "%s %p" % {self.ksyms[idx][1], offset}
else
return self.ksyms[idx][1]
end
......
......@@ -286,7 +286,7 @@ function StackTrace:walk(id)
return nil
end
local addr = tonumber(pstack[0].ip[i])
local addr = pstack[0].ip[i]
if addr == 0 then
return nil
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)
return string.sub(s, 1, string.len(p)) == p
end
......@@ -66,7 +92,7 @@ function table.bsearch(list, value, mkval)
return mid
end
end
return nil
return low - 1
end
function table.join(a, b)
......
......@@ -29,6 +29,8 @@ int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *request, struct timespec *remain);
int get_nprocs(void);
uint64_t strtoull(const char *nptr, char **endptr, int base);
]]
local CLOCK = {
......@@ -62,9 +64,15 @@ local function cpu_count()
return tonumber(ffi.C.get_nprocs())
end
local function tonumber64(n, base)
assert(type(n) == "string")
return ffi.C.strtoull(n, nil, base or 10)
end
return {
time_ns=time_ns,
sleep=sleep,
CLOCK=CLOCK,
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