proctl_test.go 6.6 KB
Newer Older
1
package proctl
D
Derek Parker 已提交
2 3

import (
4
	"bytes"
5 6 7
	"encoding/binary"
	"os"
	"os/exec"
8
	"path/filepath"
9
	"runtime"
D
Derek Parker 已提交
10
	"testing"
D
Derek Parker 已提交
11
)
12

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
func withTestProcess(name string, t *testing.T, fn func(p *DebuggedProcess)) {
	runtime.LockOSThread()
	base := filepath.Base(name)
	if err := exec.Command("go", "build", "-gcflags=-N -l", "-o", base, name+".go").Run(); err != nil {
		t.Fatalf("Could not compile %s due to %s", name, err)
	}
	defer os.Remove("./" + base)

	p, err := Launch([]string{"./" + base})
	if err != nil {
		t.Fatal("Launch():", err)
	}

	defer p.Process.Kill()

	fn(p)
}

func getRegisters(p *DebuggedProcess, t *testing.T) Registers {
32 33 34 35 36 37 38 39
	regs, err := p.Registers()
	if err != nil {
		t.Fatal("Registers():", err)
	}

	return regs
}

40 41
func dataAtAddr(pid int, addr uint64) ([]byte, error) {
	data := make([]byte, 1)
42
	_, err := readMemory(pid, uintptr(addr), data)
43 44 45 46 47 48 49
	if err != nil {
		return nil, err
	}

	return data, nil
}

50 51 52 53 54 55
func assertNoError(err error, t *testing.T, s string) {
	if err != nil {
		t.Fatal(s, ":", err)
	}
}

56
func currentPC(p *DebuggedProcess, t *testing.T) uint64 {
57 58 59 60 61 62 63 64
	pc, err := p.CurrentPC()
	if err != nil {
		t.Fatal(err)
	}

	return pc
}

65
func currentLineNumber(p *DebuggedProcess, t *testing.T) (string, int) {
66
	pc := currentPC(p, t)
D
Derek Parker 已提交
67
	f, l, _ := p.GoSymTable.PCToLine(pc)
68

D
Derek Parker 已提交
69
	return f, l
70 71
}

72
func TestStep(t *testing.T) {
73
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
74 75 76 77 78 79 80
		helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
		helloworldaddr := helloworldfunc.Entry

		_, err := p.Break(uintptr(helloworldaddr))
		assertNoError(err, t, "Break()")
		assertNoError(p.Continue(), t, "Continue()")

81
		regs := getRegisters(p, t)
82
		rip := regs.PC()
83

84
		err = p.Step()
D
Derek Parker 已提交
85
		assertNoError(err, t, "Step()")
86

87
		regs = getRegisters(p, t)
88 89 90 91 92
		if rip >= regs.PC() {
			t.Errorf("Expected %#v to be greater than %#v", regs.PC(), rip)
		}
	})
}
93

94
func TestContinue(t *testing.T) {
95
	withTestProcess("../_fixtures/continuetestprog", t, func(p *DebuggedProcess) {
96
		err := p.Continue()
97
		if err != nil {
98
			if _, ok := err.(ProcessExitedError); !ok {
99 100 101
				t.Fatal(err)
			}
		}
102

103 104
		if p.Status().ExitStatus() != 0 {
			t.Fatal("Process did not exit successfully", p.Status().ExitStatus())
105 106
		}
	})
107
}
108 109

func TestBreakPoint(t *testing.T) {
110
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
111
		sleepytimefunc := p.GoSymTable.LookupFunc("main.helloworld")
112 113 114
		sleepyaddr := sleepytimefunc.Entry

		bp, err := p.Break(uintptr(sleepyaddr))
D
Derek Parker 已提交
115
		assertNoError(err, t, "Break()")
116 117 118

		breakpc := bp.Addr + 1
		err = p.Continue()
D
Derek Parker 已提交
119
		assertNoError(err, t, "Continue()")
120

121 122 123 124
		pc, err := p.CurrentPC()
		if err != nil {
			t.Fatal(err)
		}
125 126

		if pc != breakpc {
127 128
			f, l, _ := p.GoSymTable.PCToLine(pc)
			t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, breakpc)
129 130 131
		}

		err = p.Step()
132
		assertNoError(err, t, "Step()")
133

134 135 136 137
		pc, err = p.CurrentPC()
		if err != nil {
			t.Fatal(err)
		}
138 139 140 141 142

		if pc == breakpc {
			t.Fatalf("Step not respected:\nPC:%d\nFN:%d\n", pc, breakpc)
		}
	})
143
}
144

