2015-06-12 19:49:23 +00:00
|
|
|
package proc
|
2014-05-20 18:23:35 +00:00
|
|
|
|
|
|
|
import (
|
2014-05-27 18:33:49 +00:00
|
|
|
"bytes"
|
2015-07-11 06:43:47 +00:00
|
|
|
"fmt"
|
2015-10-21 07:06:36 +00:00
|
|
|
"go/constant"
|
2015-07-16 00:57:54 +00:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2015-06-21 03:47:44 +00:00
|
|
|
"os"
|
2014-06-29 16:52:21 +00:00
|
|
|
"path/filepath"
|
2015-10-18 17:37:13 +00:00
|
|
|
"reflect"
|
2014-12-09 16:51:17 +00:00
|
|
|
"runtime"
|
2015-07-28 05:33:07 +00:00
|
|
|
"strings"
|
2014-05-20 18:23:35 +00:00
|
|
|
"testing"
|
2015-07-16 00:57:54 +00:00
|
|
|
"time"
|
2015-05-04 13:31:50 +00:00
|
|
|
|
2015-06-12 19:49:23 +00:00
|
|
|
protest "github.com/derekparker/delve/proc/test"
|
2014-06-25 19:06:04 +00:00
|
|
|
)
|
2014-05-30 15:12:18 +00:00
|
|
|
|
2015-06-13 04:47:30 +00:00
|
|
|
func init() {
|
2015-06-26 12:46:46 +00:00
|
|
|
runtime.GOMAXPROCS(4)
|
|
|
|
os.Setenv("GOMAXPROCS", "4")
|
2015-06-13 04:47:30 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 13:31:50 +00:00
|
|
|
func TestMain(m *testing.M) {
|
2015-06-21 03:47:44 +00:00
|
|
|
os.Exit(protest.RunTestsWithFixtures(m))
|
2015-05-04 13:31:50 +00:00
|
|
|
}
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func withTestProcess(name string, t *testing.T, fn func(p *Process, fixture protest.Fixture)) {
|
2015-05-15 14:23:58 +00:00
|
|
|
fixture := protest.BuildFixture(name)
|
2015-05-04 13:31:50 +00:00
|
|
|
p, err := Launch([]string{fixture.Path})
|
2014-12-09 16:51:17 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Launch():", err)
|
|
|
|
}
|
|
|
|
|
2015-06-24 14:44:24 +00:00
|
|
|
defer func() {
|
|
|
|
p.Halt()
|
2015-07-11 06:43:47 +00:00
|
|
|
p.Kill()
|
2015-06-24 14:44:24 +00:00
|
|
|
}()
|
2014-12-09 16:51:17 +00:00
|
|
|
|
2015-05-04 13:31:50 +00:00
|
|
|
fn(p, fixture)
|
2014-12-09 16:51:17 +00:00
|
|
|
}
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func getRegisters(p *Process, t *testing.T) Registers {
|
2014-12-09 16:35:55 +00:00
|
|
|
regs, err := p.Registers()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Registers():", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return regs
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:51:23 +00:00
|
|
|
func dataAtAddr(thread *Thread, addr uint64) ([]byte, error) {
|
2015-08-02 02:43:03 +00:00
|
|
|
return thread.readMemory(uintptr(addr), 1)
|
2014-05-27 18:33:49 +00:00
|
|
|
}
|
|
|
|
|
2014-06-29 16:52:21 +00:00
|
|
|
func assertNoError(err error, t *testing.T, s string) {
|
|
|
|
if err != nil {
|
2015-01-21 21:26:01 +00:00
|
|
|
_, file, line, _ := runtime.Caller(1)
|
|
|
|
fname := filepath.Base(file)
|
2015-06-26 12:46:46 +00:00
|
|
|
t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err)
|
2014-06-29 16:52:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func currentPC(p *Process, t *testing.T) uint64 {
|
2015-04-23 15:40:33 +00:00
|
|
|
pc, err := p.PC()
|
2014-06-29 16:52:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return pc
|
|
|
|
}
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func currentLineNumber(p *Process, t *testing.T) (string, int) {
|
2014-06-29 16:52:21 +00:00
|
|
|
pc := currentPC(p, t)
|
2015-04-03 16:10:35 +00:00
|
|
|
f, l, _ := p.goSymTable.PCToLine(pc)
|
2014-06-29 16:52:21 +00:00
|
|
|
|
2014-10-07 17:25:33 +00:00
|
|
|
return f, l
|
2014-06-29 16:52:21 +00:00
|
|
|
}
|
|
|
|
|
2015-03-05 22:59:51 +00:00
|
|
|
func TestExit(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("continuetestprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-03-05 22:59:51 +00:00
|
|
|
err := p.Continue()
|
|
|
|
pe, ok := err.(ProcessExitedError)
|
|
|
|
if !ok {
|
2015-04-25 14:46:16 +00:00
|
|
|
t.Fatalf("Continue() returned unexpected error type %s", err)
|
2015-03-05 22:59:51 +00:00
|
|
|
}
|
|
|
|
if pe.Status != 0 {
|
|
|
|
t.Errorf("Unexpected error status: %d", pe.Status)
|
|
|
|
}
|
|
|
|
if pe.Pid != p.Pid {
|
|
|
|
t.Errorf("Unexpected process id: %d", pe.Pid)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-08-07 16:50:14 +00:00
|
|
|
func setFunctionBreakpoint(p *Process, fname string) (*Breakpoint, error) {
|
|
|
|
addr, err := p.FindFunctionLocation(fname, true, 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return p.SetBreakpoint(addr)
|
|
|
|
}
|
|
|
|
|
2015-04-13 22:17:06 +00:00
|
|
|
func TestHalt(t *testing.T) {
|
2015-06-26 12:46:46 +00:00
|
|
|
stopChan := make(chan interface{})
|
|
|
|
withTestProcess("loopprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-08-07 16:50:14 +00:00
|
|
|
_, err := setFunctionBreakpoint(p, "main.loop")
|
2015-06-26 12:46:46 +00:00
|
|
|
assertNoError(err, t, "SetBreakpoint")
|
|
|
|
assertNoError(p.Continue(), t, "Continue")
|
|
|
|
for _, th := range p.Threads {
|
|
|
|
if th.running != false {
|
|
|
|
t.Fatal("expected running = false for thread", th.Id)
|
|
|
|
}
|
|
|
|
_, err := th.Registers()
|
|
|
|
assertNoError(err, t, "Registers")
|
|
|
|
}
|
2015-04-13 22:17:06 +00:00
|
|
|
go func() {
|
2015-04-26 11:31:28 +00:00
|
|
|
for {
|
|
|
|
if p.Running() {
|
2015-06-26 12:46:46 +00:00
|
|
|
if err := p.RequestManualStop(); err != nil {
|
2015-04-26 11:31:28 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-06-26 12:46:46 +00:00
|
|
|
stopChan <- nil
|
2015-04-26 11:31:28 +00:00
|
|
|
return
|
|
|
|
}
|
2015-04-13 22:17:06 +00:00
|
|
|
}
|
|
|
|
}()
|
2015-06-26 12:46:46 +00:00
|
|
|
assertNoError(p.Continue(), t, "Continue")
|
|
|
|
<-stopChan
|
2015-04-13 22:17:06 +00:00
|
|
|
// Loop through threads and make sure they are all
|
|
|
|
// actually stopped, err will not be nil if the process
|
|
|
|
// is still running.
|
|
|
|
for _, th := range p.Threads {
|
2015-08-20 14:28:11 +00:00
|
|
|
if !th.Stopped() {
|
|
|
|
t.Fatal("expected thread to be stopped, but was not")
|
|
|
|
}
|
2015-06-26 12:46:46 +00:00
|
|
|
if th.running != false {
|
|
|
|
t.Fatal("expected running = false for thread", th.Id)
|
2015-04-13 22:17:06 +00:00
|
|
|
}
|
2015-06-26 12:46:46 +00:00
|
|
|
_, err := th.Registers()
|
|
|
|
assertNoError(err, t, "Registers")
|
2015-04-13 22:17:06 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-05-30 15:12:18 +00:00
|
|
|
func TestStep(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-04-03 16:10:35 +00:00
|
|
|
helloworldfunc := p.goSymTable.LookupFunc("main.helloworld")
|
2014-11-27 02:35:53 +00:00
|
|
|
helloworldaddr := helloworldfunc.Entry
|
|
|
|
|
2015-06-20 23:01:06 +00:00
|
|
|
_, err := p.SetBreakpoint(helloworldaddr)
|
|
|
|
assertNoError(err, t, "SetBreakpoint()")
|
2014-11-27 02:35:53 +00:00
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
|
2014-12-09 16:35:55 +00:00
|
|
|
regs := getRegisters(p, t)
|
2014-05-30 15:12:18 +00:00
|
|
|
rip := regs.PC()
|
2014-05-20 18:23:36 +00:00
|
|
|
|
2014-11-27 02:35:53 +00:00
|
|
|
err = p.Step()
|
2014-09-06 23:53:22 +00:00
|
|
|
assertNoError(err, t, "Step()")
|
2014-05-20 18:23:36 +00:00
|
|
|
|
2014-12-09 16:35:55 +00:00
|
|
|
regs = getRegisters(p, t)
|
2014-05-30 15:12:18 +00:00
|
|
|
if rip >= regs.PC() {
|
|
|
|
t.Errorf("Expected %#v to be greater than %#v", regs.PC(), rip)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2014-05-20 18:23:36 +00:00
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func TestBreakpoint(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-04-03 16:10:35 +00:00
|
|
|
helloworldfunc := p.goSymTable.LookupFunc("main.helloworld")
|
2015-01-14 02:37:10 +00:00
|
|
|
helloworldaddr := helloworldfunc.Entry
|
2014-05-30 15:12:18 +00:00
|
|
|
|
2015-06-20 23:01:06 +00:00
|
|
|
bp, err := p.SetBreakpoint(helloworldaddr)
|
|
|
|
assertNoError(err, t, "SetBreakpoint()")
|
2015-01-14 02:37:10 +00:00
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
2014-05-30 15:12:18 +00:00
|
|
|
|
2015-04-23 15:40:33 +00:00
|
|
|
pc, err := p.PC()
|
2014-10-18 01:34:58 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-05-30 15:12:18 +00:00
|
|
|
|
2015-09-26 00:04:39 +00:00
|
|
|
if bp.TotalHitCount != 1 {
|
|
|
|
t.Fatalf("Breakpoint should be hit once, got %d\n", bp.TotalHitCount)
|
|
|
|
}
|
|
|
|
|
2015-02-28 03:35:26 +00:00
|
|
|
if pc-1 != bp.Addr && pc != bp.Addr {
|
2015-04-03 16:10:35 +00:00
|
|
|
f, l, _ := p.goSymTable.PCToLine(pc)
|
2015-01-14 02:37:10 +00:00
|
|
|
t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr)
|
2014-05-30 15:12:18 +00:00
|
|
|
}
|
|
|
|
})
|
2014-05-24 16:22:06 +00:00
|
|
|
}
|
2014-05-24 16:36:18 +00:00
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func TestBreakpointInSeperateGoRoutine(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testthreads", t, func(p *Process, fixture protest.Fixture) {
|
2015-04-03 16:10:35 +00:00
|
|
|
fn := p.goSymTable.LookupFunc("main.anotherthread")
|
2014-10-18 01:34:58 +00:00
|
|
|
if fn == nil {
|
|
|
|
t.Fatal("No fn exists")
|
|
|
|
}
|
|
|
|
|
2015-06-20 23:01:06 +00:00
|
|
|
_, err := p.SetBreakpoint(fn.Entry)
|
2014-10-18 01:34:58 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = p.Continue()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-04-23 15:40:33 +00:00
|
|
|
pc, err := p.PC()
|
2014-10-18 01:34:58 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-04-03 16:10:35 +00:00
|
|
|
f, l, _ := p.goSymTable.PCToLine(pc)
|
2014-10-18 01:34:58 +00:00
|
|
|
if f != "testthreads.go" && l != 8 {
|
|
|
|
t.Fatal("Program did not hit breakpoint")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func TestBreakpointWithNonExistantFunction(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-06-20 23:01:06 +00:00
|
|
|
_, err := p.SetBreakpoint(0)
|
2014-05-30 15:12:18 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Fatal("Should not be able to break at non existant function")
|
|
|
|
}
|
|
|
|
})
|
2014-05-24 16:36:18 +00:00
|
|
|
}
|
2014-05-27 18:33:49 +00:00
|
|
|
|
2015-06-20 23:01:06 +00:00
|
|
|
func TestClearBreakpointBreakpoint(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-04-03 16:10:35 +00:00
|
|
|
fn := p.goSymTable.LookupFunc("main.sleepytime")
|
2015-06-20 23:01:06 +00:00
|
|
|
bp, err := p.SetBreakpoint(fn.Entry)
|
|
|
|
assertNoError(err, t, "SetBreakpoint()")
|
2014-05-30 15:12:18 +00:00
|
|
|
|
2015-06-20 23:01:06 +00:00
|
|
|
bp, err = p.ClearBreakpoint(fn.Entry)
|
|
|
|
assertNoError(err, t, "ClearBreakpoint()")
|
2014-05-30 15:12:18 +00:00
|
|
|
|
2015-01-14 02:37:10 +00:00
|
|
|
data, err := dataAtAddr(p.CurrentThread, bp.Addr)
|
2014-05-30 15:12:18 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-01-10 23:33:06 +00:00
|
|
|
int3 := []byte{0xcc}
|
2014-05-30 15:12:18 +00:00
|
|
|
if bytes.Equal(data, int3) {
|
|
|
|
t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3)
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
if len(p.Breakpoints) != 0 {
|
2014-05-30 15:12:18 +00:00
|
|
|
t.Fatal("Breakpoint not removed internally")
|
|
|
|
}
|
|
|
|
})
|
2014-05-27 18:33:49 +00:00
|
|
|
}
|
2014-06-29 16:52:21 +00:00
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
type nextTest struct {
|
|
|
|
begin, end int
|
|
|
|
}
|
2014-06-29 16:52:21 +00:00
|
|
|
|
2015-05-09 17:44:38 +00:00
|
|
|
func testnext(program string, testcases []nextTest, initialLocation string, t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess(program, t, func(p *Process, fixture protest.Fixture) {
|
2015-08-07 16:50:14 +00:00
|
|
|
bp, err := setFunctionBreakpoint(p, initialLocation)
|
2015-06-20 23:01:06 +00:00
|
|
|
assertNoError(err, t, "SetBreakpoint()")
|
2014-09-06 23:56:25 +00:00
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
2015-06-20 23:01:06 +00:00
|
|
|
p.ClearBreakpoint(bp.Addr)
|
2015-04-19 22:11:33 +00:00
|
|
|
p.CurrentThread.SetPC(bp.Addr)
|
2014-06-29 16:52:21 +00:00
|
|
|
|
2014-12-29 02:48:58 +00:00
|
|
|
f, ln := currentLineNumber(p, t)
|
2014-06-29 16:52:21 +00:00
|
|
|
for _, tc := range testcases {
|
|
|
|
if ln != tc.begin {
|
2015-03-28 01:12:07 +00:00
|
|
|
t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln)
|
2014-06-29 16:52:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
assertNoError(p.Next(), t, "Next() returned an error")
|
|
|
|
|
2014-10-07 17:25:33 +00:00
|
|
|
f, ln = currentLineNumber(p, t)
|
2014-06-29 16:52:21 +00:00
|
|
|
if ln != tc.end {
|
2015-03-28 01:12:07 +00:00
|
|
|
t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, filepath.Base(f), ln)
|
2014-06-29 16:52:21 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-17 03:37:48 +00:00
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
if len(p.Breakpoints) != 0 {
|
|
|
|
t.Fatal("Not all breakpoints were cleaned up", len(p.Breakpoints))
|
2015-01-10 23:33:06 +00:00
|
|
|
}
|
2014-06-29 16:52:21 +00:00
|
|
|
})
|
|
|
|
}
|
2014-12-09 16:51:17 +00:00
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
func TestNextGeneral(t *testing.T) {
|
|
|
|
testcases := []nextTest{
|
|
|
|
{19, 20},
|
|
|
|
{20, 23},
|
|
|
|
{23, 24},
|
|
|
|
{24, 26},
|
|
|
|
{26, 31},
|
|
|
|
{31, 23},
|
|
|
|
{23, 24},
|
|
|
|
{24, 26},
|
|
|
|
{26, 31},
|
|
|
|
{31, 23},
|
|
|
|
{23, 24},
|
|
|
|
{24, 26},
|
|
|
|
{26, 27},
|
|
|
|
{27, 34},
|
|
|
|
}
|
2015-05-09 17:44:38 +00:00
|
|
|
testnext("testnextprog", testcases, "main.testnext", t)
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
|
|
|
|
2015-08-20 14:28:11 +00:00
|
|
|
func TestNextConcurrent(t *testing.T) {
|
|
|
|
testcases := []nextTest{
|
|
|
|
{9, 10},
|
|
|
|
{10, 11},
|
|
|
|
}
|
|
|
|
withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
_, err := setFunctionBreakpoint(p, "main.sayhi")
|
|
|
|
assertNoError(err, t, "SetBreakpoint")
|
|
|
|
assertNoError(p.Continue(), t, "Continue")
|
|
|
|
f, ln := currentLineNumber(p, t)
|
2015-08-28 20:06:29 +00:00
|
|
|
initV, err := evalVariable(p, "n")
|
2015-10-21 07:06:36 +00:00
|
|
|
initVval, _ := constant.Int64Val(initV.Value)
|
2015-08-20 14:28:11 +00:00
|
|
|
assertNoError(err, t, "EvalVariable")
|
|
|
|
for _, tc := range testcases {
|
2015-08-28 20:06:29 +00:00
|
|
|
g, err := p.CurrentThread.GetG()
|
|
|
|
assertNoError(err, t, "GetG()")
|
|
|
|
if p.SelectedGoroutine.Id != g.Id {
|
|
|
|
t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.Id, p.SelectedGoroutine.Id)
|
|
|
|
}
|
2015-08-20 14:28:11 +00:00
|
|
|
if ln != tc.begin {
|
|
|
|
t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln)
|
|
|
|
}
|
|
|
|
assertNoError(p.Next(), t, "Next() returned an error")
|
|
|
|
f, ln = currentLineNumber(p, t)
|
|
|
|
if ln != tc.end {
|
|
|
|
t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, filepath.Base(f), ln)
|
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
v, err := evalVariable(p, "n")
|
2015-08-20 14:28:11 +00:00
|
|
|
assertNoError(err, t, "EvalVariable")
|
2015-10-21 07:06:36 +00:00
|
|
|
vval, _ := constant.Int64Val(v.Value)
|
|
|
|
if vval != initVval {
|
2015-08-20 14:28:11 +00:00
|
|
|
t.Fatal("Did not end up on same goroutine")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
func TestNextFunctionReturn(t *testing.T) {
|
|
|
|
testcases := []nextTest{
|
2015-10-22 17:07:24 +00:00
|
|
|
{14, 15},
|
|
|
|
{15, 35},
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
2015-05-09 17:44:38 +00:00
|
|
|
testnext("testnextprog", testcases, "main.helloworld", t)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNextFunctionReturnDefer(t *testing.T) {
|
|
|
|
testcases := []nextTest{
|
2015-10-22 17:07:24 +00:00
|
|
|
{8, 9},
|
|
|
|
{9, 10},
|
|
|
|
{10, 7},
|
|
|
|
{7, 8},
|
2015-05-09 17:44:38 +00:00
|
|
|
}
|
|
|
|
testnext("testnextdefer", testcases, "main.main", t)
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
|
|
|
|
2015-07-16 00:57:54 +00:00
|
|
|
func TestNextNetHTTP(t *testing.T) {
|
|
|
|
testcases := []nextTest{
|
|
|
|
{11, 12},
|
|
|
|
{12, 13},
|
|
|
|
}
|
|
|
|
withTestProcess("testnextnethttp", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
go func() {
|
|
|
|
for !p.Running() {
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
}
|
|
|
|
// Wait for program to start listening.
|
|
|
|
for {
|
2015-08-08 19:30:23 +00:00
|
|
|
conn, err := net.Dial("tcp", ":9191")
|
2015-07-16 00:57:54 +00:00
|
|
|
if err == nil {
|
|
|
|
conn.Close()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
}
|
2015-08-08 19:30:23 +00:00
|
|
|
http.Get("http://localhost:9191")
|
2015-07-16 00:57:54 +00:00
|
|
|
}()
|
|
|
|
if err := p.Continue(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
f, ln := currentLineNumber(p, t)
|
|
|
|
for _, tc := range testcases {
|
|
|
|
if ln != tc.begin {
|
|
|
|
t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln)
|
|
|
|
}
|
|
|
|
|
|
|
|
assertNoError(p.Next(), t, "Next() returned an error")
|
|
|
|
|
|
|
|
f, ln = currentLineNumber(p, t)
|
|
|
|
if ln != tc.end {
|
|
|
|
t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, filepath.Base(f), ln)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-04-25 19:13:35 +00:00
|
|
|
func TestRuntimeBreakpoint(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testruntimebreakpoint", t, func(p *Process, fixture protest.Fixture) {
|
2015-04-25 19:13:35 +00:00
|
|
|
err := p.Continue()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
pc, err := p.PC()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
_, l, _ := p.PCToLine(pc)
|
|
|
|
if l != 10 {
|
|
|
|
t.Fatal("did not respect breakpoint")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-12-09 16:51:17 +00:00
|
|
|
func TestFindReturnAddress(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testnextprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-08-10 13:55:57 +00:00
|
|
|
start, _, err := p.goSymTable.LineToPC(fixture.Source, 24)
|
2014-12-09 16:51:17 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-06-20 23:01:06 +00:00
|
|
|
_, err = p.SetBreakpoint(start)
|
2014-12-09 16:51:17 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err = p.Continue()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-08-10 13:55:57 +00:00
|
|
|
addr, err := p.CurrentThread.ReturnAddress()
|
2014-12-09 16:51:17 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-08-10 13:55:57 +00:00
|
|
|
_, l, _ := p.goSymTable.PCToLine(addr)
|
|
|
|
if l != 40 {
|
|
|
|
t.Fatalf("return address not found correctly, expected line 40")
|
2014-12-09 16:51:17 +00:00
|
|
|
}
|
2015-08-10 13:55:57 +00:00
|
|
|
})
|
|
|
|
}
|
2014-12-09 16:51:17 +00:00
|
|
|
|
2015-08-10 13:55:57 +00:00
|
|
|
func TestFindReturnAddressTopOfStackFn(t *testing.T) {
|
|
|
|
withTestProcess("testreturnaddress", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
fnName := "runtime.rt0_go"
|
|
|
|
fn := p.goSymTable.LookupFunc(fnName)
|
|
|
|
if fn == nil {
|
|
|
|
t.Fatalf("could not find function %s", fnName)
|
|
|
|
}
|
|
|
|
if _, err := p.SetBreakpoint(fn.Entry); err != nil {
|
2014-12-09 16:51:17 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-08-10 13:55:57 +00:00
|
|
|
if err := p.Continue(); err != nil {
|
2015-08-02 02:43:03 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-08-10 13:55:57 +00:00
|
|
|
if _, err := p.CurrentThread.ReturnAddress(); err == nil {
|
|
|
|
t.Fatal("expected error to be returned")
|
2014-12-09 16:51:17 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-03-26 18:15:35 +00:00
|
|
|
|
|
|
|
func TestSwitchThread(t *testing.T) {
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("testnextprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-03-26 18:15:35 +00:00
|
|
|
// With invalid thread id
|
|
|
|
err := p.SwitchThread(-1)
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("Expected error for invalid thread id")
|
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
pc, err := p.FindFunctionLocation("main.main", true, 0)
|
2015-03-26 18:15:35 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-06-20 23:01:06 +00:00
|
|
|
_, err = p.SetBreakpoint(pc)
|
2015-03-26 18:15:35 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err = p.Continue()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
var nt int
|
|
|
|
ct := p.CurrentThread.Id
|
2015-04-26 11:31:28 +00:00
|
|
|
for tid := range p.Threads {
|
2015-03-26 18:15:35 +00:00
|
|
|
if tid != ct {
|
|
|
|
nt = tid
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nt == 0 {
|
|
|
|
t.Fatal("could not find thread to switch to")
|
|
|
|
}
|
|
|
|
// With valid thread id
|
|
|
|
err = p.SwitchThread(nt)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if p.CurrentThread.Id != nt {
|
|
|
|
t.Fatal("Did not switch threads")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-06-17 17:11:57 +00:00
|
|
|
|
2015-08-28 08:06:22 +00:00
|
|
|
func TestCGONext(t *testing.T) {
|
|
|
|
// Test if one can do 'next' in a cgo binary
|
|
|
|
// On OSX with Go < 1.5 CGO is not supported due to: https://github.com/golang/go/issues/8973
|
|
|
|
if runtime.GOOS == "darwin" && strings.Contains(runtime.Version(), "1.4") {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
pc, err := p.FindFunctionLocation("main.main", true, 0)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
_, err = p.SetBreakpoint(pc)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err = p.Continue()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err = p.Next()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-17 17:11:57 +00:00
|
|
|
type loc struct {
|
|
|
|
line int
|
|
|
|
fn string
|
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
func (l1 *loc) match(l2 Stackframe) bool {
|
2015-06-17 17:11:57 +00:00
|
|
|
if l1.line >= 0 {
|
2015-09-17 08:42:34 +00:00
|
|
|
if l1.line != l2.Call.Line {
|
2015-06-17 17:11:57 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2015-09-17 08:42:34 +00:00
|
|
|
return l1.fn == l2.Call.Fn.Name
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestStacktrace(t *testing.T) {
|
|
|
|
stacks := [][]loc{
|
2015-10-22 17:07:24 +00:00
|
|
|
{{4, "main.stacktraceme"}, {8, "main.func1"}, {16, "main.main"}},
|
|
|
|
{{4, "main.stacktraceme"}, {8, "main.func1"}, {12, "main.func2"}, {17, "main.main"}},
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("stacktraceprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-08-07 16:50:14 +00:00
|
|
|
bp, err := setFunctionBreakpoint(p, "main.stacktraceme")
|
2015-06-17 17:11:57 +00:00
|
|
|
assertNoError(err, t, "BreakByLocation()")
|
|
|
|
|
|
|
|
for i := range stacks {
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
2015-07-01 03:16:52 +00:00
|
|
|
locations, err := p.CurrentThread.Stacktrace(40)
|
2015-06-17 17:11:57 +00:00
|
|
|
assertNoError(err, t, "Stacktrace()")
|
|
|
|
|
|
|
|
if len(locations) != len(stacks[i])+2 {
|
|
|
|
t.Fatalf("Wrong stack trace size %d %d\n", len(locations), len(stacks[i])+2)
|
|
|
|
}
|
|
|
|
|
2015-09-17 08:42:34 +00:00
|
|
|
t.Logf("Stacktrace %d:\n", i)
|
|
|
|
for i := range locations {
|
|
|
|
t.Logf("\t%s:%d\n", locations[i].Call.File, locations[i].Call.Line)
|
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
|
2015-06-17 17:11:57 +00:00
|
|
|
for j := range stacks[i] {
|
|
|
|
if !stacks[i][j].match(locations[j]) {
|
|
|
|
t.Fatalf("Wrong stack trace pos %d\n", j)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-20 23:01:06 +00:00
|
|
|
p.ClearBreakpoint(bp.Addr)
|
2015-06-17 17:11:57 +00:00
|
|
|
p.Continue()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-09-17 08:42:34 +00:00
|
|
|
func TestStacktrace2(t *testing.T) {
|
|
|
|
withTestProcess("retstack", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
|
|
|
|
locations, err := p.CurrentThread.Stacktrace(40)
|
|
|
|
assertNoError(err, t, "Stacktrace()")
|
2015-11-06 18:49:29 +00:00
|
|
|
if !stackMatch([]loc{{-1, "main.f"}, {16, "main.main"}}, locations, false) {
|
2015-09-17 08:42:34 +00:00
|
|
|
for i := range locations {
|
|
|
|
t.Logf("\t%s:%d [%s]\n", locations[i].Call.File, locations[i].Call.Line, locations[i].Call.Fn.Name)
|
|
|
|
}
|
|
|
|
t.Fatalf("Stack error at main.f()\n", locations)
|
|
|
|
}
|
|
|
|
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
locations, err = p.CurrentThread.Stacktrace(40)
|
|
|
|
assertNoError(err, t, "Stacktrace()")
|
2015-11-06 18:49:29 +00:00
|
|
|
if !stackMatch([]loc{{-1, "main.g"}, {17, "main.main"}}, locations, false) {
|
2015-09-17 08:42:34 +00:00
|
|
|
for i := range locations {
|
|
|
|
t.Logf("\t%s:%d [%s]\n", locations[i].Call.File, locations[i].Call.Line, locations[i].Call.Fn.Name)
|
|
|
|
}
|
|
|
|
t.Fatalf("Stack error at main.g()\n", locations)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-11-06 18:49:29 +00:00
|
|
|
func stackMatch(stack []loc, locations []Stackframe, skipRuntime bool) bool {
|
2015-06-17 17:11:57 +00:00
|
|
|
if len(stack) > len(locations) {
|
|
|
|
return false
|
|
|
|
}
|
2015-11-06 18:49:29 +00:00
|
|
|
i := 0
|
|
|
|
for j := range locations {
|
|
|
|
if i >= len(stack) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if skipRuntime {
|
|
|
|
if locations[j].Call.Fn == nil || strings.HasPrefix(locations[j].Call.Fn.Name, "runtime.") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !stack[i].match(locations[j]) {
|
2015-06-17 17:11:57 +00:00
|
|
|
return false
|
|
|
|
}
|
2015-11-06 18:49:29 +00:00
|
|
|
i++
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
2015-11-06 18:49:29 +00:00
|
|
|
return i >= len(stack)
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestStacktraceGoroutine(t *testing.T) {
|
2015-11-06 18:49:29 +00:00
|
|
|
mainStack := []loc{{13, "main.stacktraceme"}, {26, "main.main"}}
|
|
|
|
agoroutineStackA := []loc{{9, "main.agoroutine"}}
|
|
|
|
agoroutineStackB := []loc{{10, "main.agoroutine"}}
|
2015-06-17 17:11:57 +00:00
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
withTestProcess("goroutinestackprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-08-07 16:50:14 +00:00
|
|
|
bp, err := setFunctionBreakpoint(p, "main.stacktraceme")
|
2015-06-17 17:11:57 +00:00
|
|
|
assertNoError(err, t, "BreakByLocation()")
|
|
|
|
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
|
|
|
|
gs, err := p.GoroutinesInfo()
|
|
|
|
assertNoError(err, t, "GoroutinesInfo")
|
|
|
|
|
|
|
|
agoroutineCount := 0
|
|
|
|
mainCount := 0
|
|
|
|
|
2015-07-01 03:16:52 +00:00
|
|
|
for i, g := range gs {
|
|
|
|
locations, err := p.GoroutineStacktrace(g, 40)
|
2015-06-17 17:11:57 +00:00
|
|
|
assertNoError(err, t, "GoroutineStacktrace()")
|
|
|
|
|
2015-11-06 18:49:29 +00:00
|
|
|
if stackMatch(mainStack, locations, false) {
|
2015-06-17 17:11:57 +00:00
|
|
|
mainCount++
|
|
|
|
}
|
|
|
|
|
2015-11-06 18:49:29 +00:00
|
|
|
if stackMatch(agoroutineStackA, locations, true) {
|
|
|
|
agoroutineCount++
|
|
|
|
} else if stackMatch(agoroutineStackB, locations, true) {
|
2015-06-17 17:11:57 +00:00
|
|
|
agoroutineCount++
|
|
|
|
} else {
|
2015-07-01 03:16:52 +00:00
|
|
|
t.Logf("Non-goroutine stack: %d (%d)", i, len(locations))
|
2015-06-17 17:11:57 +00:00
|
|
|
for i := range locations {
|
|
|
|
name := ""
|
2015-09-17 08:42:34 +00:00
|
|
|
if locations[i].Call.Fn != nil {
|
|
|
|
name = locations[i].Call.Fn.Name
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
2015-09-17 08:42:34 +00:00
|
|
|
t.Logf("\t%s:%d %s\n", locations[i].Call.File, locations[i].Call.Line, name)
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mainCount != 1 {
|
2015-11-06 18:49:29 +00:00
|
|
|
t.Fatalf("Main goroutine stack not found %d", mainCount)
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if agoroutineCount != 10 {
|
|
|
|
t.Fatalf("Goroutine stacks not found (%d)", agoroutineCount)
|
|
|
|
}
|
|
|
|
|
2015-06-20 23:01:06 +00:00
|
|
|
p.ClearBreakpoint(bp.Addr)
|
2015-06-17 17:11:57 +00:00
|
|
|
p.Continue()
|
|
|
|
})
|
|
|
|
}
|
2015-07-11 06:43:47 +00:00
|
|
|
|
|
|
|
func TestKill(t *testing.T) {
|
|
|
|
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
if err := p.Kill(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if p.Exited() != true {
|
|
|
|
t.Fatal("expected process to have exited")
|
|
|
|
}
|
|
|
|
if runtime.GOOS == "linux" {
|
|
|
|
_, err := os.Open(fmt.Sprintf("/proc/%d/", p.Pid))
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("process has not exited", p.Pid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-07-23 17:08:28 +00:00
|
|
|
|
|
|
|
func testGSupportFunc(name string, t *testing.T, p *Process, fixture protest.Fixture) {
|
2015-08-07 16:50:14 +00:00
|
|
|
bp, err := setFunctionBreakpoint(p, "main.main")
|
2015-07-23 17:08:28 +00:00
|
|
|
assertNoError(err, t, name+": BreakByLocation()")
|
|
|
|
|
|
|
|
assertNoError(p.Continue(), t, name+": Continue()")
|
|
|
|
|
|
|
|
g, err := p.CurrentThread.GetG()
|
|
|
|
assertNoError(err, t, name+": GetG()")
|
|
|
|
|
|
|
|
if g == nil {
|
|
|
|
t.Fatal(name + ": g was nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Logf(name+": g is: %v", g)
|
|
|
|
|
|
|
|
p.ClearBreakpoint(bp.Addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetG(t *testing.T) {
|
|
|
|
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
testGSupportFunc("nocgo", t, p, fixture)
|
|
|
|
})
|
|
|
|
|
2015-07-28 05:33:07 +00:00
|
|
|
// On OSX with Go < 1.5 CGO is not supported due to: https://github.com/golang/go/issues/8973
|
|
|
|
if runtime.GOOS == "darwin" && strings.Contains(runtime.Version(), "1.4") {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-23 17:08:28 +00:00
|
|
|
withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
testGSupportFunc("cgo", t, p, fixture)
|
|
|
|
})
|
|
|
|
}
|
2015-07-09 16:41:03 +00:00
|
|
|
|
|
|
|
func TestContinueMulti(t *testing.T) {
|
|
|
|
withTestProcess("integrationprog", t, func(p *Process, fixture protest.Fixture) {
|
2015-08-07 16:50:14 +00:00
|
|
|
bp1, err := setFunctionBreakpoint(p, "main.main")
|
2015-07-09 16:41:03 +00:00
|
|
|
assertNoError(err, t, "BreakByLocation()")
|
|
|
|
|
2015-08-07 16:50:14 +00:00
|
|
|
bp2, err := setFunctionBreakpoint(p, "main.sayhi")
|
2015-07-09 16:41:03 +00:00
|
|
|
assertNoError(err, t, "BreakByLocation()")
|
|
|
|
|
|
|
|
mainCount := 0
|
|
|
|
sayhiCount := 0
|
|
|
|
for {
|
|
|
|
err := p.Continue()
|
2015-09-26 20:56:24 +00:00
|
|
|
if p.Exited() {
|
2015-07-09 16:41:03 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
assertNoError(err, t, "Continue()")
|
|
|
|
|
|
|
|
if p.CurrentBreakpoint().ID == bp1.ID {
|
|
|
|
mainCount++
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.CurrentBreakpoint().ID == bp2.ID {
|
|
|
|
sayhiCount++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mainCount != 1 {
|
|
|
|
t.Fatalf("Main breakpoint hit wrong number of times: %d\n", mainCount)
|
|
|
|
}
|
|
|
|
|
|
|
|
if sayhiCount != 3 {
|
|
|
|
t.Fatalf("Sayhi breakpoint hit wrong number of times: %d\n", sayhiCount)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-07-28 19:54:40 +00:00
|
|
|
|
2015-07-28 20:42:56 +00:00
|
|
|
func versionAfterOrEqual(t *testing.T, verStr string, ver GoVersion) {
|
2015-07-28 19:54:40 +00:00
|
|
|
pver, ok := parseVersionString(verStr)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("Could not parse version string <%s>", verStr)
|
|
|
|
}
|
2015-07-28 20:42:56 +00:00
|
|
|
if !pver.AfterOrEqual(ver) {
|
2015-07-28 19:54:40 +00:00
|
|
|
t.Fatalf("Version <%s> parsed as %v not after %v", verStr, pver, ver)
|
|
|
|
}
|
|
|
|
t.Logf("version string <%s> → %v", verStr, ver)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseVersionString(t *testing.T) {
|
2015-08-12 11:37:36 +00:00
|
|
|
versionAfterOrEqual(t, "go1.4", GoVersion{1, 4, 0, 0, 0})
|
2015-08-11 13:37:27 +00:00
|
|
|
versionAfterOrEqual(t, "go1.5.0", GoVersion{1, 5, 0, 0, 0})
|
|
|
|
versionAfterOrEqual(t, "go1.4.2", GoVersion{1, 4, 2, 0, 0})
|
|
|
|
versionAfterOrEqual(t, "go1.5beta2", GoVersion{1, 5, -1, 2, 0})
|
|
|
|
versionAfterOrEqual(t, "go1.5rc2", GoVersion{1, 5, -1, 0, 2})
|
2015-07-28 19:54:40 +00:00
|
|
|
ver, ok := parseVersionString("devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64")
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("Could not parse devel version string")
|
|
|
|
}
|
|
|
|
if !ver.IsDevel() {
|
|
|
|
t.Fatalf("Devel version string not correctly recognized")
|
|
|
|
}
|
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
|
|
|
|
func TestBreakpointOnFunctionEntry(t *testing.T) {
|
|
|
|
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
addr, err := p.FindFunctionLocation("main.main", false, 0)
|
|
|
|
assertNoError(err, t, "FindFunctionLocation()")
|
|
|
|
_, err = p.SetBreakpoint(addr)
|
|
|
|
assertNoError(err, t, "SetBreakpoint()")
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
_, ln := currentLineNumber(p, t)
|
|
|
|
if ln != 17 {
|
|
|
|
t.Fatalf("Wrong line number: %d (expected: 17)\n", ln)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-08-12 00:12:37 +00:00
|
|
|
|
|
|
|
func TestProcessReceivesSIGCHLD(t *testing.T) {
|
|
|
|
withTestProcess("sigchldprog", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
err := p.Continue()
|
|
|
|
_, ok := err.(ProcessExitedError)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("Continue() returned unexpected error type %s", err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-10-04 18:16:39 +00:00
|
|
|
|
|
|
|
func TestIssue239(t *testing.T) {
|
|
|
|
withTestProcess("is sue239", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
pos, _, err := p.goSymTable.LineToPC(fixture.Source, 17)
|
|
|
|
assertNoError(err, t, "LineToPC()")
|
|
|
|
_, err = p.SetBreakpoint(pos)
|
|
|
|
assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%d)", pos))
|
|
|
|
assertNoError(p.Continue(), t, fmt.Sprintf("Continue()"))
|
|
|
|
})
|
|
|
|
}
|
2015-10-18 17:37:13 +00:00
|
|
|
|
|
|
|
func evalVariable(p *Process, symbol string) (*Variable, error) {
|
|
|
|
scope, err := p.CurrentThread.Scope()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return scope.EvalVariable(symbol)
|
|
|
|
}
|
|
|
|
|
|
|
|
func setVariable(p *Process, symbol, value string) error {
|
|
|
|
scope, err := p.CurrentThread.Scope()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return scope.SetVariable(symbol, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestVariableEvaluation(t *testing.T) {
|
|
|
|
testcases := []struct {
|
|
|
|
name string
|
|
|
|
st reflect.Kind
|
|
|
|
value interface{}
|
|
|
|
length, cap int64
|
|
|
|
childrenlen int
|
|
|
|
}{
|
|
|
|
{"a1", reflect.String, "foofoofoofoofoofoo", 18, 0, 0},
|
|
|
|
{"a11", reflect.Array, nil, 3, -1, 3},
|
|
|
|
{"a12", reflect.Slice, nil, 2, 2, 2},
|
|
|
|
{"a13", reflect.Slice, nil, 3, 3, 3},
|
|
|
|
{"a2", reflect.Int, int64(6), 0, 0, 0},
|
|
|
|
{"a3", reflect.Float64, float64(7.23), 0, 0, 0},
|
|
|
|
{"a4", reflect.Array, nil, 2, -1, 2},
|
|
|
|
{"a5", reflect.Slice, nil, 5, 5, 5},
|
|
|
|
{"a6", reflect.Struct, nil, 2, 0, 2},
|
|
|
|
{"a7", reflect.Ptr, nil, 1, 0, 1},
|
|
|
|
{"a8", reflect.Struct, nil, 2, 0, 2},
|
|
|
|
{"a9", reflect.Ptr, nil, 1, 0, 1},
|
|
|
|
{"baz", reflect.String, "bazburzum", 9, 0, 0},
|
|
|
|
{"neg", reflect.Int, int64(-1), 0, 0, 0},
|
|
|
|
{"f32", reflect.Float32, float64(float32(1.2)), 0, 0, 0},
|
2015-10-30 11:39:32 +00:00
|
|
|
{"c64", reflect.Complex64, complex128(complex64(1 + 2i)), 0, 0, 0},
|
|
|
|
{"c128", reflect.Complex128, complex128(2 + 3i), 0, 0, 0},
|
2015-10-18 17:37:13 +00:00
|
|
|
{"a6.Baz", reflect.Int, int64(8), 0, 0, 0},
|
|
|
|
{"a7.Baz", reflect.Int, int64(5), 0, 0, 0},
|
|
|
|
{"a8.Baz", reflect.String, "feh", 3, 0, 0},
|
|
|
|
{"a8", reflect.Struct, nil, 2, 0, 2},
|
|
|
|
{"i32", reflect.Array, nil, 2, -1, 2},
|
|
|
|
{"b1", reflect.Bool, true, 0, 0, 0},
|
|
|
|
{"b2", reflect.Bool, false, 0, 0, 0},
|
|
|
|
{"f", reflect.Func, "main.barfoo", 0, 0, 0},
|
|
|
|
{"ba", reflect.Slice, nil, 200, 200, 64},
|
|
|
|
}
|
|
|
|
|
|
|
|
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
assertNoError(p.Continue(), t, "Continue() returned an error")
|
|
|
|
|
|
|
|
for _, tc := range testcases {
|
|
|
|
v, err := evalVariable(p, tc.name)
|
|
|
|
assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", tc.name))
|
|
|
|
|
|
|
|
if v.Kind != tc.st {
|
|
|
|
t.Fatalf("%s simple type: expected: %s got: %s", tc.name, tc.st, v.Kind.String())
|
|
|
|
}
|
|
|
|
if v.Value == nil && tc.value != nil {
|
|
|
|
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
|
|
|
} else {
|
2015-10-21 07:06:36 +00:00
|
|
|
switch v.Kind {
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
x, _ := constant.Int64Val(v.Value)
|
2015-10-18 17:37:13 +00:00
|
|
|
if y, ok := tc.value.(int64); !ok || x != y {
|
|
|
|
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
|
|
|
}
|
2015-10-21 07:06:36 +00:00
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
x, _ := constant.Float64Val(v.Value)
|
2015-10-18 17:37:13 +00:00
|
|
|
if y, ok := tc.value.(float64); !ok || x != y {
|
|
|
|
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
|
|
|
}
|
2015-10-30 11:39:32 +00:00
|
|
|
case reflect.Complex64, reflect.Complex128:
|
|
|
|
xr, _ := constant.Float64Val(constant.Real(v.Value))
|
|
|
|
xi, _ := constant.Float64Val(constant.Imag(v.Value))
|
|
|
|
if y, ok := tc.value.(complex128); !ok || complex(xr, xi) != y {
|
|
|
|
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
|
|
|
}
|
2015-10-21 07:06:36 +00:00
|
|
|
case reflect.String:
|
|
|
|
if y, ok := tc.value.(string); !ok || constant.StringVal(v.Value) != y {
|
2015-10-18 17:37:13 +00:00
|
|
|
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if v.Len != tc.length {
|
|
|
|
t.Fatalf("%s len: expected: %d got: %d", tc.name, tc.length, v.Len)
|
|
|
|
}
|
|
|
|
if v.Cap != tc.cap {
|
|
|
|
t.Fatalf("%s cap: expected: %d got: %d", tc.name, tc.cap, v.Cap)
|
|
|
|
}
|
|
|
|
if len(v.Children) != tc.childrenlen {
|
|
|
|
t.Fatalf("%s children len: expected %d got: %d", tc.name, tc.childrenlen, len(v.Children))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFrameEvaluation(t *testing.T) {
|
|
|
|
withTestProcess("goroutinestackprog", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
_, err := setFunctionBreakpoint(p, "main.stacktraceme")
|
|
|
|
assertNoError(err, t, "setFunctionBreakpoint")
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
|
|
|
|
/**** Testing evaluation on goroutines ****/
|
|
|
|
gs, err := p.GoroutinesInfo()
|
|
|
|
assertNoError(err, t, "GoroutinesInfo")
|
|
|
|
found := make([]bool, 10)
|
|
|
|
for _, g := range gs {
|
|
|
|
frame := -1
|
|
|
|
frames, err := p.GoroutineStacktrace(g, 10)
|
|
|
|
assertNoError(err, t, "GoroutineStacktrace()")
|
|
|
|
for i := range frames {
|
|
|
|
if frames[i].Call.Fn != nil && frames[i].Call.Fn.Name == "main.agoroutine" {
|
|
|
|
frame = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if frame < 0 {
|
|
|
|
t.Logf("Goroutine %d: could not find correct frame", g.Id)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
scope, err := p.ConvertEvalScope(g.Id, frame)
|
|
|
|
assertNoError(err, t, "ConvertEvalScope()")
|
|
|
|
t.Logf("scope = %v", scope)
|
|
|
|
v, err := scope.EvalVariable("i")
|
|
|
|
t.Logf("v = %v", v)
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("Goroutine %d: %v\n", g.Id, err)
|
|
|
|
continue
|
|
|
|
}
|
2015-10-21 07:06:36 +00:00
|
|
|
vval, _ := constant.Int64Val(v.Value)
|
|
|
|
found[vval] = true
|
2015-10-18 17:37:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range found {
|
|
|
|
if !found[i] {
|
|
|
|
t.Fatalf("Goroutine %d not found\n", i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**** Testing evaluation on frames ****/
|
|
|
|
assertNoError(p.Continue(), t, "Continue() 2")
|
|
|
|
g, err := p.CurrentThread.GetG()
|
|
|
|
assertNoError(err, t, "GetG()")
|
|
|
|
|
|
|
|
for i := 0; i <= 3; i++ {
|
|
|
|
scope, err := p.ConvertEvalScope(g.Id, i+1)
|
|
|
|
assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1))
|
|
|
|
v, err := scope.EvalVariable("n")
|
|
|
|
assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1))
|
2015-10-21 07:06:36 +00:00
|
|
|
n, _ := constant.Int64Val(v.Value)
|
2015-10-18 17:37:13 +00:00
|
|
|
t.Logf("frame %d n %d\n", i+1, n)
|
|
|
|
if n != int64(3-i) {
|
|
|
|
t.Fatalf("On frame %d value of n is %d (not %d)", i+1, n, 3-i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPointerSetting(t *testing.T) {
|
|
|
|
withTestProcess("testvariables3", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
assertNoError(p.Continue(), t, "Continue() returned an error")
|
|
|
|
|
|
|
|
pval := func(n int64) {
|
|
|
|
variable, err := evalVariable(p, "p1")
|
|
|
|
assertNoError(err, t, "EvalVariable()")
|
2015-10-21 07:06:36 +00:00
|
|
|
c0val, _ := constant.Int64Val(variable.Children[0].Value)
|
|
|
|
if c0val != n {
|
|
|
|
t.Fatalf("Wrong value of p1, *%d expected *%d", c0val, n)
|
2015-10-18 17:37:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pval(1)
|
|
|
|
|
|
|
|
// change p1 to point to i2
|
|
|
|
scope, err := p.CurrentThread.Scope()
|
|
|
|
assertNoError(err, t, "Scope()")
|
2015-10-30 11:39:32 +00:00
|
|
|
i2addr, err := scope.EvalExpression("i2")
|
|
|
|
assertNoError(err, t, "EvalExpression()")
|
|
|
|
assertNoError(setVariable(p, "p1", fmt.Sprintf("(*int)(0x%x)", i2addr.Addr)), t, "SetVariable()")
|
2015-10-18 17:37:13 +00:00
|
|
|
pval(2)
|
|
|
|
|
|
|
|
// change the value of i2 check that p1 also changes
|
|
|
|
assertNoError(setVariable(p, "i2", "5"), t, "SetVariable()")
|
|
|
|
pval(5)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestVariableFunctionScoping(t *testing.T) {
|
|
|
|
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
err := p.Continue()
|
|
|
|
assertNoError(err, t, "Continue() returned an error")
|
|
|
|
|
|
|
|
_, err = evalVariable(p, "a1")
|
|
|
|
assertNoError(err, t, "Unable to find variable a1")
|
|
|
|
|
|
|
|
_, err = evalVariable(p, "a2")
|
|
|
|
assertNoError(err, t, "Unable to find variable a1")
|
|
|
|
|
|
|
|
// Move scopes, a1 exists here by a2 does not
|
|
|
|
err = p.Continue()
|
|
|
|
assertNoError(err, t, "Continue() returned an error")
|
|
|
|
|
|
|
|
_, err = evalVariable(p, "a1")
|
|
|
|
assertNoError(err, t, "Unable to find variable a1")
|
|
|
|
|
|
|
|
_, err = evalVariable(p, "a2")
|
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("Can eval out of scope variable a2")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRecursiveStructure(t *testing.T) {
|
|
|
|
withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
v, err := evalVariable(p, "aas")
|
|
|
|
assertNoError(err, t, "EvalVariable()")
|
|
|
|
t.Logf("v: %v\n", v)
|
|
|
|
})
|
|
|
|
}
|
2015-12-19 13:57:48 +00:00
|
|
|
|
|
|
|
func TestIssue316(t *testing.T) {
|
|
|
|
// A pointer loop that includes one interface should not send dlv into an infinite loop
|
|
|
|
withTestProcess("testvariables3", t, func(p *Process, fixture protest.Fixture) {
|
|
|
|
assertNoError(p.Continue(), t, "Continue()")
|
|
|
|
_, err := evalVariable(p, "iface5")
|
|
|
|
assertNoError(err, t, "EvalVariable()")
|
|
|
|
})
|
|
|
|
}
|