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
		helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
		helloworldaddr := helloworldfunc.Entry

77
		_, err := p.Break(helloworldaddr)
78 79 80
		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
		sleepyaddr := sleepytimefunc.Entry

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

117
		breakpc := bp.Addr
118
		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
		fn := p.GoSymTable.LookupFunc("main.anotherthread")
		if fn == nil {
			t.Fatal("No fn exists")
		}

152
		_, err := p.Break(fn.Entry)
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
		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
		_, err := p.Break(0)
177 178 179 180
		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
		fn := p.GoSymTable.LookupFunc("main.sleepytime")
186
		bp, err := p.Break(fn.Entry)
D
Derek Parker 已提交
187
		assertNoError(err, t, "Break()")
188 189

		bp, err = p.Clear(fn.Entry)
D
Derek Parker 已提交
190
		assertNoError(err, t, "Clear()")
191 192 193 194 195 196

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

197
		int3 := []byte{0xcc}
198 199 200 201
		if bytes.Equal(data, int3) {
			t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3)
		}

202
		if len(p.BreakPoints) != 0 {
203 204 205
			t.Fatal("Breakpoint not removed internally")
		}
	})
206
}
207 208 209

func TestNext(t *testing.T) {
	var (
210 211
		err            error
		executablePath = "../_fixtures/testnextprog"
212 213 214 215 216
	)

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

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

242
	withTestProcess(executablePath, t, func(p *DebuggedProcess) {
243
		pc, _, _ := p.GoSymTable.LineToPC(fp, testcases[0].begin)
244
		_, err := p.Break(pc)
245 246
		assertNoError(err, t, "Break()")
		assertNoError(p.Continue(), t, "Continue()")
247

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

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

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

262 263 264 265 266 267 268 269
		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)
			}
270
		}
271 272
	})
}
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288

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

289
		_, err = p.Break(start)
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 318 319
		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 已提交
320
		expected := uint64(0x400fbc)
321 322 323 324 325
		if addr != expected {
			t.Fatalf("return address not found correctly, expected %#v got %#v", expected, addr)
		}
	})
}