Skip to content

Commit

Permalink
link/kprobe: specify symbol offset
Browse files Browse the repository at this point in the history
This patch adds kprobe symbol offset and its unit test.
With symbol offset, we can insert kprobes for functions
inlined.

Signed-off-by: Tonghao Zhang <zhangtonghao@didiglobal.com>
  • Loading branch information
xpu22 committed Apr 12, 2022
1 parent 2b0d817 commit cf27494
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
22 changes: 21 additions & 1 deletion link/kprobe.go
Expand Up @@ -50,6 +50,10 @@ type KprobeOptions struct {
//
// Needs kernel 5.15+.
Cookie uint64
// Offset of the kprobe relative to the traced symbol.
// Can be used to insert kprobes at arbitrary offsets in kernel functions,
// e.g. in places where functions have been inlined.
Offset uint64
}

const (
Expand Down Expand Up @@ -163,6 +167,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*

if opts != nil {
args.cookie = opts.Cookie
args.offset = opts.Offset
}

// Use kprobe PMU if the kernel has it available.
Expand Down Expand Up @@ -235,8 +240,12 @@ func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
}

attr = unix.PerfEventAttr{
// The minimum size required for PMU kprobes is PERF_ATTR_SIZE_VER1,
// since it added the config2 (Ext2) field. Use Ext2 as probe_offset.
Size: unix.PERF_ATTR_SIZE_VER1,
Type: uint32(et), // PMU event type read from sysfs
Ext1: uint64(uintptr(sp)), // Kernel symbol to trace
Ext2: args.offset, // Kernel symbol offset
Config: config, // Retprobe flag
}
case uprobeType:
Expand Down Expand Up @@ -392,7 +401,7 @@ func createTraceFSProbeEvent(typ probeType, args probeArgs) error {
// subsampling or rate limiting logic can be more accurately implemented in
// the eBPF program itself.
// See Documentation/kprobes.txt for more details.
pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret), args.group, sanitizedSymbol(args.symbol), args.symbol)
pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret), args.group, sanitizedSymbol(args.symbol), kprobeToken(args))
case uprobeType:
// The uprobe_events syntax is as follows:
// p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe
Expand Down Expand Up @@ -496,3 +505,14 @@ func kretprobeBit() (uint64, error) {
})
return kprobeRetprobeBit.value, kprobeRetprobeBit.err
}

// kprobeToken creates the SYM[+offs] token for the tracefs api.
func kprobeToken(args probeArgs) string {
po := args.symbol

if args.offset != 0 {
po += fmt.Sprintf("+%#x", args.offset)
}

return po
}
49 changes: 49 additions & 0 deletions link/kprobe_test.go
Expand Up @@ -2,6 +2,8 @@ package link

import (
"errors"
"fmt"
"math"
"os"
"testing"

Expand Down Expand Up @@ -405,3 +407,50 @@ func TestKprobeCookie(t *testing.T) {
}
k.Close()
}

func TestKprobeToken(t *testing.T) {
tests := []struct {
args probeArgs
expected string
}{
{probeArgs{symbol: "symbol"}, "symbol"},
{probeArgs{symbol: "symbol", offset: 1}, "symbol+0x1"},
{probeArgs{symbol: "symbol", offset: 65535}, "symbol+0xffff"},
{probeArgs{symbol: "symbol", offset: 65536}, "symbol+0x10000"},
}

for i, tt := range tests {
t.Run(fmt.Sprint(i), func(t *testing.T) {
po := kprobeToken(tt.args)
if tt.expected != po {
t.Errorf("Expected symbol+offset to be '%s', got '%s'", tt.expected, po)
}
})
}
}

// Test Kprobe with Offset
func TestKprobeOffset(t *testing.T) {
tests := []struct {
offset uint64
ok bool
}{
{0, true},
{math.MaxUint64, false},
}

prog := mustLoadProgram(t, ebpf.Kprobe, 0, "")
for i, tt := range tests {
t.Run(fmt.Sprint(i), func(t *testing.T) {
k, err := Kprobe(ksym, prog, &KprobeOptions{Offset: tt.offset})

ok := err == nil
if tt.ok != ok {
t.Errorf("Expected symbol+offset load %v', got '%v'", tt.ok, ok)
}
if ok {
k.Close()
}
})
}
}

0 comments on commit cf27494

Please sign in to comment.