proctl_test.go 7.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
}

D
Derek Parker 已提交
40
func dataAtAddr(thread *ThreadContext, addr uint64) ([]byte, error) {
41
	data := make([]byte, 1)
D
Derek Parker 已提交
42
	_, err := readMemory(thread, uintptr(addr), data)
43 44 45 46 47 48 49
	if err != nil {
		return nil, err
	}

	return data, nil
}

50 51
func assertNoError(err error, t *testing.T, s string) {
	if err != nil {
52 53 54
		_, file, line, _ := runtime.Caller(1)
		fname := filepath.Base(file)
		t.Fatalf("failed assertion at %s:%d: %s : %s\n", fname, line, s, err)
55 56 57
	}
}

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

	return pc
}

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

D
Derek Parker 已提交
71
	return f, l
72 73
}

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
func TestExit(t *testing.T) {
	withTestProcess("../_fixtures/continuetestprog", t, func(p *DebuggedProcess) {
		err := p.Continue()
		pe, ok := err.(ProcessExitedError)
		if !ok {
			t.Fatalf("Continue() returned unexpected error type")
		}
		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)
		}
	})
}

90
func TestStep(t *testing.T) {
91
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
92 93 94
		helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
		helloworldaddr := helloworldfunc.Entry

95
		_, err := p.Break(helloworldaddr)
96 97 98
		assertNoError(err, t, "Break()")
		assertNoError(p.Continue(), t, "Continue()")

99
		regs := getRegisters(p, t)
100
		rip := regs.PC()
101

102
		err = p.Step()
D
Derek Parker 已提交
103
		assertNoError(err, t, "Step()")
104

105
		regs = getRegisters(p, t)
106 107 108 109 110
		if rip >= regs.PC() {
			t.Errorf("Expected %#v to be greater than %#v", regs.PC(), rip)
		}
	})
}
111

112
func TestBreakPoint(t *testing.T) {
113
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
D
Derek Parker 已提交
114 115
		helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
		helloworldaddr := helloworldfunc.Entry
116

D
Derek Parker 已提交
117
		bp, err := p.Break(helloworldaddr)
D
Derek Parker 已提交
118
		assertNoError(err, t, "Break()")
D
Derek Parker 已提交
119
		assertNoError(p.Continue(), t, "Continue()")
120

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

D
Derek Parker 已提交
126
		if pc-1 != bp.Addr && pc != bp.Addr {
127
			f, l, _ := p.GoSymTable.PCToLine(pc)
D
Derek Parker 已提交
128
			t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr)
129 130
		}
	})
131
}
132

133
func TestBreakPointInSeperateGoRoutine(t *testing.T) {
134
	withTestProcess("../_fixtures/testthreads", t, func(p *DebuggedProcess) {
135 136 137 138 139
		fn := p.GoSymTable.LookupFunc("main.anotherthread")
		if fn == nil {
			t.Fatal("No fn exists")
		}

140
		_, err := p.Break(fn.Entry)
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
		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")
		}
	})
}

162
func TestBreakPointWithNonExistantFunction(t *testing.T) {
163
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
164
		_, err := p.Break(0)
165 166 167 168
		if err == nil {
			t.Fatal("Should not be able to break at non existant function")
		}
	})
169
}
170 171

func TestClearBreakPoint(t *testing.T) {
172
	withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
173
		fn := p.GoSymTable.LookupFunc("main.sleepytime")
174
		bp, err := p.Break(fn.Entry)
D
Derek Parker 已提交
175
		assertNoError(err, t, "Break()")
176 177

		bp, err = p.Clear(fn.Entry)
D
Derek Parker 已提交
178
		assertNoError(err, t, "Clear()")
179

D
Derek Parker 已提交
180
		data, err := dataAtAddr(p.CurrentThread, bp.Addr)
181 182 183 184
		if err != nil {
			t.Fatal(err)
		}

185
		int3 := []byte{0xcc}
186 187 188 189
		if bytes.Equal(data, int3) {
			t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3)
		}

190
		if len(p.BreakPoints) != 0 {
191 192 193
			t.Fatal("Breakpoint not removed internally")
		}
	})
194
}
195 196 197

func TestNext(t *testing.T) {
	var (
198 199
		err            error
		executablePath = "../_fixtures/testnextprog"
200 201 202 203 204
	)

	testcases := []struct {
		begin, end int
	}{
D
Derek Parker 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
		{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 已提交
220
		{35, 41},
221
		{41, 40},
D
Derek Parker 已提交
222
		{40, 41},
223 224 225 226 227 228 229
	}

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

230
	withTestProcess(executablePath, t, func(p *DebuggedProcess) {
231
		pc, _, _ := p.GoSymTable.LineToPC(fp, testcases[0].begin)
232
		_, err := p.Break(pc)
233 234
		assertNoError(err, t, "Break()")
		assertNoError(p.Continue(), t, "Continue()")
235

D
Derek Parker 已提交
236
		f, ln := currentLineNumber(p, t)
237 238
		for _, tc := range testcases {
			if ln != tc.begin {
D
Derek Parker 已提交
239
				t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, f, ln)
240 241 242 243
			}

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

D
Derek Parker 已提交
244
			f, ln = currentLineNumber(p, t)
245
			if ln != tc.end {
D
Derek Parker 已提交
246
				t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, f, ln)
247 248
			}
		}
249

250 251 252 253 254 255 256 257
		p.Clear(pc)
		if len(p.BreakPoints) != 0 {
			t.Fatal("Not all breakpoints were cleaned up", len(p.HWBreakPoints))
		}
		for _, bp := range p.HWBreakPoints {
			if bp != nil {
				t.Fatal("Not all breakpoints were cleaned up", bp.Addr)
			}
258
		}
259 260
	})
}
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276

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)
		}

277
		_, err = p.Break(start)
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
		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)

D
Derek Parker 已提交
305
		readMemory(p.CurrentThread, uintptr(addr), data)
306 307
		addr = binary.LittleEndian.Uint64(data)

D
Derek Parker 已提交
308 309 310 311
		linuxExpected := uint64(0x400fbc)
		darwinExpected := uint64(0x23bc)
		if addr != linuxExpected && addr != darwinExpected {
			t.Fatalf("return address not found correctly, expected (linux) %#v or (darwin) %#v got %#v", linuxExpected, darwinExpected, addr)
312 313 314
		}
	})
}
D
Derek Parker 已提交
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

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

	withTestProcess(testfile, t, func(p *DebuggedProcess) {
		// With invalid thread id
		err := p.SwitchThread(-1)
		if err == nil {
			t.Fatal("Expected error for invalid thread id")
		}
		pc, err := p.FindLocation("main.main")
		if err != nil {
			t.Fatal(err)
		}
		_, err = p.Break(pc)
		if err != nil {
			t.Fatal(err)
		}
		err = p.Continue()
		if err != nil {
			t.Fatal(err)
		}
		var nt int
		ct := p.CurrentThread.Id
		for tid, _ := range p.Threads {
			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")
		}
	})
}