145
func TestBreakPointInSeperateGoRoutine(t *testing.T) {
146
	withTestProcess("../_fixtures/testthreads", t, func(p *DebuggedProcess) {
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
		fn := p.GoSymTable.LookupFunc("main.anotherthread")
		if fn == nil {
			t.Fatal("No fn exists")
		}

		_, err := p.Break(uintptr(fn.Entry))
		if err != nil {
			t.Fatal(err)
		}

		err = p.Continue()
		if err != nil {
			t.Fatal(err)
		}

		pc, err := p.CurrentPC()
		if err != nil {
			t.Fatal(err)
		}

		f, l, _ := p.GoSymTable.PCToLine(pc)
		if f != "testthreads.go" && l != 8 {
			t.Fatal("Program did not hit breakpoint")
		}
	})
}

174
func TestBreakPointWithNonExistantFunction(t *testing.T) {
175
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
176 177 178 179 180
		_, err := p.Break(uintptr(0))
		if err == nil {
			t.Fatal("Should not be able to break at non existant function")
		}
	})
181
}
182 183

func TestClearBreakPoint(t *testing.T) {
184
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
185 186
		fn := p.GoSymTable.LookupFunc("main.sleepytime")
		bp, err := p.Break(uintptr(fn.Entry))
D
Derek Parker 已提交
187
		assertNoError(err, t, "Break()")
188 189 190 191 192 193 194

		int3, err := dataAtAddr(p.Pid, bp.Addr)
		if err != nil {
			t.Fatal(err)
		}

		bp, err = p.Clear(fn.Entry)
D
Derek Parker 已提交
195
		assertNoError(err, t, "Clear()")
196 197 198 199 200 201 202 203 204 205

		data, err := dataAtAddr(p.Pid, bp.Addr)
		if err != nil {
			t.Fatal(err)
		}

		if bytes.Equal(data, int3) {
			t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3)
		}

206
		if len(p.BreakPoints) != 0 {
207 208 209
			t.Fatal("Breakpoint not removed internally")
		}
	})
210
}
211 212 213

func TestNext(t *testing.T) {
	var (
214 215
		err            error
		executablePath = "../_fixtures/testnextprog"
216 217 218 219 220
	)

	testcases := []struct {
		begin, end int
	}{
D
Derek Parker 已提交
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
		{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},
		{34, 35},
D
Derek Parker 已提交
236
		{35, 41},
237
		{41, 40},
D
Derek Parker 已提交
238
		{40, 41},
239 240 241 242 243 244 245
	}

	fp, err := filepath.Abs("../_fixtures/testnextprog.go")
	if err != nil {
		t.Fatal(err)
	}

246
	withTestProcess(executablePath, t, func(p *DebuggedProcess) {
247
		pc, _, _ := p.GoSymTable.LineToPC(fp, testcases[0].begin)
D
Derek Parker 已提交
248
		_, err := p.Break(uintptr(pc))
249 250
		assertNoError(err, t, "Break()")
		assertNoError(p.Continue(), t, "Continue()")
251

D
Derek Parker 已提交
252
		f, ln := currentLineNumber(p, t)
253 254
		for _, tc := range testcases {
			if ln != tc.begin {
D
Derek Parker 已提交
255
				t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, f, ln)
256 257 258 259
			}

			assertNoError(p.Next(), t, "Next() returned an error")

D
Derek Parker 已提交
260
			f, ln = currentLineNumber(p, t)
261
			if ln != tc.end {
D
Derek Parker 已提交
262
				t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, f, ln)
263 264
			}
		}
265

266
		if len(p.BreakPoints) != 1 {
D
Derek Parker 已提交
267
			t.Fatal("Not all breakpoints were cleaned up", len(p.BreakPoints))
268
		}
269 270
	})
}
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

func TestFindReturnAddress(t *testing.T) {
	var testfile, _ = filepath.Abs("../_fixtures/testnextprog")

	withTestProcess(testfile, t, func(p *DebuggedProcess) {
		var (
			fdes = p.FrameEntries
			gsd  = p.GoSymTable
		)

		testsourcefile := testfile + ".go"
		start, _, err := gsd.LineToPC(testsourcefile, 24)
		if err != nil {
			t.Fatal(err)
		}

		_, err = p.Break(uintptr(start))
		if err != nil {
			t.Fatal(err)
		}

		err = p.Continue()
		if err != nil {
			t.Fatal(err)
		}

		regs, err := p.Registers()
		if err != nil {
			t.Fatal(err)
		}

		fde, err := fdes.FDEForPC(start)
		if err != nil {
			t.Fatal(err)
		}

		ret := fde.ReturnAddressOffset(start)
		if err != nil {
			t.Fatal(err)
		}

		addr := uint64(int64(regs.SP()) + ret)
		data := make([]byte, 8)

		readMemory(p.Pid, uintptr(addr), data)
		addr = binary.LittleEndian.Uint64(data)

D
Derek Parker 已提交
318
		expected := uint64(0x400fbc)
319 320 321 322 323
		if addr != expected {
			t.Fatalf("return address not found correctly, expected %#v got %#v", expected, addr)
		}
	})
}