Skip to content

Commit

Permalink
lua: Properly support high-range 64 addresses
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vmg committed Apr 15, 2016
1 parent b51da5e commit fbf7193
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 17 deletions.
4 changes: 2 additions & 2 deletions examples/lua/memleak.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/lua/offcputime.lua
Original file line number Diff line number Diff line change
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(" %-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))
Expand Down
4 changes: 2 additions & 2 deletions src/lua/bcc/bpf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 3 additions & 2 deletions src/lua/bcc/ld.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down
17 changes: 9 additions & 8 deletions src/lua/bcc/sym.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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")
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/lua/bcc/table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 27 additions & 1 deletion src/lua/bcc/vendor/helpers.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
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
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions src/lua/bcc/vendor/posix.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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,
}

0 comments on commit fbf7193

Please sign in to comment.