Test added to compare the Scanner vs GDB
gdb_test.go and testdata implement a go test to compare and verify that we get correct results from the scanner. testdata/simple.c is a small program that embeds Python and runs the same code as in runforever.py. The Makefile compiles this using python3.7. Adjustments to the flags might be needed in your environment gdb_test.go contains only one test, TestSimpleGDB, that 1. compiles simple.c 2. runs it multiple times 3. calls gdb to extract the address of symbol _PyRuntime for each pid 4. runs our Scanner 5. compares the results from gdb and our scanner
This commit is contained in:
parent
6eebe8c9f4
commit
554ae92194
106
GetRuntimeAddresses/symbolyze/gdb_test.go
Normal file
106
GetRuntimeAddresses/symbolyze/gdb_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package symbolyze
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildSimple() error {
|
||||||
|
cmd := exec.Command("make")
|
||||||
|
cmd.Dir = "testdata"
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
return cmd.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*")
|
||||||
|
scanner.OnFound(func(pid int, offset uint64) error {
|
||||||
|
t.Logf("scanner setting pid %d to offset %x", pid, offset)
|
||||||
|
ourResults[pid] = offset
|
||||||
|
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()
|
||||||
|
go 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 %d while gdb has found offset %d for pid %d", off2, off, pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchRX = regexp.MustCompile(`Symbol "_PyRuntime" is static storage at address 0x([0-9a-fA-F]+)`)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("symbol not found with gdb")
|
||||||
|
}
|
2
GetRuntimeAddresses/symbolyze/testdata/.gitignore
vendored
Normal file
2
GetRuntimeAddresses/symbolyze/testdata/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
simple
|
||||||
|
simple.o
|
12
GetRuntimeAddresses/symbolyze/testdata/Makefile
vendored
Normal file
12
GetRuntimeAddresses/symbolyze/testdata/Makefile
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
CC=gcc
|
||||||
|
PC=python3.7dm-config
|
||||||
|
PIE=-fPIE # this was necessary on my machine
|
||||||
|
CFLAGS=$(shell $(PC) --cflags)
|
||||||
|
LDFLAGS=$(shell $(PC) --ldflags)
|
||||||
|
|
||||||
|
simple: simple.o
|
||||||
|
$(CC) -o $@ $< $(LDFLAGS)
|
||||||
|
|
||||||
|
simple.o: simple.c
|
||||||
|
$(CC) -c $(CFLAGS) $(PIE) $<
|
||||||
|
|
22
GetRuntimeAddresses/symbolyze/testdata/simple.c
vendored
Normal file
22
GetRuntimeAddresses/symbolyze/testdata/simple.c
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
static char script[] = "import time\n"
|
||||||
|
"import os\n"
|
||||||
|
"\n"
|
||||||
|
"print(\"Process ID: {}\".format(os.getpid()))\n"
|
||||||
|
"print(\"Entering infinite loop ...\")\n"
|
||||||
|
"while 1:\n"
|
||||||
|
" time.sleep(2)\n"
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
Py_Initialize();
|
||||||
|
PyRun_SimpleString(script);
|
||||||
|
if (Py_FinalizeEx() < 0) {
|
||||||
|
exit(120);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user