Skip to content

Commit 742463b

Browse files
authored
Merge pull request #29 from jpsamaroo/jps/stacks-on-stacks
Add stack helpers and more
2 parents 5daf45d + 0eabaa0 commit 742463b

File tree

10 files changed

+321
-90
lines changed

10 files changed

+321
-90
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "BPFnative"
22
uuid = "b6338580-32ea-11e9-1791-33a79977d8c4"
33
authors = ["Julian P Samaroo <[email protected]>"]
4-
version = "0.1.4"
4+
version = "0.1.5"
55

66
[deps]
77
CBinding = "d43a6710-96b8-4a2d-833c-c424785e5374"

src/BPFnative.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ end
6666
# Runtime API
6767
module RT
6868
import ..API
69+
using ..LLVM
70+
using ..LLVM.Interop
71+
import Core: LLVMPtr
72+
include("runtime/maps_core.jl")
6973
include("runtime/bpfcall.jl")
7074
include("runtime/maps.jl")
7175
include("runtime/buffers.jl")
@@ -79,6 +83,7 @@ import ..API
7983
include("host/syscall.jl")
8084
include("host/maps.jl")
8185
include("host/socket.jl")
86+
include("host/kallsyms.jl")
8287
end
8388

8489
# Compiler

src/common.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ end
255255
sk_assign
256256
end
257257

258+
const PERF_MAX_STACK_DEPTH = 127
259+
258260
# Kernel structures
259261

260262
if has_vmlinux

src/host/kallsyms.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function find_ksym(addr::UInt64)
2+
start, stop = UInt64(0), addr
3+
rgx = r"([0-9abcdef]*) ([a-zA-Z]) ([0-9a-zA-Z_\-]*)"
4+
last_addr = start
5+
last_kind = "?"
6+
last_name = ""
7+
for line in readlines(open("/proc/kallsyms", "r"))
8+
m = match(rgx, line)
9+
@assert m !== nothing
10+
start_addr, kind, name = m.captures
11+
start_addr = parse(UInt64, "0x"*start_addr)
12+
if start_addr > stop
13+
return last_addr, last_kind, last_name
14+
elseif start_addr == stop
15+
return addr, kind, name
16+
end
17+
last_addr = addr
18+
last_kind = kind
19+
last_name = name
20+
end
21+
end
22+
function stack_to_string(nt::NTuple{N,UInt64}) where N
23+
iob = IOBuffer()
24+
for i in 1:N
25+
addr = nt[i]
26+
if addr == UInt64(0)
27+
break
28+
end
29+
println(iob, "$(find_ksym(addr)[3])")
30+
end
31+
String(take!(iob))
32+
end

src/host/maps.jl

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct HashMap{K,V} <: AbstractHashMap{K,V}
1515
fd::Cint
1616
end
1717
maptype_to_jltype(::Val{API.BPF_MAP_TYPE_HASH}) = HashMap
18+
maptype_to_jltype(::Val{API.BPF_MAP_TYPE_STACK_TRACE}) = HashMap
1819
struct ArrayMap{K,V} <: AbstractArrayMap{K,V}
1920
fd::Cint
2021
end
@@ -45,8 +46,25 @@ struct map_access_elem_attr
4546
flags::UInt64
4647
end
4748

49+
memset!(ptr::Ptr{T}) where T =
50+
ccall(:memset, Cvoid,
51+
(Ptr{T}, UInt8, UInt64),
52+
ptr, UInt8(0), sizeof(T))
53+
"Creates a Ref{T} that's been zero-initialized before being stored. Necessary
54+
to ensure that struct padding bytes are zeroed."
55+
function ZeroInitRef(T, val; set=true)
56+
ref = Ref{T}()
57+
memset!(Base.unsafe_convert(Ptr{T}, ref))
58+
if set
59+
ref[] = val
60+
end
61+
ref
62+
end
63+
ZeroInitRef(val::T) where T = ZeroInitRef(T, val)
64+
ZeroInitRef(::Type{T}) where T = ZeroInitRef(T, nothing; set=false)
65+
4866
function Base.getindex(map::AbstractHashMap{K,V}, idx) where {K,V}
49-
key = Ref{K}(idx)
67+
key = ZeroInitRef(K, idx)
5068
value = Ref{V}()
5169
key_ptr = Base.unsafe_convert(Ptr{K}, key)
5270
value_ptr = Base.unsafe_convert(Ptr{V}, value)
@@ -61,7 +79,7 @@ function Base.getindex(map::AbstractHashMap{K,V}, idx) where {K,V}
6179
value[]
6280
end
6381
function Base.getindex(map::AbstractArrayMap{K,V}, idx) where {K,V}
64-
key = Ref{K}(idx-1)
82+
key = ZeroInitRef(K, idx-1)
6583
value = Ref{V}()
6684
key_ptr = Base.unsafe_convert(Ptr{K}, key)
6785
value_ptr = Base.unsafe_convert(Ptr{V}, value)
@@ -77,8 +95,8 @@ function Base.getindex(map::AbstractArrayMap{K,V}, idx) where {K,V}
7795
end
7896

