From 554ae92194d82663999f9d1fe916f2c3cf2a249b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Thu, 16 Jan 2020 10:30:49 +0100 Subject: [PATCH] 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 --- GetRuntimeAddresses/symbolyze/gdb_test.go | 106 ++++++++++++++++++ .../symbolyze/testdata/.gitignore | 2 + .../symbolyze/testdata/Makefile | 12 ++ .../symbolyze/testdata/simple.c | 22 ++++ 4 files changed, 142 insertions(+) create mode 100644 GetRuntimeAddresses/symbolyze/gdb_test.go create mode 100644 GetRuntimeAddresses/symbolyze/testdata/.gitignore create mode 100644 GetRuntimeAddresses/symbolyze/testdata/Makefile create mode 100644 GetRuntimeAddresses/symbolyze/testdata/simple.c diff --git a/GetRuntimeAddresses/symbolyze/gdb_test.go b/GetRuntimeAddresses/symbolyze/gdb_test.go new file mode 100644 index 0000000..36eac45 --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/gdb_test.go @@ -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") +} diff --git a/GetRuntimeAddresses/symbolyze/testdata/.gitignore b/GetRuntimeAddresses/symbolyze/testdata/.gitignore new file mode 100644 index 0000000..2f17ffb --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/testdata/.gitignore @@ -0,0 +1,2 @@ +simple +simple.o diff --git a/GetRuntimeAddresses/symbolyze/testdata/Makefile b/GetRuntimeAddresses/symbolyze/testdata/Makefile new file mode 100644 index 0000000..0c30885 --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/testdata/Makefile @@ -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) $< + diff --git a/GetRuntimeAddresses/symbolyze/testdata/simple.c b/GetRuntimeAddresses/symbolyze/testdata/simple.c new file mode 100644 index 0000000..437019f --- /dev/null +++ b/GetRuntimeAddresses/symbolyze/testdata/simple.c @@ -0,0 +1,22 @@ +#define PY_SSIZE_T_CLEAN +#include + +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; +}