ebpf-challenge/GetRuntimeAddresses/symbolyze/gdb_test.go

123 lines
3.1 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package symbolyze
import (
"bufio"
"fmt"
"os"
"os/exec"
"regexp"
"strconv"
"sync"
"testing"
)
// buildSimple calls make in the testdata-directory in order to build the
// test-binary `simple` that is linked against python3.7. If this fails, make
// necessary adjustments to the Makefile and/or your environment and try again.
func buildSimple() error {
cmd := exec.Command("make", "-s")
cmd.Dir = "testdata"
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// TestSimpeGDB runs the program testdata/simple multiple times and uses gdb to
// extract the address of the symbol _PyRuntime for each pid. It compares
// those with the results from Scanner.Run().
func TestSimpleGDB(t *testing.T) {
ourResults := map[int]uint64{}
gdbResults := map[int]uint64{}
err := buildSimple()
if err != nil {
t.Fatal(err)
}
scanner := NewScanner("_PyRuntime", "*python3*")
var m sync.Mutex
scanner.OnFound(func(pid int, offset uint64) error {
t.Logf("scanner setting pid %d to offset %x", pid, offset)
m.Lock()
ourResults[pid] = offset
m.Unlock()
return nil
})
for i := 0; i < 5; i++ {
simple := exec.Command("testdata/simple")
if err := simple.Start(); err != nil {
t.Fatal(err)
}
defer func() {
simple.Process.Kill()
simple.Wait()
}()
t.Logf("Asking Gdb about PID %d", simple.Process.Pid)
offset, err := extractOffsetWithGdb(simple.Process.Pid, t)
if err != nil {
t.Fatalf("extractOffsetWithGdb: %v", err)
}
t.Logf("Found offset at 0x%x", offset)
gdbResults[simple.Process.Pid] = offset
}
if err = scanner.Run(); err != nil {
t.Logf("Scanner failure: %v", err)
}
for pid, off := range gdbResults {
if off2, ok := ourResults[pid]; !ok {
t.Fatalf("Scanner has no offset for pid %d", pid)
} else if off != off2 {
t.Fatalf("Scanner has found offset %x while gdb has found offset %x for pid %d", off2, off, pid)
} else {
t.Logf("Scanner and gdb agree: pid %d at offset %x", pid, off)
}
}
}
// searchRX should match the output of gdb when it prints the address of the
// symbol
var searchRX = regexp.MustCompile(`Symbol "_PyRuntime" is.* at.* 0x([0-9a-fA-F]+)`)
// extractOffsetWithGdb calls gdb in batch-mode on a pid and looks up the
// address of the symbol _PyRuntime by calling `info address _PyRuntime`. If
// found, it extracts the address from the gdb-output.
func extractOffsetWithGdb(pid int, t *testing.T) (offset uint64, err error) {
cmd := exec.Command("gdb",
"--batch",
"-p", strconv.Itoa(pid),
"-ex", "info address _PyRuntime",
"-ex", "quit")
output, err := cmd.StdoutPipe()
if err != nil {
return 0, err
}
if err := cmd.Start(); err != nil {
return 0, err
}
defer cmd.Wait()
scanner := bufio.NewScanner(output)
for scanner.Scan() {
line := scanner.Text()
// t.Logf("[gdb]: %v", line)
match := searchRX.FindStringSubmatch(line)
if len(match) > 1 {
return strconv.ParseUint(match[1], 16, 64)
}
}
return 0, fmt.Errorf("Symbol not found with gdb.\n" +
"Maybe /proc/sys/kernel/yama/ptrace_scope must be set to 0? Or try to run the test as root!")
}