7997
function Base.setindex!(map::AbstractHashMap{K,V}, value::U, idx) where {K,V,U}
80-
key_ref = Ref{K}(convert(K,idx))
81-
value_ref = Ref{V}(convert(V,value))
98+
key_ref = ZeroInitRef(convert(K,idx))
99+
value_ref = ZeroInitRef(convert(V,value))
82100
key_ptr = Base.unsafe_convert(Ptr{K}, key_ref)
83101
value_ptr = Base.unsafe_convert(Ptr{V}, value_ref)
84102
attr = Ref(map_access_elem_attr(map.fd,
@@ -92,8 +110,8 @@ function Base.setindex!(map::AbstractHashMap{K,V}, value::U, idx) where {K,V,U}
92110
value
93111
end
94112
function Base.setindex!(map::AbstractArrayMap{K,V}, value::U, idx) where {K,V,U}
95-
key_ref = Ref{K}(convert(K,idx-1))
96-
value_ref = Ref{V}(convert(V,value))
113+
key_ref = ZeroInitRef(convert(K,idx-1))
114+
value_ref = ZeroInitRef(convert(V,value))
97115
key_ptr = Base.unsafe_convert(Ptr{K}, key_ref)
98116
value_ptr = Base.unsafe_convert(Ptr{V}, value_ref)
99117
attr = Ref(map_access_elem_attr(map.fd,
@@ -108,7 +126,7 @@ function Base.setindex!(map::AbstractArrayMap{K,V}, value::U, idx) where {K,V,U}
108126
end
109127

110128
function Base.delete!(map::AbstractHashMap{K,V}, idx) where {K,V}
111-
key = Ref{K}(idx)
129+
key = ZeroInitRef(K, idx)
112130
key_ptr = Base.unsafe_convert(Ptr{K}, key)
113131
attr = Ref(map_access_elem_attr(map.fd,
114132
key_ptr,
@@ -121,7 +139,7 @@ function Base.delete!(map::AbstractHashMap{K,V}, idx) where {K,V}
121139
end
122140

123141
function Base.haskey(map::HostMap{K,V}, idx) where {K,V}
124-
key = Ref{K}(idx)
142+
key = ZeroInitRef(K, idx)
125143
value = Ref{V}()
126144
key_ptr = Base.unsafe_convert(Ptr{K}, key)
127145
value_ptr = Base.unsafe_convert(Ptr{V}, value)
@@ -134,4 +152,47 @@ function Base.haskey(map::HostMap{K,V}, idx) where {K,V}
134152
end
135153
end
136154

137-
# TODO: keys(map) using BPF_MAP_GET_NEXT_KEY
155+
function nextkey(map::HostMap{K,V}, idx) where {K,V}
156+
key = ZeroInitRef(K, idx)
157+
nkey = Ref{K}()
158+
key_ptr = Base.unsafe_convert(Ptr{K}, key)
159+
nkey_ptr = Base.unsafe_convert(Ptr{K}, nkey)
160+
attr = Ref(map_access_elem_attr(map.fd,
161+
key_ptr,
162+
nkey_ptr,
163+
0))
164+
ret = GC.@preserve key nkey begin
165+
bpf(API.BPF_MAP_GET_NEXT_KEY, attr)
166+
end
167+
if (ret == -1) && (Libc.errno() == Libc.ENOENT)
168+
return nothing
169+
end
170+
ret >= 0 || Base.systemerror(ret)
171+
nkey[]
172+
end
173+
174+
struct HostMapKeySet{K,V,H<:HostMap{K,V}}
175+
map::H
176+
end
177+
Base.keys(map::H) where {K,V,H<:HostMap{K,V}} = HostMapKeySet{K,V,H}(map)
178+
Base.IteratorSize(::Type{<:HostMapKeySet}) = Base.SizeUnknown()
179+
Base.eltype(::Type{HostMapKeySet{K,V,H}}) where {K,V,H} = K
180+
function Base.iterate(hmks::HostMapKeySet{K,V,H}) where {K,V,H}
181+
fakekey_ref = ZeroInitRef(K)
182+
realkey = if haskey(hmks.map, fakekey_ref[])
183+
fakekey_ref[]
184+
else
185+
nextkey(hmks.map, fakekey_ref[])
186+
end
187+
realkey === nothing && return nothing # empty
188+
return realkey, (realkey, realkey)
189+
end
190+
function Base.iterate(hmks::HostMapKeySet{K,V,H}, (lastkey, initial)) where {K,V,H}
191+
lastkey === nothing && return nothing
192+
nkey = nextkey(hmks.map, lastkey)
193+
if nkey === nothing || nkey == lastkey
194+
return nothing
195+
end
196+
return nkey, (nkey == initial ? nothing : nkey, initial)
197+
end
198+
Base.length(map::HostMap) = length(keys(map))

src/runtime/bpfcall.jl

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,52 @@ export bpfcall
33
@generated function _bpfcall(::Val{cmd}, ::Type{rettype}, ::Type{argtypes}, args::Vararg{Any}) where {cmd,rettype,argtypes}
44
JuliaContext() do ctx
55
T_ret = convert(LLVMType, rettype, ctx)
6-
T_args = map(x->convert(LLVMType, x, ctx), argtypes.parameters)
6+
T_jlptr = convert(LLVMType, Ptr{Cvoid}, ctx)
7+
T_ptr_i8 = LLVM.PointerType(LLVM.Int8Type(ctx))
78

8-
llvm_f, _ = create_function(T_ret, LLVMType[T_args...])
9+
outer_args = filter(arg->!(arg <: RTMap), args)
10+
T_outer_args = LLVMType[convert(LLVMType, arg, ctx) for arg in outer_args]
11+
12+
llvm_f, _ = create_function(T_ret, T_outer_args)
913
mod = LLVM.parent(llvm_f)
1014

1115
Builder(ctx) do builder
1216
entry = BasicBlock(llvm_f, "entry", ctx)
1317
position!(builder, entry)
14-
ft = LLVM.FunctionType(T_ret, LLVMType[T_args...])
18+
19+
inner_args = LLVM.Value[]
20+
T_inner_args = LLVMType[]
21+
outer_args_ex = Expr[]
22+
jlidx = 1
23+
pidx = 1
24+
for (idx, arg) in enumerate(argtypes.parameters)
25+
if arg <: RTMap
26+
map_gv = _genmap!(mod, arg, ctx)
27+
push!(inner_args, map_gv)
28+
push!(T_inner_args, llvmtype(map_gv))
29+
else
30+
llvm_arg = parameters(llvm_f)[pidx]
31+
T_arg = T_outer_args[pidx]
32+
if arg <: Ptr
33+
llvm_arg = inttoptr!(builder, llvm_arg, T_ptr_i8)
34+
T_arg = llvmtype(llvm_arg)
35+
end
36+
push!(inner_args, llvm_arg)
37+
push!(T_inner_args, T_arg)
38+
push!(outer_args_ex, Expr(:ref, :args, jlidx))
39+
pidx += 1
40+
end
41+
jlidx += 1
42+
end
43+
44+
ft = LLVM.FunctionType(T_ret, T_inner_args)
1545
ftp = LLVM.PointerType(ft)
1646
f = inttoptr!(builder, ConstantInt(cmd, ctx), ftp)
17-
value = call!(builder, f, LLVM.Value[parameters(llvm_f)...])
47+
value = call!(builder, f, inner_args)
1848
ret!(builder, value)
1949
end
20-
call_function(llvm_f, rettype, Base.to_tuple_type(args), :((args...,)))
50+
outer_args_ex = Expr(:tuple, outer_args_ex...)
51+
call_function(llvm_f, rettype, Base.to_tuple_type(outer_args), outer_args_ex)
2152
end
2253
end
2354
@inline bpfcall(cmd::API.BPFHelper, RT, AT, args...) =

src/runtime/helpers.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# BPF helpers
22

33
const ptr_sk_buff = API.pointertype(API.sk_buff)
4+
const ptr_task_struct = Ptr{Cvoid} #API.pointertype(API.task_struct)
45

56
bpfconvert(x) = x
67
bpfconvert(x::AbstractBuffer) = pointer(x)
@@ -54,4 +55,19 @@ end
5455
@inline get_current_comm(buf::AbstractSizedBuffer) =
5556
bpfcall(API.get_current_comm, Clong, Tuple{BufPtr, UInt32}, pointer(buf), length(buf))
5657

58+
@inline get_stackid(ctx::T, map::M, flags::Integer) where {T,M<:RTMap} =
59+
bpfcall(API.get_stackid, Clong, Tuple{T, M, UInt64}, ctx, map, unsafe_trunc(UInt64,flags))
60+
@inline function get_current_task()
61+
res = bpfcall(API.get_current_task, UInt64)
62+
if res > 0
63+
unsafe_load(reinterpret(ptr_task_struct, res))
64+
else
65+
nothing
66+
end
67+
end
68+
@inline get_stack(ctx::T, buf::AbstractSizedBuffer, flags::UInt64) where {T} =
69+
bpfcall(API.get_stack, Clong, Tuple{T, BufPtr, UInt32, UInt64}, ctx, pointer(buf), length(buf), flags)
70+
@inline get_task_stack(ctx::ptr_task_struct, buf::AbstractSizedBuffer, flags::UInt64) where {T} =
71+
bpfcall(API.get_task_stack, Clong, Tuple{ptr_task_struct, BufPtr, UInt32, UInt64}, ctx, pointer(buf), length(buf), flags)
72+
5773
# TODO: The rest!

0 commit comments

Comments
 (0)