gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/proc_test.go (about) 1 package proc_test 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "flag" 7 "fmt" 8 "go/ast" 9 "go/constant" 10 "go/parser" 11 "go/token" 12 "io" 13 "math/rand" 14 "net" 15 "net/http" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "reflect" 20 "runtime" 21 "sort" 22 "strconv" 23 "strings" 24 "sync" 25 "testing" 26 "text/tabwriter" 27 "time" 28 29 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/frame" 30 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/op" 31 "gitlab.com/Raven-IO/raven-delve/pkg/goversion" 32 "gitlab.com/Raven-IO/raven-delve/pkg/logflags" 33 "gitlab.com/Raven-IO/raven-delve/pkg/proc" 34 "gitlab.com/Raven-IO/raven-delve/pkg/proc/core" 35 "gitlab.com/Raven-IO/raven-delve/pkg/proc/gdbserial" 36 "gitlab.com/Raven-IO/raven-delve/pkg/proc/native" 37 protest "gitlab.com/Raven-IO/raven-delve/pkg/proc/test" 38 "gitlab.com/Raven-IO/raven-delve/service/api" 39 ) 40 41 var normalLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1, 0} 42 var testBackend, buildMode string 43 44 func init() { 45 runtime.GOMAXPROCS(4) 46 os.Setenv("GOMAXPROCS", "4") 47 } 48 49 func TestMain(m *testing.M) { 50 flag.StringVar(&testBackend, "backend", "", "selects backend") 51 flag.StringVar(&buildMode, "test-buildmode", "", "selects build mode") 52 var logConf string 53 flag.StringVar(&logConf, "log", "", "configures logging") 54 flag.Parse() 55 protest.DefaultTestBackend(&testBackend) 56 if buildMode != "" && buildMode != "pie" { 57 fmt.Fprintf(os.Stderr, "unknown build mode %q", buildMode) 58 os.Exit(1) 59 } 60 logflags.Setup(logConf != "", logConf, "") 61 os.Exit(protest.RunTestsWithFixtures(m)) 62 } 63 64 func matchSkipConditions(conditions ...string) bool { 65 for _, cond := range conditions { 66 condfound := false 67 for _, s := range []string{runtime.GOOS, runtime.GOARCH, testBackend, buildMode} { 68 if s == cond { 69 condfound = true 70 break 71 } 72 } 73 if !condfound { 74 return false 75 } 76 } 77 return true 78 } 79 80 func skipOn(t testing.TB, reason string, conditions ...string) { 81 if matchSkipConditions(conditions...) { 82 t.Skipf("skipped on %s: %s", strings.Join(conditions, "/"), reason) 83 } 84 } 85 86 func skipUnlessOn(t testing.TB, reason string, conditions ...string) { 87 if !matchSkipConditions(conditions...) { 88 t.Skipf("skipped on %s: %s", strings.Join(conditions, "/"), reason) 89 } 90 } 91 92 func withTestProcess(name string, t testing.TB, fn func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture)) { 93 withTestProcessArgs(name, t, ".", []string{}, 0, fn) 94 } 95 96 func withTestProcessArgs(name string, t testing.TB, wd string, args []string, buildFlags protest.BuildFlags, fn func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture)) { 97 if buildMode == "pie" { 98 buildFlags |= protest.BuildModePIE 99 } 100 fixture := protest.BuildFixture(name, buildFlags) 101 var grp *proc.TargetGroup 102 var err error 103 var tracedir string 104 105 switch testBackend { 106 case "native": 107 grp, err = native.Launch(append([]string{fixture.Path}, args...), wd, 0, []string{}, "", "", proc.OutputRedirect{}, proc.OutputRedirect{}) 108 case "lldb": 109 grp, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, 0, []string{}, "", [3]string{}) 110 case "rr": 111 protest.MustHaveRecordingAllowed(t) 112 t.Log("recording") 113 grp, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{}, "", proc.OutputRedirect{}, proc.OutputRedirect{}) 114 t.Logf("replaying %q", tracedir) 115 default: 116 t.Fatal("unknown backend") 117 } 118 if err != nil { 119 t.Fatal("Launch():", err) 120 } 121 122 defer func() { 123 grp.Detach(true) 124 }() 125 126 fn(grp.Selected, grp, fixture) 127 } 128 129 func getRegisters(p *proc.Target, t *testing.T) proc.Registers { 130 regs, err := p.CurrentThread().Registers() 131 if err != nil { 132 t.Fatal("Registers():", err) 133 } 134 135 return regs 136 } 137 138 func dataAtAddr(thread proc.MemoryReadWriter, addr uint64) ([]byte, error) { 139 data := make([]byte, 1) 140 _, err := thread.ReadMemory(data, addr) 141 return data, err 142 } 143 144 func assertNoError(err error, t testing.TB, s string) { 145 if err != nil { 146 _, file, line, _ := runtime.Caller(1) 147 fname := filepath.Base(file) 148 t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err) 149 } 150 } 151 152 func currentPC(p *proc.Target, t *testing.T) uint64 { 153 regs, err := p.CurrentThread().Registers() 154 if err != nil { 155 t.Fatal(err) 156 } 157 158 return regs.PC() 159 } 160 161 func currentLineNumber(p *proc.Target, t *testing.T) (string, int) { 162 pc := currentPC(p, t) 163 f, l, _ := p.BinInfo().PCToLine(pc) 164 return f, l 165 } 166 167 func assertLineNumber(p *proc.Target, t *testing.T, lineno int, descr string) (string, int) { 168 f, l := currentLineNumber(p, t) 169 if l != lineno { 170 _, callerFile, callerLine, _ := runtime.Caller(1) 171 t.Fatalf("%s expected line :%d got %s:%d\n\tat %s:%d", descr, lineno, f, l, callerFile, callerLine) 172 } 173 return f, l 174 } 175 176 func assertFunctionName(p *proc.Target, t *testing.T, fnname string, descr string) { 177 pc := currentPC(p, t) 178 f, l, fn := p.BinInfo().PCToLine(pc) 179 if fn == nil { 180 t.Fatalf("%s expected function %s got %s:%d", descr, fnname, f, l) 181 } 182 if fn.Name != fnname { 183 t.Fatalf("%s expected function %s got %s %s:%d", descr, fnname, fn.Name, f, l) 184 } 185 } 186 187 func TestExit(t *testing.T) { 188 protest.AllowRecording(t) 189 withTestProcess("continuetestprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 190 err := grp.Continue() 191 pe, ok := err.(proc.ErrProcessExited) 192 if !ok { 193 t.Fatalf("Continue() returned unexpected error type %s", err) 194 } 195 if pe.Status != 0 { 196 t.Errorf("Unexpected error status: %d", pe.Status) 197 } 198 if pe.Pid != p.Pid() { 199 t.Errorf("Unexpected process id: %d", pe.Pid) 200 } 201 }) 202 } 203 204 func TestExitAfterContinue(t *testing.T) { 205 protest.AllowRecording(t) 206 withTestProcess("continuetestprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 207 setFunctionBreakpoint(p, t, "main.sayhi") 208 assertNoError(grp.Continue(), t, "First Continue()") 209 err := grp.Continue() 210 pe, ok := err.(proc.ErrProcessExited) 211 if !ok { 212 t.Fatalf("Continue() returned unexpected error type %s", pe) 213 } 214 if pe.Status != 0 { 215 t.Errorf("Unexpected error status: %d", pe.Status) 216 } 217 if pe.Pid != p.Pid() { 218 t.Errorf("Unexpected process id: %d", pe.Pid) 219 } 220 }) 221 } 222 223 func setFunctionBreakpoint(p *proc.Target, t testing.TB, fname string) *proc.Breakpoint { 224 t.Helper() 225 addrs, err := proc.FindFunctionLocation(p, fname, 0) 226 if err != nil { 227 t.Fatalf("FindFunctionLocation(%s): %v", fname, err) 228 } 229 if len(addrs) != 1 { 230 t.Fatalf("setFunctionBreakpoint(%s): too many results %v", fname, addrs) 231 } 232 bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil) 233 if err != nil { 234 t.Fatalf("FindFunctionLocation(%s): %v", fname, err) 235 } 236 return bp 237 } 238 239 func setFunctionBreakpointAll(p *proc.Target, t testing.TB, fname string) { 240 t.Helper() 241 addrs, err := proc.FindFunctionLocation(p, fname, 0) 242 if err != nil { 243 t.Fatalf("FindFunctionLocation(%s): %v", fname, err) 244 } 245 for _, addr := range addrs { 246 _, err := p.SetBreakpoint(int(addr), addr, proc.UserBreakpoint, nil) 247 if err != nil { 248 t.Fatalf("FindFunctionLocation(%s): %v", fname, err) 249 } 250 } 251 } 252 253 func setFileBreakpoint(p *proc.Target, t testing.TB, path string, lineno int) *proc.Breakpoint { 254 _, f, l, _ := runtime.Caller(1) 255 f = filepath.Base(f) 256 257 addrs, err := proc.FindFileLocation(p, path, lineno) 258 if err != nil { 259 t.Fatalf("%s:%d: FindFileLocation(%s, %d): %v", f, l, path, lineno, err) 260 } 261 if len(addrs) != 1 { 262 t.Fatalf("%s:%d: setFileLineBreakpoint(%s, %d): too many (or not enough) results %v", f, l, path, lineno, addrs) 263 } 264 bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil) 265 if err != nil { 266 t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err) 267 } 268 return bp 269 } 270 271 func findFunctionLocation(p *proc.Target, t *testing.T, fnname string) uint64 { 272 _, f, l, _ := runtime.Caller(1) 273 f = filepath.Base(f) 274 addrs, err := proc.FindFunctionLocation(p, fnname, 0) 275 if err != nil { 276 t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fnname, err) 277 } 278 if len(addrs) != 1 { 279 t.Fatalf("%s:%d: FindFunctionLocation(%s): too many results %v", f, l, fnname, addrs) 280 } 281 return addrs[0] 282 } 283 284 func findFileLocation(p *proc.Target, t *testing.T, file string, lineno int) uint64 { 285 _, f, l, _ := runtime.Caller(1) 286 f = filepath.Base(f) 287 addrs, err := proc.FindFileLocation(p, file, lineno) 288 if err != nil { 289 t.Fatalf("%s:%d: FindFileLocation(%s, %d): %v", f, l, file, lineno, err) 290 } 291 if len(addrs) != 1 { 292 t.Fatalf("%s:%d: FindFileLocation(%s, %d): too many results %v", f, l, file, lineno, addrs) 293 } 294 return addrs[0] 295 } 296 297 func TestHalt(t *testing.T) { 298 stopChan := make(chan interface{}, 1) 299 withTestProcess("loopprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 300 setFunctionBreakpoint(p, t, "main.loop") 301 assertNoError(grp.Continue(), t, "Continue") 302 resumeChan := make(chan struct{}, 1) 303 go func() { 304 <-resumeChan 305 time.Sleep(100 * time.Millisecond) 306 stopChan <- grp.RequestManualStop() 307 }() 308 grp.ResumeNotify(resumeChan) 309 assertNoError(grp.Continue(), t, "Continue") 310 retVal := <-stopChan 311 312 if err, ok := retVal.(error); ok && err != nil { 313 t.Fatal() 314 } 315 }) 316 } 317 318 func TestStepInstruction(t *testing.T) { 319 protest.AllowRecording(t) 320 withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 321 setFunctionBreakpoint(p, t, "main.helloworld") 322 assertNoError(grp.Continue(), t, "Continue()") 323 324 regs := getRegisters(p, t) 325 rip := regs.PC() 326 327 err := grp.StepInstruction(false) 328 assertNoError(err, t, "Step()") 329 330 regs = getRegisters(p, t) 331 if rip >= regs.PC() { 332 t.Errorf("Expected %#v to be greater than %#v", regs.PC(), rip) 333 } 334 }) 335 } 336 337 func TestNextInstruction(t *testing.T) { 338 protest.AllowRecording(t) 339 withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 340 setFileBreakpoint(p, t, fixture.Source, 19) 341 assertNoError(grp.Continue(), t, "Continue()") 342 343 err := grp.StepInstruction(true) 344 assertNoError(err, t, "Step()") 345 346 assertLineNumber(p, t, 20, "next-instruction did not step over call") 347 }) 348 } 349 350 func TestBreakpoint(t *testing.T) { 351 protest.AllowRecording(t) 352 withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 353 bp := setFunctionBreakpoint(p, t, "main.helloworld") 354 assertNoError(grp.Continue(), t, "Continue()") 355 356 regs, err := p.CurrentThread().Registers() 357 assertNoError(err, t, "Registers") 358 pc := regs.PC() 359 360 if bp.Logical.TotalHitCount != 1 { 361 t.Fatalf("Breakpoint should be hit once, got %d\n", bp.Logical.TotalHitCount) 362 } 363 364 if pc-1 != bp.Addr && pc != bp.Addr { 365 f, l, _ := p.BinInfo().PCToLine(pc) 366 t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr) 367 } 368 }) 369 } 370 371 func TestBreakpointInSeparateGoRoutine(t *testing.T) { 372 protest.AllowRecording(t) 373 withTestProcess("testthreads", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 374 setFunctionBreakpoint(p, t, "main.anotherthread") 375 376 assertNoError(grp.Continue(), t, "Continue") 377 378 regs, err := p.CurrentThread().Registers() 379 assertNoError(err, t, "Registers") 380 pc := regs.PC() 381 382 f, l, _ := p.BinInfo().PCToLine(pc) 383 if f != "testthreads.go" && l != 8 { 384 t.Fatal("Program did not hit breakpoint") 385 } 386 }) 387 } 388 389 func TestBreakpointWithNonExistentFunction(t *testing.T) { 390 withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 391 _, err := p.SetBreakpoint(0, 0, proc.UserBreakpoint, nil) 392 if err == nil { 393 t.Fatal("Should not be able to break at non existent function") 394 } 395 }) 396 } 397 398 func TestClearBreakpointBreakpoint(t *testing.T) { 399 withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 400 bp := setFunctionBreakpoint(p, t, "main.sleepytime") 401 402 err := p.ClearBreakpoint(bp.Addr) 403 assertNoError(err, t, "ClearBreakpoint()") 404 405 data, err := dataAtAddr(p.Memory(), bp.Addr) 406 assertNoError(err, t, "dataAtAddr") 407 408 int3 := []byte{0xcc} 409 if bytes.Equal(data, int3) { 410 t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3) 411 } 412 413 if countBreakpoints(p) != 0 { 414 t.Fatal("Breakpoint not removed internally") 415 } 416 }) 417 } 418 419 type nextTest struct { 420 begin, end int 421 } 422 423 func countBreakpoints(p *proc.Target) int { 424 bpcount := 0 425 for _, bp := range p.Breakpoints().M { 426 if bp.LogicalID() >= 0 { 427 bpcount++ 428 } 429 } 430 return bpcount 431 } 432 433 type contFunc int 434 435 const ( 436 contContinue contFunc = iota 437 contNext 438 contStep 439 contStepout 440 contReverseNext 441 contReverseStep 442 contReverseStepout 443 contContinueToBreakpoint 444 contNothing 445 ) 446 447 type seqTest struct { 448 cf contFunc 449 pos interface{} 450 } 451 452 func testseq(program string, contFunc contFunc, testcases []nextTest, initialLocation string, t *testing.T) { 453 seqTestcases := make([]seqTest, len(testcases)+1) 454 seqTestcases[0] = seqTest{contContinue, testcases[0].begin} 455 for i := range testcases { 456 if i > 0 { 457 if testcases[i-1].end != testcases[i].begin { 458 panic(fmt.Errorf("begin/end mismatch at index %d", i)) 459 } 460 } 461 seqTestcases[i+1] = seqTest{contFunc, testcases[i].end} 462 } 463 testseq2(t, program, initialLocation, seqTestcases) 464 } 465 466 const traceTestseq2 = true 467 468 func testseq2(t *testing.T, program string, initialLocation string, testcases []seqTest) { 469 testseq2Args(".", []string{}, 0, t, program, initialLocation, testcases) 470 } 471 472 func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *testing.T, program string, initialLocation string, testcases []seqTest) { 473 protest.AllowRecording(t) 474 withTestProcessArgs(program, t, wd, args, buildFlags, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 475 var bp *proc.Breakpoint 476 if initialLocation != "" { 477 bp = setFunctionBreakpoint(p, t, initialLocation) 478 } else if testcases[0].cf == contContinue { 479 bp = setFileBreakpoint(p, t, fixture.Source, testcases[0].pos.(int)) 480 } else { 481 panic("testseq2 can not set initial breakpoint") 482 } 483 if traceTestseq2 { 484 t.Logf("initial breakpoint %v", bp) 485 } 486 regs, err := p.CurrentThread().Registers() 487 assertNoError(err, t, "Registers") 488 489 f, ln := currentLineNumber(p, t) 490 for i, tc := range testcases { 491 switch tc.cf { 492 case contNext: 493 if traceTestseq2 { 494 t.Log("next") 495 } 496 assertNoError(grp.Next(), t, "Next() returned an error") 497 case contStep: 498 if traceTestseq2 { 499 t.Log("step") 500 } 501 assertNoError(grp.Step(), t, "Step() returned an error") 502 case contStepout: 503 if traceTestseq2 { 504 t.Log("stepout") 505 } 506 assertNoError(grp.StepOut(), t, "StepOut() returned an error") 507 case contContinue: 508 if traceTestseq2 { 509 t.Log("continue") 510 } 511 assertNoError(grp.Continue(), t, "Continue() returned an error") 512 if i == 0 { 513 if traceTestseq2 { 514 t.Log("clearing initial breakpoint") 515 } 516 err := p.ClearBreakpoint(bp.Addr) 517 assertNoError(err, t, "ClearBreakpoint() returned an error") 518 } 519 case contReverseNext: 520 if traceTestseq2 { 521 t.Log("reverse-next") 522 } 523 assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch") 524 assertNoError(grp.Next(), t, "reverse Next() returned an error") 525 assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch") 526 case contReverseStep: 527 if traceTestseq2 { 528 t.Log("reverse-step") 529 } 530 assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch") 531 assertNoError(grp.Step(), t, "reverse Step() returned an error") 532 assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch") 533 case contReverseStepout: 534 if traceTestseq2 { 535 t.Log("reverse-stepout") 536 } 537 assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch") 538 assertNoError(grp.StepOut(), t, "reverse StepOut() returned an error") 539 assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch") 540 case contContinueToBreakpoint: 541 bp := setFileBreakpoint(p, t, fixture.Source, tc.pos.(int)) 542 if traceTestseq2 { 543 t.Log("continue") 544 } 545 assertNoError(grp.Continue(), t, "Continue() returned an error") 546 err := p.ClearBreakpoint(bp.Addr) 547 assertNoError(err, t, "ClearBreakpoint() returned an error") 548 case contNothing: 549 // do nothing 550 } 551 552 if err := p.CurrentThread().Breakpoint().CondError; err != nil { 553 t.Logf("breakpoint condition error: %v", err) 554 } 555 556 f, ln = currentLineNumber(p, t) 557 regs, _ = p.CurrentThread().Registers() 558 pc := regs.PC() 559 560 if traceTestseq2 { 561 t.Logf("at %#x %s:%d", pc, f, ln) 562 fmt.Printf("at %#x %s:%d\n", pc, f, ln) 563 } 564 switch pos := tc.pos.(type) { 565 case int: 566 if pos >= 0 && ln != pos { 567 t.Fatalf("Program did not continue to correct next location expected %d was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i) 568 } 569 case string: 570 v := strings.Split(pos, ":") 571 tgtln, _ := strconv.Atoi(v[1]) 572 if !strings.HasSuffix(f, v[0]) || (ln != tgtln) { 573 t.Fatalf("Program did not continue to correct next location, expected %s was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i) 574 } 575 case func(*proc.Target): 576 pos(p) 577 default: 578 panic(fmt.Errorf("unexpected type %T", pos)) 579 } 580 } 581 582 if countBreakpoints(p) != 0 { 583 t.Fatal("Not all breakpoints were cleaned up", len(p.Breakpoints().M)) 584 } 585 }) 586 } 587 588 func TestNextGeneral(t *testing.T) { 589 var testcases []nextTest 590 591 ver, _ := goversion.Parse(runtime.Version()) 592 593 if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) { 594 testcases = []nextTest{ 595 {17, 19}, 596 {19, 20}, 597 {20, 23}, 598 {23, 24}, 599 {24, 26}, 600 {26, 31}, 601 {31, 23}, 602 {23, 24}, 603 {24, 26}, 604 {26, 31}, 605 {31, 23}, 606 {23, 24}, 607 {24, 26}, 608 {26, 27}, 609 {27, 28}, 610 {28, 34}, 611 } 612 } else { 613 testcases = []nextTest{ 614 {17, 19}, 615 {19, 20}, 616 {20, 23}, 617 {23, 24}, 618 {24, 26}, 619 {26, 31}, 620 {31, 23}, 621 {23, 24}, 622 {24, 26}, 623 {26, 31}, 624 {31, 23}, 625 {23, 24}, 626 {24, 26}, 627 {26, 27}, 628 {27, 34}, 629 } 630 } 631 632 testseq("testnextprog", contNext, testcases, "main.testnext", t) 633 } 634 635 func TestNextConcurrent(t *testing.T) { 636 skipOn(t, "broken", "windows", "arm64") 637 testcases := []nextTest{ 638 {8, 9}, 639 {9, 10}, 640 {10, 11}, 641 } 642 protest.AllowRecording(t) 643 withTestProcess("parallel_next", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 644 bp := setFunctionBreakpoint(p, t, "main.sayhi") 645 assertNoError(grp.Continue(), t, "Continue") 646 f, ln := currentLineNumber(p, t) 647 initV := evalVariable(p, t, "n") 648 initVval, _ := constant.Int64Val(initV.Value) 649 err := p.ClearBreakpoint(bp.Addr) 650 assertNoError(err, t, "ClearBreakpoint()") 651 for _, tc := range testcases { 652 g, err := proc.GetG(p.CurrentThread()) 653 assertNoError(err, t, "GetG()") 654 if p.SelectedGoroutine().ID != g.ID { 655 t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.SelectedGoroutine().ID) 656 } 657 if ln != tc.begin { 658 t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) 659 } 660 assertNoError(grp.Next(), t, "Next() returned an error") 661 f, ln = assertLineNumber(p, t, tc.end, "Program did not continue to the expected location") 662 v := evalVariable(p, t, "n") 663 vval, _ := constant.Int64Val(v.Value) 664 if vval != initVval { 665 t.Fatal("Did not end up on same goroutine") 666 } 667 } 668 }) 669 } 670 671 func TestNextConcurrentVariant2(t *testing.T) { 672 skipOn(t, "broken", "windows", "arm64") 673 // Just like TestNextConcurrent but instead of removing the initial breakpoint we check that when it happens is for other goroutines 674 testcases := []nextTest{ 675 {8, 9}, 676 {9, 10}, 677 {10, 11}, 678 } 679 protest.AllowRecording(t) 680 withTestProcess("parallel_next", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 681 setFunctionBreakpoint(p, t, "main.sayhi") 682 assertNoError(grp.Continue(), t, "Continue") 683 f, ln := currentLineNumber(p, t) 684 initV := evalVariable(p, t, "n") 685 initVval, _ := constant.Int64Val(initV.Value) 686 for _, tc := range testcases { 687 t.Logf("test case %v", tc) 688 g, err := proc.GetG(p.CurrentThread()) 689 assertNoError(err, t, "GetG()") 690 if p.SelectedGoroutine().ID != g.ID { 691 t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.SelectedGoroutine().ID) 692 } 693 if ln != tc.begin { 694 t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) 695 } 696 assertNoError(grp.Next(), t, "Next() returned an error") 697 var vval int64 698 for { 699 v := evalVariable(p, t, "n") 700 for _, thread := range p.ThreadList() { 701 proc.GetG(thread) 702 } 703 vval, _ = constant.Int64Val(v.Value) 704 if bpstate := p.CurrentThread().Breakpoint(); bpstate.Breakpoint == nil { 705 if vval != initVval { 706 t.Fatal("Did not end up on same goroutine") 707 } 708 break 709 } else { 710 if vval == initVval { 711 t.Fatal("Initial breakpoint triggered twice for the same goroutine") 712 } 713 assertNoError(grp.Continue(), t, "Continue 2") 714 } 715 } 716 f, ln = assertLineNumber(p, t, tc.end, "Program did not continue to the expected location") 717 } 718 }) 719 } 720 721 func TestNextFunctionReturn(t *testing.T) { 722 testcases := []nextTest{ 723 {13, 14}, 724 {14, 15}, 725 {15, 35}, 726 } 727 protest.AllowRecording(t) 728 testseq("testnextprog", contNext, testcases, "main.helloworld", t) 729 } 730 731 func TestNextFunctionReturnDefer(t *testing.T) { 732 var testcases []nextTest 733 734 ver, _ := goversion.Parse(runtime.Version()) 735 736 if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) { 737 testcases = []nextTest{ 738 {5, 6}, 739 {6, 9}, 740 {9, 10}, 741 } 742 } else { 743 testcases = []nextTest{ 744 {5, 8}, 745 {8, 9}, 746 {9, 10}, 747 } 748 } 749 protest.AllowRecording(t) 750 testseq("testnextdefer", contNext, testcases, "main.main", t) 751 } 752 753 func TestNextNetHTTP(t *testing.T) { 754 testcases := []nextTest{ 755 {11, 12}, 756 {12, 13}, 757 } 758 withTestProcess("testnextnethttp", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 759 go func() { 760 // Wait for program to start listening. 761 for { 762 conn, err := net.Dial("tcp", "127.0.0.1:9191") 763 if err == nil { 764 conn.Close() 765 break 766 } 767 time.Sleep(50 * time.Millisecond) 768 } 769 resp, err := http.Get("http://127.0.0.1:9191") 770 if err == nil { 771 resp.Body.Close() 772 } 773 }() 774 if err := grp.Continue(); err != nil { 775 t.Fatal(err) 776 } 777 f, ln := currentLineNumber(p, t) 778 for _, tc := range testcases { 779 if ln != tc.begin { 780 t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) 781 } 782 783 assertNoError(grp.Next(), t, "Next() returned an error") 784 785 f, ln = assertLineNumber(p, t, tc.end, "Program did not continue to correct next location") 786 } 787 }) 788 } 789 790 func TestRuntimeBreakpoint(t *testing.T) { 791 withTestProcess("testruntimebreakpoint", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 792 err := grp.Continue() 793 if err != nil { 794 t.Fatal(err) 795 } 796 regs, err := p.CurrentThread().Registers() 797 assertNoError(err, t, "Registers") 798 pc := regs.PC() 799 f, l, _ := p.BinInfo().PCToLine(pc) 800 if l != 10 { 801 t.Fatalf("did not respect breakpoint %s:%d", f, l) 802 } 803 }) 804 } 805 806 func returnAddress(tgt *proc.Target, thread proc.Thread) (uint64, error) { 807 locations, err := proc.ThreadStacktrace(tgt, thread, 2) 808 if err != nil { 809 return 0, err 810 } 811 if len(locations) < 2 { 812 return 0, fmt.Errorf("no return address for function: %s", locations[0].Current.Fn.BaseName()) 813 } 814 return locations[1].Current.PC, nil 815 } 816 817 func TestFindReturnAddress(t *testing.T) { 818 protest.AllowRecording(t) 819 withTestProcess("testnextprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 820 setFileBreakpoint(p, t, fixture.Source, 24) 821 err := grp.Continue() 822 if err != nil { 823 t.Fatal(err) 824 } 825 addr, err := returnAddress(p, p.CurrentThread()) 826 if err != nil { 827 t.Fatal(err) 828 } 829 _, l, _ := p.BinInfo().PCToLine(addr) 830 if l != 40 { 831 t.Fatalf("return address not found correctly, expected line 40") 832 } 833 }) 834 } 835 836 func TestFindReturnAddressTopOfStackFn(t *testing.T) { 837 skipOn(t, "broken in linux ppc64le", "linux", "ppc64le", "native") 838 protest.AllowRecording(t) 839 withTestProcess("testreturnaddress", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 840 fnName := "runtime.rt0_go" 841 setFunctionBreakpoint(p, t, fnName) 842 if err := grp.Continue(); err != nil { 843 t.Fatal(err) 844 } 845 if _, err := returnAddress(p, p.CurrentThread()); err == nil { 846 t.Fatal("expected error to be returned") 847 } 848 }) 849 } 850 851 func TestSwitchThread(t *testing.T) { 852 protest.AllowRecording(t) 853 withTestProcess("testnextprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 854 // With invalid thread id 855 err := p.SwitchThread(-1) 856 if err == nil { 857 t.Fatal("Expected error for invalid thread id") 858 } 859 setFunctionBreakpoint(p, t, "main.main") 860 err = grp.Continue() 861 if err != nil { 862 t.Fatal(err) 863 } 864 var nt int 865 ct := p.CurrentThread().ThreadID() 866 for _, thread := range p.ThreadList() { 867 if thread.ThreadID() != ct { 868 nt = thread.ThreadID() 869 break 870 } 871 } 872 if nt == 0 { 873 t.Fatal("could not find thread to switch to") 874 } 875 // With valid thread id 876 err = p.SwitchThread(nt) 877 if err != nil { 878 t.Fatal(err) 879 } 880 if p.CurrentThread().ThreadID() != nt { 881 t.Fatal("Did not switch threads") 882 } 883 }) 884 } 885 886 func TestCGONext(t *testing.T) { 887 // Test if one can do 'next' in a cgo binary 888 // On OSX with Go < 1.5 CGO is not supported due to: https://github.com/golang/go/issues/8973 889 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 5) { 890 skipOn(t, "upstream issue", "darwin") 891 } 892 protest.MustHaveCgo(t) 893 894 skipOn(t, "broken - see https://gitlab.com/Raven-IO/raven-delve/issues/3158", "darwin", "amd64") 895 896 protest.AllowRecording(t) 897 withTestProcess("cgotest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 898 setFunctionBreakpoint(p, t, "main.main") 899 assertNoError(grp.Continue(), t, "Continue()") 900 assertNoError(grp.Next(), t, "Next()") 901 }) 902 } 903 904 func TestCGOBreakpointLocation(t *testing.T) { 905 protest.MustHaveCgo(t) 906 protest.AllowRecording(t) 907 908 withTestProcess("cgotest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 909 bp := setFunctionBreakpoint(p, t, "C.foo") 910 if !strings.Contains(bp.File, "cgotest.go") { 911 t.Fatalf("incorrect breakpoint location, expected cgotest.go got %s", bp.File) 912 } 913 }) 914 } 915 916 type loc struct { 917 line int 918 fn string 919 } 920 921 func (l1 *loc) match(l2 proc.Stackframe) bool { 922 if l1.line >= 0 { 923 if l1.line != l2.Call.Line { 924 return false 925 } 926 } 927 return l1.fn == l2.Call.Fn.Name 928 } 929 930 func TestStacktrace(t *testing.T) { 931 stacks := [][]loc{ 932 {{4, "main.stacktraceme"}, {8, "main.func1"}, {16, "main.main"}}, 933 {{4, "main.stacktraceme"}, {8, "main.func1"}, {12, "main.func2"}, {17, "main.main"}}, 934 } 935 protest.AllowRecording(t) 936 withTestProcess("stacktraceprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 937 bp := setFunctionBreakpoint(p, t, "main.stacktraceme") 938 939 for i := range stacks { 940 assertNoError(grp.Continue(), t, "Continue()") 941 locations, err := proc.ThreadStacktrace(p, p.CurrentThread(), 40) 942 assertNoError(err, t, "Stacktrace()") 943 944 if len(locations) != len(stacks[i])+2 { 945 t.Fatalf("Wrong stack trace size %d %d\n", len(locations), len(stacks[i])+2) 946 } 947 948 t.Logf("Stacktrace %d:\n", i) 949 for i := range locations { 950 t.Logf("\t%s:%d\n", locations[i].Call.File, locations[i].Call.Line) 951 } 952 953 for j := range stacks[i] { 954 if !stacks[i][j].match(locations[j]) { 955 t.Fatalf("Wrong stack trace pos %d\n", j) 956 } 957 } 958 } 959 960 p.ClearBreakpoint(bp.Addr) 961 grp.Continue() 962 }) 963 } 964 965 func TestStacktrace2(t *testing.T) { 966 withTestProcess("retstack", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 967 assertNoError(grp.Continue(), t, "Continue()") 968 969 locations, err := proc.ThreadStacktrace(p, p.CurrentThread(), 40) 970 assertNoError(err, t, "Stacktrace()") 971 if !stackMatch([]loc{{-1, "main.f"}, {16, "main.main"}}, locations, false) { 972 for i := range locations { 973 t.Logf("\t%s:%d [%s]\n", locations[i].Call.File, locations[i].Call.Line, locations[i].Call.Fn.Name) 974 } 975 t.Fatalf("Stack error at main.f()\n%v\n", locations) 976 } 977 978 assertNoError(grp.Continue(), t, "Continue()") 979 locations, err = proc.ThreadStacktrace(p, p.CurrentThread(), 40) 980 assertNoError(err, t, "Stacktrace()") 981 if !stackMatch([]loc{{-1, "main.g"}, {17, "main.main"}}, locations, false) { 982 for i := range locations { 983 t.Logf("\t%s:%d [%s]\n", locations[i].Call.File, locations[i].Call.Line, locations[i].Call.Fn.Name) 984 } 985 t.Fatalf("Stack error at main.g()\n%v\n", locations) 986 } 987 }) 988 989 } 990 991 func stackMatch(stack []loc, locations []proc.Stackframe, skipRuntime bool) bool { 992 if len(stack) > len(locations) { 993 return false 994 } 995 i := 0 996 for j := range locations { 997 if i >= len(stack) { 998 break 999 } 1000 if skipRuntime { 1001 if locations[j].Call.Fn == nil || strings.HasPrefix(locations[j].Call.Fn.Name, "runtime.") { 1002 continue 1003 } 1004 } 1005 if !stack[i].match(locations[j]) { 1006 return false 1007 } 1008 i++ 1009 } 1010 return i >= len(stack) 1011 } 1012 1013 func TestStacktraceGoroutine(t *testing.T) { 1014 mainStack := []loc{{14, "main.stacktraceme"}, {29, "main.main"}} 1015 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) { 1016 mainStack[0].line = 15 1017 } 1018 agoroutineStacks := [][]loc{ 1019 {{8, "main.agoroutine"}}, 1020 {{9, "main.agoroutine"}}, 1021 {{10, "main.agoroutine"}}, 1022 } 1023 1024 lenient := 0 1025 if runtime.GOOS == "windows" { 1026 lenient = 1 1027 } 1028 1029 protest.AllowRecording(t) 1030 withTestProcess("goroutinestackprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1031 bp := setFunctionBreakpoint(p, t, "main.stacktraceme") 1032 1033 assertNoError(grp.Continue(), t, "Continue()") 1034 1035 gs, _, err := proc.GoroutinesInfo(p, 0, 0) 1036 assertNoError(err, t, "GoroutinesInfo") 1037 1038 agoroutineCount := 0 1039 mainCount := 0 1040 1041 for i, g := range gs { 1042 locations, err := proc.GoroutineStacktrace(p, g, 40, 0) 1043 if err != nil { 1044 // On windows we do not have frame information for goroutines doing system calls. 1045 t.Logf("Could not retrieve goroutine stack for goid=%d: %v", g.ID, err) 1046 continue 1047 } 1048 1049 if stackMatch(mainStack, locations, false) { 1050 mainCount++ 1051 } 1052 1053 found := false 1054 for _, agoroutineStack := range agoroutineStacks { 1055 if stackMatch(agoroutineStack, locations, true) { 1056 found = true 1057 } 1058 } 1059 1060 if found { 1061 agoroutineCount++ 1062 } else { 1063 t.Logf("Non-goroutine stack: %d (%d)", i, len(locations)) 1064 for i := range locations { 1065 name := "" 1066 if locations[i].Call.Fn != nil { 1067 name = locations[i].Call.Fn.Name 1068 } 1069 t.Logf("\t%s:%d %s (%#x) %x %v\n", locations[i].Call.File, locations[i].Call.Line, name, locations[i].Current.PC, locations[i].FrameOffset(), locations[i].SystemStack) 1070 } 1071 } 1072 } 1073 1074 if mainCount != 1 { 1075 t.Fatalf("Main goroutine stack not found %d", mainCount) 1076 } 1077 1078 if agoroutineCount < 10-lenient { 1079 t.Fatalf("Goroutine stacks not found (%d)", agoroutineCount) 1080 } 1081 1082 p.ClearBreakpoint(bp.Addr) 1083 grp.Continue() 1084 }) 1085 } 1086 1087 func TestKill(t *testing.T) { 1088 withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1089 if err := grp.Detach(true); err != nil { 1090 t.Fatal(err) 1091 } 1092 if valid, _ := p.Valid(); valid { 1093 t.Fatal("expected process to have exited") 1094 } 1095 if runtime.GOOS == "linux" { 1096 if runtime.GOARCH == "arm64" { 1097 // there is no any sync between signal sended(tracee handled) and open /proc/%d/. It may fail on arm64 1098 return 1099 } 1100 _, err := os.Open(fmt.Sprintf("/proc/%d/", p.Pid())) 1101 if err == nil { 1102 t.Fatal("process has not exited", p.Pid()) 1103 } 1104 } 1105 }) 1106 } 1107 1108 func testGSupportFunc(name string, t *testing.T, p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1109 bp := setFunctionBreakpoint(p, t, "main.main") 1110 1111 assertNoError(grp.Continue(), t, name+": Continue()") 1112 1113 g, err := proc.GetG(p.CurrentThread()) 1114 assertNoError(err, t, name+": GetG()") 1115 1116 if g == nil { 1117 t.Fatal(name + ": g was nil") 1118 } 1119 1120 t.Logf(name+": g is: %v", g) 1121 1122 p.ClearBreakpoint(bp.Addr) 1123 } 1124 1125 func TestGetG(t *testing.T) { 1126 withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1127 testGSupportFunc("nocgo", t, p, grp, fixture) 1128 }) 1129 1130 // On OSX with Go < 1.5 CGO is not supported due to: https://github.com/golang/go/issues/8973 1131 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 5) { 1132 skipOn(t, "upstream issue", "darwin") 1133 } 1134 protest.MustHaveCgo(t) 1135 1136 protest.AllowRecording(t) 1137 withTestProcess("cgotest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1138 testGSupportFunc("cgo", t, p, grp, fixture) 1139 }) 1140 } 1141 1142 func TestContinueMulti(t *testing.T) { 1143 protest.AllowRecording(t) 1144 withTestProcess("integrationprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1145 bp1 := setFunctionBreakpoint(p, t, "main.main") 1146 bp2 := setFunctionBreakpoint(p, t, "main.sayhi") 1147 1148 mainCount := 0 1149 sayhiCount := 0 1150 for { 1151 err := grp.Continue() 1152 if valid, _ := p.Valid(); !valid { 1153 break 1154 } 1155 assertNoError(err, t, "Continue()") 1156 1157 if bp := p.CurrentThread().Breakpoint(); bp.LogicalID() == bp1.LogicalID() { 1158 mainCount++ 1159 } 1160 1161 if bp := p.CurrentThread().Breakpoint(); bp.LogicalID() == bp2.LogicalID() { 1162 sayhiCount++ 1163 } 1164 } 1165 1166 if mainCount != 1 { 1167 t.Fatalf("Main breakpoint hit wrong number of times: %d\n", mainCount) 1168 } 1169 1170 if sayhiCount != 3 { 1171 t.Fatalf("Sayhi breakpoint hit wrong number of times: %d\n", sayhiCount) 1172 } 1173 }) 1174 } 1175 1176 func TestBreakpointOnFunctionEntry(t *testing.T) { 1177 testseq2(t, "testprog", "main.main", []seqTest{{contContinue, 17}}) 1178 } 1179 1180 func TestProcessReceivesSIGCHLD(t *testing.T) { 1181 protest.AllowRecording(t) 1182 withTestProcess("sigchldprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1183 err := grp.Continue() 1184 _, ok := err.(proc.ErrProcessExited) 1185 if !ok { 1186 t.Fatalf("Continue() returned unexpected error type %v", err) 1187 } 1188 }) 1189 } 1190 1191 func TestIssue239(t *testing.T) { 1192 withTestProcess("is sue239", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1193 setFileBreakpoint(p, t, fixture.Source, 17) 1194 assertNoError(grp.Continue(), t, "Continue()") 1195 }) 1196 } 1197 1198 func findFirstNonRuntimeFrame(p *proc.Target) (proc.Stackframe, error) { 1199 frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 10) 1200 if err != nil { 1201 return proc.Stackframe{}, err 1202 } 1203 1204 for _, frame := range frames { 1205 if frame.Current.Fn != nil && !strings.HasPrefix(frame.Current.Fn.Name, "runtime.") { 1206 return frame, nil 1207 } 1208 } 1209 return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found") 1210 } 1211 1212 func evalVariableOrError(p *proc.Target, symbol string) (*proc.Variable, error) { 1213 var scope *proc.EvalScope 1214 var err error 1215 1216 if testBackend == "rr" { 1217 var frame proc.Stackframe 1218 frame, err = findFirstNonRuntimeFrame(p) 1219 if err == nil { 1220 scope = proc.FrameToScope(p, p.Memory(), nil, 0, frame) 1221 } 1222 } else { 1223 scope, err = proc.GoroutineScope(p, p.CurrentThread()) 1224 } 1225 1226 if err != nil { 1227 return nil, err 1228 } 1229 return scope.EvalExpression(symbol, normalLoadConfig) 1230 } 1231 1232 func evalVariable(p *proc.Target, t testing.TB, symbol string) *proc.Variable { 1233 v, err := evalVariableOrError(p, symbol) 1234 if err != nil { 1235 _, file, line, _ := runtime.Caller(1) 1236 fname := filepath.Base(file) 1237 t.Fatalf("%s:%d: EvalVariable(%q): %v", fname, line, symbol, err) 1238 } 1239 return v 1240 } 1241 1242 func setVariable(p *proc.Target, symbol, value string) error { 1243 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 1244 if err != nil { 1245 return err 1246 } 1247 return scope.SetVariable(symbol, value) 1248 } 1249 1250 func TestVariableEvaluation(t *testing.T) { 1251 protest.AllowRecording(t) 1252 testcases := []struct { 1253 name string 1254 st reflect.Kind 1255 value interface{} 1256 length, cap int64 1257 childrenlen int 1258 }{ 1259 {"a1", reflect.String, "foofoofoofoofoofoo", 18, 0, 0}, 1260 {"a11", reflect.Array, nil, 3, -1, 3}, 1261 {"a12", reflect.Slice, nil, 2, 2, 2}, 1262 {"a13", reflect.Slice, nil, 3, 3, 3}, 1263 {"a2", reflect.Int, int64(6), 0, 0, 0}, 1264 {"a3", reflect.Float64, float64(7.23), 0, 0, 0}, 1265 {"a4", reflect.Array, nil, 2, -1, 2}, 1266 {"a5", reflect.Slice, nil, 5, 5, 5}, 1267 {"a6", reflect.Struct, nil, 2, 0, 2}, 1268 {"a7", reflect.Ptr, nil, 1, 0, 1}, 1269 {"a8", reflect.Struct, nil, 2, 0, 2}, 1270 {"a9", reflect.Ptr, nil, 1, 0, 1}, 1271 {"baz", reflect.String, "bazburzum", 9, 0, 0}, 1272 {"neg", reflect.Int, int64(-1), 0, 0, 0}, 1273 {"f32", reflect.Float32, float64(float32(1.2)), 0, 0, 0}, 1274 {"c64", reflect.Complex64, complex128(complex64(1 + 2i)), 0, 0, 0}, 1275 {"c128", reflect.Complex128, complex128(2 + 3i), 0, 0, 0}, 1276 {"a6.Baz", reflect.Int, int64(8), 0, 0, 0}, 1277 {"a7.Baz", reflect.Int, int64(5), 0, 0, 0}, 1278 {"a8.Baz", reflect.String, "feh", 3, 0, 0}, 1279 {"a8", reflect.Struct, nil, 2, 0, 2}, 1280 {"i32", reflect.Array, nil, 2, -1, 2}, 1281 {"b1", reflect.Bool, true, 0, 0, 0}, 1282 {"b2", reflect.Bool, false, 0, 0, 0}, 1283 {"f", reflect.Func, "main.barfoo", 0, 0, 0}, 1284 {"ba", reflect.Slice, nil, 200, 200, 64}, 1285 } 1286 1287 withTestProcess("testvariables", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1288 assertNoError(grp.Continue(), t, "Continue() returned an error") 1289 1290 for _, tc := range testcases { 1291 v := evalVariable(p, t, tc.name) 1292 1293 if v.Kind != tc.st { 1294 t.Fatalf("%s simple type: expected: %s got: %s", tc.name, tc.st, v.Kind.String()) 1295 } 1296 if v.Value == nil && tc.value != nil { 1297 t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) 1298 } else { 1299 switch v.Kind { 1300 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1301 x, _ := constant.Int64Val(v.Value) 1302 if y, ok := tc.value.(int64); !ok || x != y { 1303 t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) 1304 } 1305 case reflect.Float32, reflect.Float64: 1306 x, _ := constant.Float64Val(v.Value) 1307 if y, ok := tc.value.(float64); !ok || x != y { 1308 t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) 1309 } 1310 case reflect.Complex64, reflect.Complex128: 1311 xr, _ := constant.Float64Val(constant.Real(v.Value)) 1312 xi, _ := constant.Float64Val(constant.Imag(v.Value)) 1313 if y, ok := tc.value.(complex128); !ok || complex(xr, xi) != y { 1314 t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) 1315 } 1316 case reflect.String: 1317 if y, ok := tc.value.(string); !ok || constant.StringVal(v.Value) != y { 1318 t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) 1319 } 1320 } 1321 } 1322 if v.Len != tc.length { 1323 t.Fatalf("%s len: expected: %d got: %d", tc.name, tc.length, v.Len) 1324 } 1325 if v.Cap != tc.cap { 1326 t.Fatalf("%s cap: expected: %d got: %d", tc.name, tc.cap, v.Cap) 1327 } 1328 if len(v.Children) != tc.childrenlen { 1329 t.Fatalf("%s children len: expected %d got: %d", tc.name, tc.childrenlen, len(v.Children)) 1330 } 1331 } 1332 }) 1333 } 1334 1335 func TestFrameEvaluation(t *testing.T) { 1336 protest.AllowRecording(t) 1337 lenient := false 1338 if runtime.GOOS == "windows" { 1339 lenient = true 1340 } 1341 withTestProcess("goroutinestackprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1342 setFunctionBreakpoint(p, t, "main.stacktraceme") 1343 assertNoError(grp.Continue(), t, "Continue()") 1344 1345 t.Logf("stopped on thread %d, goroutine: %#v", p.CurrentThread().ThreadID(), p.SelectedGoroutine()) 1346 1347 // Testing evaluation on goroutines 1348 gs, _, err := proc.GoroutinesInfo(p, 0, 0) 1349 assertNoError(err, t, "GoroutinesInfo") 1350 found := make([]bool, 10) 1351 for _, g := range gs { 1352 frame := -1 1353 frames, err := proc.GoroutineStacktrace(p, g, 40, 0) 1354 if err != nil { 1355 t.Logf("could not stacktrace goroutine %d: %v\n", g.ID, err) 1356 continue 1357 } 1358 t.Logf("Goroutine %d %#v", g.ID, g.Thread) 1359 logStacktrace(t, p, frames) 1360 for i := range frames { 1361 if frames[i].Call.Fn != nil && frames[i].Call.Fn.Name == "main.agoroutine" { 1362 frame = i 1363 break 1364 } 1365 } 1366 1367 if frame < 0 { 1368 t.Logf("Goroutine %d: could not find correct frame", g.ID) 1369 continue 1370 } 1371 1372 scope, err := proc.ConvertEvalScope(p, g.ID, frame, 0) 1373 assertNoError(err, t, "ConvertEvalScope()") 1374 t.Logf("scope = %v", scope) 1375 v, err := scope.EvalExpression("i", normalLoadConfig) 1376 t.Logf("v = %v", v) 1377 if err != nil { 1378 t.Logf("Goroutine %d: %v\n", g.ID, err) 1379 continue 1380 } 1381 vval, _ := constant.Int64Val(v.Value) 1382 found[vval] = true 1383 } 1384 1385 for i := range found { 1386 if !found[i] { 1387 if lenient { 1388 lenient = false 1389 } else { 1390 t.Fatalf("Goroutine %d not found\n", i) 1391 } 1392 } 1393 } 1394 1395 // Testing evaluation on frames 1396 assertNoError(grp.Continue(), t, "Continue() 2") 1397 g, err := proc.GetG(p.CurrentThread()) 1398 assertNoError(err, t, "GetG()") 1399 1400 frames, err := proc.GoroutineStacktrace(p, g, 40, 0) 1401 assertNoError(err, t, "Stacktrace()") 1402 t.Logf("Goroutine %d %#v", g.ID, g.Thread) 1403 logStacktrace(t, p, frames) 1404 1405 for i := 0; i <= 3; i++ { 1406 scope, err := proc.ConvertEvalScope(p, g.ID, i+1, 0) 1407 assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1)) 1408 v, err := scope.EvalExpression("n", normalLoadConfig) 1409 assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1)) 1410 n, _ := constant.Int64Val(v.Value) 1411 t.Logf("frame %d n %d\n", i+1, n) 1412 if n != int64(3-i) { 1413 t.Fatalf("On frame %d value of n is %d (not %d)", i+1, n, 3-i) 1414 } 1415 } 1416 }) 1417 } 1418 1419 func TestThreadFrameEvaluation(t *testing.T) { 1420 skipOn(t, "upstream issue - https://github.com/golang/go/issues/29322", "pie") 1421 deadlockBp := proc.FatalThrow 1422 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) { 1423 t.SkipNow() 1424 } 1425 withTestProcess("testdeadlock", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1426 assertNoError(grp.Continue(), t, "Continue()") 1427 1428 bp := p.CurrentThread().Breakpoint() 1429 if bp.Breakpoint == nil || bp.Logical.Name != deadlockBp { 1430 t.Fatalf("did not stop at deadlock breakpoint %v", bp) 1431 } 1432 1433 // There is no selected goroutine during a deadlock, so the scope will 1434 // be a thread scope. 1435 scope, err := proc.ConvertEvalScope(p, 0, 0, 0) 1436 assertNoError(err, t, "ConvertEvalScope() on frame 0") 1437 _, err = scope.EvalExpression("s", normalLoadConfig) 1438 assertNoError(err, t, "EvalVariable(\"s\") on frame 0") 1439 }) 1440 } 1441 1442 func TestPointerSetting(t *testing.T) { 1443 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1444 assertNoError(grp.Continue(), t, "Continue() returned an error") 1445 1446 pval := func(n int64) { 1447 variable := evalVariable(p, t, "p1") 1448 c0val, _ := constant.Int64Val(variable.Children[0].Value) 1449 if c0val != n { 1450 t.Fatalf("Wrong value of p1, *%d expected *%d", c0val, n) 1451 } 1452 } 1453 1454 pval(1) 1455 1456 // change p1 to point to i2 1457 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 1458 assertNoError(err, t, "Scope()") 1459 i2addr, err := scope.EvalExpression("i2", normalLoadConfig) 1460 assertNoError(err, t, "EvalExpression()") 1461 assertNoError(setVariable(p, "p1", fmt.Sprintf("(*int)(0x%x)", i2addr.Addr)), t, "SetVariable()") 1462 pval(2) 1463 1464 // change the value of i2 check that p1 also changes 1465 assertNoError(setVariable(p, "i2", "5"), t, "SetVariable()") 1466 pval(5) 1467 }) 1468 } 1469 1470 func TestVariableFunctionScoping(t *testing.T) { 1471 withTestProcess("testvariables", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1472 err := grp.Continue() 1473 assertNoError(err, t, "Continue() returned an error") 1474 1475 evalVariable(p, t, "a1") 1476 evalVariable(p, t, "a2") 1477 1478 // Move scopes, a1 exists here by a2 does not 1479 err = grp.Continue() 1480 assertNoError(err, t, "Continue() returned an error") 1481 1482 evalVariable(p, t, "a1") 1483 1484 _, err = evalVariableOrError(p, "a2") 1485 if err == nil { 1486 t.Fatalf("Can eval out of scope variable a2") 1487 } 1488 }) 1489 } 1490 1491 func TestRecursiveStructure(t *testing.T) { 1492 protest.AllowRecording(t) 1493 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1494 assertNoError(grp.Continue(), t, "Continue()") 1495 v := evalVariable(p, t, "aas") 1496 t.Logf("v: %v\n", v) 1497 }) 1498 } 1499 1500 func TestIssue316(t *testing.T) { 1501 // A pointer loop that includes one interface should not send dlv into an infinite loop 1502 protest.AllowRecording(t) 1503 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1504 assertNoError(grp.Continue(), t, "Continue()") 1505 evalVariable(p, t, "iface5") 1506 }) 1507 } 1508 1509 func TestIssue325(t *testing.T) { 1510 // nil pointer dereference when evaluating interfaces to function pointers 1511 protest.AllowRecording(t) 1512 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1513 assertNoError(grp.Continue(), t, "Continue()") 1514 iface2fn1v := evalVariable(p, t, "iface2fn1") 1515 t.Logf("iface2fn1: %v\n", iface2fn1v) 1516 1517 iface2fn2v := evalVariable(p, t, "iface2fn2") 1518 t.Logf("iface2fn2: %v\n", iface2fn2v) 1519 }) 1520 } 1521 1522 func TestBreakpointCounts(t *testing.T) { 1523 protest.AllowRecording(t) 1524 withTestProcess("bpcountstest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1525 bp := setFileBreakpoint(p, t, fixture.Source, 12) 1526 1527 for { 1528 if err := grp.Continue(); err != nil { 1529 if _, exited := err.(proc.ErrProcessExited); exited { 1530 break 1531 } 1532 assertNoError(err, t, "Continue()") 1533 } 1534 } 1535 1536 t.Logf("TotalHitCount: %d", bp.Logical.TotalHitCount) 1537 if bp.Logical.TotalHitCount != 200 { 1538 t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.Logical.TotalHitCount) 1539 } 1540 1541 if len(bp.Logical.HitCount) != 2 { 1542 t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.Logical.HitCount)) 1543 } 1544 1545 for _, v := range bp.Logical.HitCount { 1546 if v != 100 { 1547 t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.Logical.HitCount) 1548 } 1549 } 1550 }) 1551 } 1552 1553 func TestHardcodedBreakpointCounts(t *testing.T) { 1554 skipOn(t, "broken", "windows", "arm64") 1555 withTestProcess("hcbpcountstest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1556 counts := map[int64]int{} 1557 for { 1558 if err := grp.Continue(); err != nil { 1559 if _, exited := err.(proc.ErrProcessExited); exited { 1560 break 1561 } 1562 assertNoError(err, t, "Continue()") 1563 } 1564 1565 for _, th := range p.ThreadList() { 1566 bp := th.Breakpoint().Breakpoint 1567 if bp == nil { 1568 continue 1569 } 1570 if bp.Logical.Name != proc.HardcodedBreakpoint { 1571 t.Fatalf("wrong breakpoint name %s", bp.Logical.Name) 1572 } 1573 g, err := proc.GetG(th) 1574 assertNoError(err, t, "GetG") 1575 counts[g.ID]++ 1576 } 1577 } 1578 1579 if len(counts) != 2 { 1580 t.Fatalf("Wrong number of goroutines for hardcoded breakpoint (%d)", len(counts)) 1581 } 1582 1583 for goid, count := range counts { 1584 if count != 100 { 1585 t.Fatalf("Wrong hit count for hardcoded breakpoint (%d) on goroutine %d", count, goid) 1586 } 1587 } 1588 }) 1589 } 1590 1591 func BenchmarkArray(b *testing.B) { 1592 // each bencharr struct is 128 bytes, bencharr is 64 elements long 1593 b.SetBytes(int64(64 * 128)) 1594 withTestProcess("testvariables2", b, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1595 assertNoError(grp.Continue(), b, "Continue()") 1596 b.ResetTimer() 1597 for i := 0; i < b.N; i++ { 1598 evalVariable(p, b, "bencharr") 1599 } 1600 }) 1601 } 1602 1603 const doTestBreakpointCountsWithDetection = false 1604 1605 func TestBreakpointCountsWithDetection(t *testing.T) { 1606 if !doTestBreakpointCountsWithDetection { 1607 return 1608 } 1609 m := map[int64]int64{} 1610 protest.AllowRecording(t) 1611 withTestProcess("bpcountstest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1612 bp := setFileBreakpoint(p, t, fixture.Source, 12) 1613 1614 for { 1615 if err := grp.Continue(); err != nil { 1616 if _, exited := err.(proc.ErrProcessExited); exited { 1617 break 1618 } 1619 assertNoError(err, t, "Continue()") 1620 } 1621 for _, th := range p.ThreadList() { 1622 if bp := th.Breakpoint(); bp.Breakpoint == nil { 1623 continue 1624 } 1625 scope, err := proc.GoroutineScope(p, th) 1626 assertNoError(err, t, "Scope()") 1627 v, err := scope.EvalExpression("i", normalLoadConfig) 1628 assertNoError(err, t, "evalVariable") 1629 i, _ := constant.Int64Val(v.Value) 1630 v, err = scope.EvalExpression("id", normalLoadConfig) 1631 assertNoError(err, t, "evalVariable") 1632 id, _ := constant.Int64Val(v.Value) 1633 m[id] = i 1634 } 1635 1636 total := int64(0) 1637 for i := range m { 1638 total += m[i] + 1 1639 } 1640 1641 if uint64(total) != bp.Logical.TotalHitCount { 1642 t.Fatalf("Mismatched total count %d %d\n", total, bp.Logical.TotalHitCount) 1643 } 1644 } 1645 1646 t.Logf("TotalHitCount: %d", bp.Logical.TotalHitCount) 1647 if bp.Logical.TotalHitCount != 200 { 1648 t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.Logical.TotalHitCount) 1649 } 1650 1651 if len(bp.Logical.HitCount) != 2 { 1652 t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.Logical.HitCount)) 1653 } 1654 1655 for _, v := range bp.Logical.HitCount { 1656 if v != 100 { 1657 t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.Logical.HitCount) 1658 } 1659 } 1660 }) 1661 } 1662 1663 func BenchmarkArrayPointer(b *testing.B) { 1664 // each bencharr struct is 128 bytes, benchparr is an array of 64 pointers to bencharr 1665 // each read will read 64 bencharr structs plus the 64 pointers of benchparr 1666 b.SetBytes(int64(64*128 + 64*8)) 1667 withTestProcess("testvariables2", b, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1668 assertNoError(grp.Continue(), b, "Continue()") 1669 b.ResetTimer() 1670 for i := 0; i < b.N; i++ { 1671 evalVariable(p, b, "bencharr") 1672 } 1673 }) 1674 } 1675 1676 func BenchmarkMap(b *testing.B) { 1677 // m1 contains 41 entries, each one has a value that's 2 int values (2* 8 bytes) and a string key 1678 // each string key has an average of 9 character 1679 // reading strings and the map structure imposes a overhead that we ignore here 1680 b.SetBytes(int64(41 * (2*8 + 9))) 1681 withTestProcess("testvariables2", b, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1682 assertNoError(grp.Continue(), b, "Continue()") 1683 b.ResetTimer() 1684 for i := 0; i < b.N; i++ { 1685 evalVariable(p, b, "m1") 1686 } 1687 }) 1688 } 1689 1690 func BenchmarkGoroutinesInfo(b *testing.B) { 1691 withTestProcess("testvariables2", b, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1692 assertNoError(grp.Continue(), b, "Continue()") 1693 b.ResetTimer() 1694 for i := 0; i < b.N; i++ { 1695 p.ClearCaches() 1696 _, _, err := proc.GoroutinesInfo(p, 0, 0) 1697 assertNoError(err, b, "GoroutinesInfo") 1698 } 1699 }) 1700 } 1701 1702 func TestIssue262(t *testing.T) { 1703 // Continue does not work when the current breakpoint is set on a NOP instruction 1704 protest.AllowRecording(t) 1705 withTestProcess("issue262", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1706 setFileBreakpoint(p, t, fixture.Source, 11) 1707 1708 assertNoError(grp.Continue(), t, "Continue()") 1709 err := grp.Continue() 1710 if err == nil { 1711 t.Fatalf("No error on second continue") 1712 } 1713 _, exited := err.(proc.ErrProcessExited) 1714 if !exited { 1715 t.Fatalf("Process did not exit after second continue: %v", err) 1716 } 1717 }) 1718 } 1719 1720 func TestIssue305(t *testing.T) { 1721 // If 'next' hits a breakpoint on the goroutine it's stepping through 1722 // the internal breakpoints aren't cleared preventing further use of 1723 // 'next' command 1724 protest.AllowRecording(t) 1725 withTestProcess("issue305", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1726 setFileBreakpoint(p, t, fixture.Source, 5) 1727 1728 assertNoError(grp.Continue(), t, "Continue()") 1729 1730 assertNoError(grp.Next(), t, "Next() 1") 1731 assertNoError(grp.Next(), t, "Next() 2") 1732 assertNoError(grp.Next(), t, "Next() 3") 1733 assertNoError(grp.Next(), t, "Next() 4") 1734 assertNoError(grp.Next(), t, "Next() 5") 1735 }) 1736 } 1737 1738 func TestPointerLoops(t *testing.T) { 1739 // Pointer loops through map entries, pointers and slices 1740 // Regression test for issue #341 1741 protest.AllowRecording(t) 1742 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1743 assertNoError(grp.Continue(), t, "Continue()") 1744 for _, expr := range []string{"mapinf", "ptrinf", "sliceinf"} { 1745 t.Logf("requesting %s", expr) 1746 v := evalVariable(p, t, expr) 1747 t.Logf("%s: %v\n", expr, v) 1748 } 1749 }) 1750 } 1751 1752 func BenchmarkLocalVariables(b *testing.B) { 1753 withTestProcess("testvariables", b, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1754 assertNoError(grp.Continue(), b, "Continue() returned an error") 1755 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 1756 assertNoError(err, b, "Scope()") 1757 b.ResetTimer() 1758 for i := 0; i < b.N; i++ { 1759 _, err := scope.LocalVariables(normalLoadConfig) 1760 assertNoError(err, b, "LocalVariables()") 1761 } 1762 }) 1763 } 1764 1765 func TestCondBreakpoint(t *testing.T) { 1766 protest.AllowRecording(t) 1767 withTestProcess("parallel_next", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1768 bp := setFileBreakpoint(p, t, fixture.Source, 9) 1769 bp.UserBreaklet().Cond = &ast.BinaryExpr{ 1770 Op: token.EQL, 1771 X: &ast.Ident{Name: "n"}, 1772 Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, 1773 } 1774 1775 assertNoError(grp.Continue(), t, "Continue()") 1776 1777 nvar := evalVariable(p, t, "n") 1778 1779 n, _ := constant.Int64Val(nvar.Value) 1780 if n != 7 { 1781 t.Fatalf("Stopped on wrong goroutine %d\n", n) 1782 } 1783 }) 1784 } 1785 1786 func TestCondBreakpointWithFrame(t *testing.T) { 1787 protest.AllowRecording(t) 1788 withTestProcess("condframe", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1789 bp := setFileBreakpoint(p, t, fixture.Source, 12) 1790 parsed, err := parser.ParseExpr("runtime.frame(1).i == 3") 1791 if err != nil { 1792 t.Fatalf("failed to parse expression: %v", err) 1793 } 1794 bp.UserBreaklet().Cond = parsed 1795 1796 assertNoError(grp.Continue(), t, "Continue()") 1797 1798 g := p.SelectedGoroutine() 1799 scope, err := proc.ConvertEvalScope(p, g.ID, 1, 0) 1800 if err != nil { 1801 t.Fatal(err) 1802 } 1803 1804 v, err := scope.EvalExpression("i", normalLoadConfig) 1805 if err != nil { 1806 t.Fatal(err) 1807 } 1808 1809 vval, _ := constant.Int64Val(v.Value) 1810 if vval != 3 { 1811 t.Fatalf("Incorrect value for frame variable: %v", vval) 1812 } 1813 }) 1814 } 1815 1816 func TestCondBreakpointError(t *testing.T) { 1817 protest.AllowRecording(t) 1818 withTestProcess("parallel_next", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1819 bp := setFileBreakpoint(p, t, fixture.Source, 9) 1820 bp.UserBreaklet().Cond = &ast.BinaryExpr{ 1821 Op: token.EQL, 1822 X: &ast.Ident{Name: "nonexistentvariable"}, 1823 Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, 1824 } 1825 1826 err := grp.Continue() 1827 if err == nil { 1828 t.Fatalf("No error on first Continue()") 1829 } 1830 1831 if err.Error() != "error evaluating expression: could not find symbol value for nonexistentvariable" && err.Error() != "multiple errors evaluating conditions" { 1832 t.Fatalf("Unexpected error on first Continue(): %v", err) 1833 } 1834 1835 bp.UserBreaklet().Cond = &ast.BinaryExpr{ 1836 Op: token.EQL, 1837 X: &ast.Ident{Name: "n"}, 1838 Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, 1839 } 1840 1841 err = grp.Continue() 1842 if err != nil { 1843 if _, exited := err.(proc.ErrProcessExited); !exited { 1844 t.Fatalf("Unexpected error on second Continue(): %v", err) 1845 } 1846 } else { 1847 nvar := evalVariable(p, t, "n") 1848 1849 n, _ := constant.Int64Val(nvar.Value) 1850 if n != 7 { 1851 t.Fatalf("Stopped on wrong goroutine %d\n", n) 1852 } 1853 } 1854 }) 1855 } 1856 1857 func TestHitCondBreakpointEQ(t *testing.T) { 1858 withTestProcess("break", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1859 bp := setFileBreakpoint(p, t, fixture.Source, 7) 1860 bp.Logical.HitCond = &struct { 1861 Op token.Token 1862 Val int 1863 }{token.EQL, 3} 1864 1865 assertNoError(grp.Continue(), t, "Continue()") 1866 ivar := evalVariable(p, t, "i") 1867 1868 i, _ := constant.Int64Val(ivar.Value) 1869 if i != 3 { 1870 t.Fatalf("Stopped on wrong hitcount %d\n", i) 1871 } 1872 1873 err := grp.Continue() 1874 if _, exited := err.(proc.ErrProcessExited); !exited { 1875 t.Fatalf("Unexpected error on Continue(): %v", err) 1876 } 1877 }) 1878 } 1879 1880 func TestHitCondBreakpointGEQ(t *testing.T) { 1881 protest.AllowRecording(t) 1882 withTestProcess("break", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1883 bp := setFileBreakpoint(p, t, fixture.Source, 7) 1884 bp.Logical.HitCond = &struct { 1885 Op token.Token 1886 Val int 1887 }{token.GEQ, 3} 1888 1889 for it := 3; it <= 10; it++ { 1890 assertNoError(grp.Continue(), t, "Continue()") 1891 ivar := evalVariable(p, t, "i") 1892 1893 i, _ := constant.Int64Val(ivar.Value) 1894 if int(i) != it { 1895 t.Fatalf("Stopped on wrong hitcount %d\n", i) 1896 } 1897 } 1898 1899 assertNoError(grp.Continue(), t, "Continue()") 1900 }) 1901 } 1902 1903 func TestHitCondBreakpointREM(t *testing.T) { 1904 protest.AllowRecording(t) 1905 withTestProcess("break", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1906 bp := setFileBreakpoint(p, t, fixture.Source, 7) 1907 bp.Logical.HitCond = &struct { 1908 Op token.Token 1909 Val int 1910 }{token.REM, 2} 1911 1912 for it := 2; it <= 10; it += 2 { 1913 assertNoError(grp.Continue(), t, "Continue()") 1914 ivar := evalVariable(p, t, "i") 1915 1916 i, _ := constant.Int64Val(ivar.Value) 1917 if int(i) != it { 1918 t.Fatalf("Stopped on wrong hitcount %d\n", i) 1919 } 1920 } 1921 1922 err := grp.Continue() 1923 if _, exited := err.(proc.ErrProcessExited); !exited { 1924 t.Fatalf("Unexpected error on Continue(): %v", err) 1925 } 1926 }) 1927 } 1928 1929 func TestIssue356(t *testing.T) { 1930 // slice with a typedef does not get printed correctly 1931 protest.AllowRecording(t) 1932 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1933 assertNoError(grp.Continue(), t, "Continue() returned an error") 1934 mmvar := evalVariable(p, t, "mainMenu") 1935 if mmvar.Kind != reflect.Slice { 1936 t.Fatalf("Wrong kind for mainMenu: %v\n", mmvar.Kind) 1937 } 1938 }) 1939 } 1940 1941 func TestStepIntoFunction(t *testing.T) { 1942 withTestProcess("teststep", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1943 // Continue until breakpoint 1944 assertNoError(grp.Continue(), t, "Continue() returned an error") 1945 // Step into function 1946 assertNoError(grp.Step(), t, "Step() returned an error") 1947 // We should now be inside the function. 1948 loc, err := p.CurrentThread().Location() 1949 if err != nil { 1950 t.Fatal(err) 1951 } 1952 if loc.Fn.Name != "main.callme" { 1953 t.Fatalf("expected to be within the 'callme' function, was in %s instead", loc.Fn.Name) 1954 } 1955 if !strings.Contains(loc.File, "teststep") { 1956 t.Fatalf("debugger stopped at incorrect location: %s:%d", loc.File, loc.Line) 1957 } 1958 if loc.Line != 8 { 1959 t.Fatalf("debugger stopped at incorrect line: %d", loc.Line) 1960 } 1961 }) 1962 } 1963 1964 func TestIssue332_Part1(t *testing.T) { 1965 // Next shouldn't step inside a function call 1966 protest.AllowRecording(t) 1967 withTestProcess("issue332", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1968 setFileBreakpoint(p, t, fixture.Source, 8) 1969 assertNoError(grp.Continue(), t, "Continue()") 1970 assertNoError(grp.Next(), t, "first Next()") 1971 locations, err := proc.ThreadStacktrace(p, p.CurrentThread(), 2) 1972 assertNoError(err, t, "Stacktrace()") 1973 if locations[0].Call.Fn == nil { 1974 t.Fatalf("Not on a function") 1975 } 1976 if locations[0].Call.Fn.Name != "main.main" { 1977 t.Fatalf("Not on main.main after Next: %s (%s:%d)", locations[0].Call.Fn.Name, locations[0].Call.File, locations[0].Call.Line) 1978 } 1979 if locations[0].Call.Line != 9 { 1980 t.Fatalf("Not on line 9 after Next: %s (%s:%d)", locations[0].Call.Fn.Name, locations[0].Call.File, locations[0].Call.Line) 1981 } 1982 }) 1983 } 1984 1985 func TestIssue332_Part2(t *testing.T) { 1986 // Step should skip a function's prologue 1987 // In some parts of the prologue, for some functions, the FDE data is incorrect 1988 // which leads to 'next' and 'stack' failing with error "could not find FDE for PC: <garbage>" 1989 // because the incorrect FDE data leads to reading the wrong stack address as the return address 1990 protest.AllowRecording(t) 1991 withTestProcess("issue332", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 1992 setFileBreakpoint(p, t, fixture.Source, 8) 1993 assertNoError(grp.Continue(), t, "Continue()") 1994 1995 // step until we enter changeMe 1996 for { 1997 assertNoError(grp.Step(), t, "Step()") 1998 locations, err := proc.ThreadStacktrace(p, p.CurrentThread(), 2) 1999 assertNoError(err, t, "Stacktrace()") 2000 if locations[0].Call.Fn == nil { 2001 t.Fatalf("Not on a function") 2002 } 2003 if locations[0].Call.Fn.Name == "main.changeMe" { 2004 break 2005 } 2006 } 2007 2008 regs, err := p.CurrentThread().Registers() 2009 assertNoError(err, t, "Registers()") 2010 pc := regs.PC() 2011 pcAfterPrologue := findFunctionLocation(p, t, "main.changeMe") 2012 if pcAfterPrologue == p.BinInfo().LookupFunc()["main.changeMe"][0].Entry { 2013 t.Fatalf("main.changeMe and main.changeMe:0 are the same (%x)", pcAfterPrologue) 2014 } 2015 if pc != pcAfterPrologue { 2016 t.Fatalf("Step did not skip the prologue: current pc: %x, first instruction after prologue: %x", pc, pcAfterPrologue) 2017 } 2018 2019 assertNoError(grp.Next(), t, "first Next()") 2020 assertNoError(grp.Next(), t, "second Next()") 2021 assertNoError(grp.Next(), t, "third Next()") 2022 err = grp.Continue() 2023 if _, exited := err.(proc.ErrProcessExited); !exited { 2024 assertNoError(err, t, "final Continue()") 2025 } 2026 }) 2027 } 2028 2029 func TestIssue414(t *testing.T) { 2030 skipOn(t, "broken", "linux", "386", "pie") // test occasionally hangs on linux/386/pie 2031 // Stepping until the program exits 2032 protest.AllowRecording(t) 2033 withTestProcess("math", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2034 setFileBreakpoint(p, t, fixture.Source, 9) 2035 assertNoError(grp.Continue(), t, "Continue()") 2036 for { 2037 pc := currentPC(p, t) 2038 f, ln := currentLineNumber(p, t) 2039 t.Logf("at %s:%d %#x\n", f, ln, pc) 2040 var err error 2041 // Stepping through the runtime is not generally safe so after we are out 2042 // of main.main just use Next. 2043 // See: https://gitlab.com/Raven-IO/raven-delve/pull/2082 2044 if f == fixture.Source { 2045 err = grp.Step() 2046 } else { 2047 err = grp.Next() 2048 } 2049 if err != nil { 2050 if _, exited := err.(proc.ErrProcessExited); exited { 2051 break 2052 } 2053 } 2054 assertNoError(err, t, "Step()") 2055 } 2056 }) 2057 } 2058 2059 func TestPackageVariables(t *testing.T) { 2060 var skippedVariable = map[string]bool{ 2061 "runtime.uint16Eface": true, 2062 "runtime.uint32Eface": true, 2063 "runtime.uint64Eface": true, 2064 "runtime.stringEface": true, 2065 "runtime.sliceEface": true, 2066 } 2067 2068 protest.AllowRecording(t) 2069 withTestProcess("testvariables", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2070 err := grp.Continue() 2071 assertNoError(err, t, "Continue()") 2072 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 2073 assertNoError(err, t, "Scope()") 2074 vars, err := scope.PackageVariables(normalLoadConfig) 2075 assertNoError(err, t, "PackageVariables()") 2076 failed := false 2077 for _, v := range vars { 2078 if skippedVariable[v.Name] { 2079 continue 2080 } 2081 if v.Unreadable != nil && v.Unreadable.Error() != "no location attribute Location" { 2082 failed = true 2083 t.Logf("Unreadable variable %s: %v", v.Name, v.Unreadable) 2084 } 2085 } 2086 if failed { 2087 t.Fatalf("previous errors") 2088 } 2089 }) 2090 } 2091 2092 func TestIssue149(t *testing.T) { 2093 ver, _ := goversion.Parse(runtime.Version()) 2094 if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) { 2095 return 2096 } 2097 // setting breakpoint on break statement 2098 withTestProcess("break", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2099 findFileLocation(p, t, fixture.Source, 8) 2100 }) 2101 } 2102 2103 func TestPanicBreakpoint(t *testing.T) { 2104 protest.AllowRecording(t) 2105 withTestProcess("panic", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2106 assertNoError(grp.Continue(), t, "Continue()") 2107 bp := p.CurrentThread().Breakpoint() 2108 if bp.Breakpoint == nil || bp.Logical.Name != proc.UnrecoveredPanic { 2109 t.Fatalf("not on unrecovered-panic breakpoint: %v", bp) 2110 } 2111 }) 2112 } 2113 2114 func TestCmdLineArgs(t *testing.T) { 2115 expectSuccess := func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2116 err := grp.Continue() 2117 bp := p.CurrentThread().Breakpoint() 2118 if bp.Breakpoint != nil && bp.Logical.Name == proc.UnrecoveredPanic { 2119 t.Fatalf("testing args failed on unrecovered-panic breakpoint: %v", bp) 2120 } 2121 exit, exited := err.(proc.ErrProcessExited) 2122 if !exited { 2123 t.Fatalf("Process did not exit: %v", err) 2124 } else { 2125 if exit.Status != 0 { 2126 t.Fatalf("process exited with invalid status %d", exit.Status) 2127 } 2128 } 2129 } 2130 2131 expectPanic := func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2132 grp.Continue() 2133 bp := p.CurrentThread().Breakpoint() 2134 if bp.Breakpoint == nil || bp.Logical.Name != proc.UnrecoveredPanic { 2135 t.Fatalf("not on unrecovered-panic breakpoint: %v", bp) 2136 } 2137 } 2138 2139 // make sure multiple arguments (including one with spaces) are passed to the binary correctly 2140 withTestProcessArgs("testargs", t, ".", []string{"test"}, 0, expectSuccess) 2141 withTestProcessArgs("testargs", t, ".", []string{"-test"}, 0, expectPanic) 2142 withTestProcessArgs("testargs", t, ".", []string{"test", "pass flag"}, 0, expectSuccess) 2143 // check that arguments with spaces are *only* passed correctly when correctly called 2144 withTestProcessArgs("testargs", t, ".", []string{"test pass", "flag"}, 0, expectPanic) 2145 withTestProcessArgs("testargs", t, ".", []string{"test", "pass", "flag"}, 0, expectPanic) 2146 withTestProcessArgs("testargs", t, ".", []string{"test pass flag"}, 0, expectPanic) 2147 // and that invalid cases (wrong arguments or no arguments) panic 2148 withTestProcess("testargs", t, expectPanic) 2149 withTestProcessArgs("testargs", t, ".", []string{"invalid"}, 0, expectPanic) 2150 withTestProcessArgs("testargs", t, ".", []string{"test", "invalid"}, 0, expectPanic) 2151 withTestProcessArgs("testargs", t, ".", []string{"invalid", "pass flag"}, 0, expectPanic) 2152 } 2153 2154 func TestIssue462(t *testing.T) { 2155 skipOn(t, "broken", "windows") // Stacktrace of Goroutine 0 fails with an error 2156 withTestProcess("testnextnethttp", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2157 go func() { 2158 // Wait for program to start listening. 2159 for { 2160 conn, err := net.Dial("tcp", "127.0.0.1:9191") 2161 if err == nil { 2162 conn.Close() 2163 break 2164 } 2165 time.Sleep(50 * time.Millisecond) 2166 } 2167 2168 grp.RequestManualStop() 2169 }() 2170 2171 assertNoError(grp.Continue(), t, "Continue()") 2172 _, err := proc.ThreadStacktrace(p, p.CurrentThread(), 40) 2173 assertNoError(err, t, "Stacktrace()") 2174 }) 2175 } 2176 2177 func TestNextParked(t *testing.T) { 2178 protest.AllowRecording(t) 2179 withTestProcess("parallel_next", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2180 bp := setFunctionBreakpoint(p, t, "main.sayhi") 2181 2182 // continue until a parked goroutine exists 2183 var parkedg *proc.G 2184 for parkedg == nil { 2185 err := grp.Continue() 2186 if _, exited := err.(proc.ErrProcessExited); exited { 2187 t.Log("could not find parked goroutine") 2188 return 2189 } 2190 assertNoError(err, t, "Continue()") 2191 2192 gs, _, err := proc.GoroutinesInfo(p, 0, 0) 2193 assertNoError(err, t, "GoroutinesInfo()") 2194 2195 // Search for a parked goroutine that we know for sure will have to be 2196 // resumed before the program can exit. This is a parked goroutine that: 2197 // 1. is executing main.sayhi 2198 // 2. hasn't called wg.Done yet 2199 for _, g := range gs { 2200 if g.Thread != nil { 2201 continue 2202 } 2203 frames, _ := proc.GoroutineStacktrace(p, g, 5, 0) 2204 for _, frame := range frames { 2205 // line 11 is the line where wg.Done is called 2206 if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.sayhi" && frame.Current.Line < 11 { 2207 parkedg = g 2208 break 2209 } 2210 } 2211 if parkedg != nil { 2212 break 2213 } 2214 } 2215 } 2216 2217 assertNoError(p.SwitchGoroutine(parkedg), t, "SwitchGoroutine()") 2218 p.ClearBreakpoint(bp.Addr) 2219 assertNoError(grp.Next(), t, "Next()") 2220 2221 if p.SelectedGoroutine().ID != parkedg.ID { 2222 t.Fatalf("Next did not continue on the selected goroutine, expected %d got %d", parkedg.ID, p.SelectedGoroutine().ID) 2223 } 2224 }) 2225 } 2226 2227 func TestStepParked(t *testing.T) { 2228 protest.AllowRecording(t) 2229 withTestProcess("parallel_next", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2230 bp := setFunctionBreakpoint(p, t, "main.sayhi") 2231 2232 // continue until a parked goroutine exists 2233 var parkedg *proc.G 2234 LookForParkedG: 2235 for { 2236 err := grp.Continue() 2237 if _, exited := err.(proc.ErrProcessExited); exited { 2238 t.Log("could not find parked goroutine") 2239 return 2240 } 2241 assertNoError(err, t, "Continue()") 2242 2243 gs, _, err := proc.GoroutinesInfo(p, 0, 0) 2244 assertNoError(err, t, "GoroutinesInfo()") 2245 2246 for _, g := range gs { 2247 if g.Thread == nil && g.CurrentLoc.Fn != nil && g.CurrentLoc.Fn.Name == "main.sayhi" { 2248 parkedg = g 2249 break LookForParkedG 2250 } 2251 } 2252 } 2253 2254 t.Logf("Parked g is: %v\n", parkedg) 2255 frames, _ := proc.GoroutineStacktrace(p, parkedg, 20, 0) 2256 for _, frame := range frames { 2257 name := "" 2258 if frame.Call.Fn != nil { 2259 name = frame.Call.Fn.Name 2260 } 2261 t.Logf("\t%s:%d in %s (%#x)", frame.Call.File, frame.Call.Line, name, frame.Current.PC) 2262 } 2263 2264 assertNoError(p.SwitchGoroutine(parkedg), t, "SwitchGoroutine()") 2265 p.ClearBreakpoint(bp.Addr) 2266 assertNoError(grp.Step(), t, "Step()") 2267 2268 if p.SelectedGoroutine().ID != parkedg.ID { 2269 t.Fatalf("Step did not continue on the selected goroutine, expected %d got %d", parkedg.ID, p.SelectedGoroutine().ID) 2270 } 2271 }) 2272 } 2273 2274 func TestUnsupportedArch(t *testing.T) { 2275 ver, _ := goversion.Parse(runtime.Version()) 2276 if ver.Major < 0 || !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 6, Rev: -1}) || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) { 2277 // cross compile (with -N?) works only on select versions of go 2278 return 2279 } 2280 2281 fixturesDir := protest.FindFixturesDir() 2282 infile := filepath.Join(fixturesDir, "math.go") 2283 outfile := filepath.Join(fixturesDir, "_math_debug_386") 2284 2285 cmd := exec.Command("go", "build", "-gcflags=-N -l", "-o", outfile, infile) 2286 for _, v := range os.Environ() { 2287 if !strings.HasPrefix(v, "GOARCH=") { 2288 cmd.Env = append(cmd.Env, v) 2289 } 2290 } 2291 cmd.Env = append(cmd.Env, "GOARCH=386") 2292 out, err := cmd.CombinedOutput() 2293 if err != nil { 2294 t.Fatalf("go build failed: %v: %v", err, string(out)) 2295 } 2296 defer os.Remove(outfile) 2297 2298 var p *proc.TargetGroup 2299 2300 switch testBackend { 2301 case "native": 2302 p, err = native.Launch([]string{outfile}, ".", 0, []string{}, "", "", proc.OutputRedirect{}, proc.OutputRedirect{}) 2303 case "lldb": 2304 p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", 0, []string{}, "", [3]string{}) 2305 default: 2306 t.Skip("test not valid for this backend") 2307 } 2308 2309 if err == nil { 2310 p.Detach(true) 2311 t.Fatal("Launch is expected to fail, but succeeded") 2312 } 2313 2314 if _, ok := err.(*proc.ErrUnsupportedArch); ok { 2315 // all good 2316 return 2317 } 2318 2319 t.Fatal(err) 2320 } 2321 2322 func TestIssue573(t *testing.T) { 2323 // calls to runtime.duffzero and runtime.duffcopy jump directly into the middle 2324 // of the function and the internal breakpoint set by StepInto may be missed. 2325 protest.AllowRecording(t) 2326 withTestProcess("issue573", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2327 setFunctionBreakpoint(p, t, "main.foo") 2328 assertNoError(grp.Continue(), t, "Continue()") 2329 assertNoError(grp.Step(), t, "Step() #1") 2330 assertNoError(grp.Step(), t, "Step() #2") // Bug exits here. 2331 assertNoError(grp.Step(), t, "Step() #3") // Third step ought to be possible; program ought not have exited. 2332 }) 2333 } 2334 2335 func TestTestvariables2Prologue(t *testing.T) { 2336 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2337 addrEntry := p.BinInfo().LookupFunc()["main.main"][0].Entry 2338 addrPrologue := findFunctionLocation(p, t, "main.main") 2339 if addrEntry == addrPrologue { 2340 t.Fatalf("Prologue detection failed on testvariables2.go/main.main") 2341 } 2342 }) 2343 } 2344 2345 func TestNextDeferReturnAndDirectCall(t *testing.T) { 2346 // Next should not step into a deferred function if it is called 2347 // directly, only if it is called through a panic or a deferreturn. 2348 // Here we test the case where the function is called by a deferreturn 2349 testseq("defercall", contNext, []nextTest{ 2350 {9, 10}, 2351 {10, 11}, 2352 {11, 12}, 2353 {12, 13}, 2354 {13, 28}}, "main.callAndDeferReturn", t) 2355 } 2356 2357 func TestNextPanicAndDirectCall(t *testing.T) { 2358 // Next should not step into a deferred function if it is called 2359 // directly, only if it is called through a panic or a deferreturn. 2360 // Here we test the case where the function is called by a panic 2361 testseq("defercall", contNext, []nextTest{ 2362 {15, 16}, 2363 {16, 17}, 2364 {17, 18}, 2365 {18, 6}}, "main.callAndPanic2", t) 2366 } 2367 2368 func TestStepCall(t *testing.T) { 2369 testseq("testnextprog", contStep, []nextTest{ 2370 {34, 13}, 2371 {13, 14}}, "", t) 2372 } 2373 2374 func TestStepCallPtr(t *testing.T) { 2375 // Tests that Step works correctly when calling functions with a 2376 // function pointer. 2377 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) && !protest.RegabiSupported() { 2378 testseq("teststepprog", contStep, []nextTest{ 2379 {9, 10}, 2380 {10, 6}, 2381 {6, 7}, 2382 {7, 11}}, "", t) 2383 } else { 2384 if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" && buildMode == "pie" { 2385 testseq("teststepprog", contStep, []nextTest{ 2386 {9, 10}, 2387 {10, 5}, 2388 {5, 6}, 2389 {6, 7}, 2390 {7, 10}, 2391 {10, 11}}, "", t) 2392 } else { 2393 testseq("teststepprog", contStep, []nextTest{ 2394 {9, 10}, 2395 {10, 5}, 2396 {5, 6}, 2397 {6, 7}, 2398 {7, 11}}, "", t) 2399 } 2400 } 2401 } 2402 2403 func TestStepReturnAndPanic(t *testing.T) { 2404 // Tests that Step works correctly when returning from functions 2405 // and when a deferred function is called when panic'ing. 2406 testseq("defercall", contStep, []nextTest{ 2407 {17, 6}, 2408 {6, 7}, 2409 {7, 18}, 2410 {18, 6}, 2411 {6, 7}}, "", t) 2412 } 2413 2414 func TestStepDeferReturn(t *testing.T) { 2415 // Tests that Step works correctly when a deferred function is 2416 // called during a return. 2417 testseq("defercall", contStep, []nextTest{ 2418 {11, 6}, 2419 {6, 7}, 2420 {7, 12}, 2421 {12, 13}, 2422 {13, 6}, 2423 {6, 7}, 2424 {7, 13}, 2425 {13, 28}}, "", t) 2426 } 2427 2428 func TestStepIgnorePrivateRuntime(t *testing.T) { 2429 // Tests that Step will ignore calls to private runtime functions 2430 // (such as runtime.convT2E in this case) 2431 switch { 2432 case goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) && protest.RegabiSupported(): 2433 testseq("teststepprog", contStep, []nextTest{ 2434 {21, 13}, 2435 {13, 14}, 2436 {14, 15}, 2437 {15, 17}, 2438 {17, 22}}, "", t) 2439 case goversion.VersionAfterOrEqual(runtime.Version(), 1, 17): 2440 testseq("teststepprog", contStep, []nextTest{ 2441 {21, 14}, 2442 {14, 15}, 2443 {15, 17}, 2444 {17, 22}}, "", t) 2445 case goversion.VersionAfterOrEqual(runtime.Version(), 1, 11): 2446 testseq("teststepprog", contStep, []nextTest{ 2447 {21, 14}, 2448 {14, 15}, 2449 {15, 22}}, "", t) 2450 default: 2451 panic("too old") 2452 } 2453 } 2454 2455 func TestIssue561(t *testing.T) { 2456 // Step fails to make progress when PC is at a CALL instruction 2457 // where a breakpoint is also set. 2458 protest.AllowRecording(t) 2459 withTestProcess("issue561", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2460 setFileBreakpoint(p, t, fixture.Source, 10) 2461 assertNoError(grp.Continue(), t, "Continue()") 2462 assertNoError(grp.Step(), t, "Step()") 2463 assertLineNumber(p, t, 5, "wrong line number after Step,") 2464 }) 2465 } 2466 2467 func TestGoroutineLabels(t *testing.T) { 2468 withTestProcess("goroutineLabels", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2469 assertNoError(grp.Continue(), t, "Continue()") 2470 g, err := proc.GetG(p.CurrentThread()) 2471 assertNoError(err, t, "GetG()") 2472 if len(g.Labels()) != 0 { 2473 t.Fatalf("No labels expected") 2474 } 2475 2476 assertNoError(grp.Continue(), t, "Continue()") 2477 g, err = proc.GetG(p.CurrentThread()) 2478 assertNoError(err, t, "GetG()") 2479 labels := g.Labels() 2480 if v := labels["k1"]; v != "v1" { 2481 t.Errorf("Unexpected label value k1=%v", v) 2482 } 2483 if v := labels["k2"]; v != "v2" { 2484 t.Errorf("Unexpected label value k2=%v", v) 2485 } 2486 }) 2487 } 2488 2489 func TestStepOut(t *testing.T) { 2490 testseq2(t, "testnextprog", "main.helloworld", []seqTest{{contContinue, 13}, {contStepout, 35}}) 2491 } 2492 2493 func TestStepConcurrentDirect(t *testing.T) { 2494 skipOn(t, "broken - step concurrent", "windows", "arm64") 2495 protest.AllowRecording(t) 2496 withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2497 bp := setFileBreakpoint(p, t, fixture.Source, 37) 2498 2499 assertNoError(grp.Continue(), t, "Continue()") 2500 err := p.ClearBreakpoint(bp.Addr) 2501 assertNoError(err, t, "ClearBreakpoint()") 2502 2503 for _, b := range p.Breakpoints().M { 2504 if b.Logical.Name == proc.UnrecoveredPanic { 2505 err := p.ClearBreakpoint(b.Addr) 2506 assertNoError(err, t, "ClearBreakpoint(unrecovered-panic)") 2507 break 2508 } 2509 } 2510 2511 gid := p.SelectedGoroutine().ID 2512 2513 seq := []int{37, 38, 13, 15, 16, 38} 2514 2515 i := 0 2516 count := 0 2517 for { 2518 anyerr := false 2519 if p.SelectedGoroutine().ID != gid { 2520 t.Errorf("Step switched to different goroutine %d %d\n", gid, p.SelectedGoroutine().ID) 2521 anyerr = true 2522 } 2523 f, ln := currentLineNumber(p, t) 2524 if ln != seq[i] { 2525 if i == 1 && ln == 40 { 2526 // loop exited 2527 break 2528 } 2529 frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 20) 2530 if err != nil { 2531 t.Errorf("Could not get stacktrace of goroutine %d\n", p.SelectedGoroutine().ID) 2532 } else { 2533 t.Logf("Goroutine %d (thread: %d):", p.SelectedGoroutine().ID, p.CurrentThread().ThreadID()) 2534 for _, frame := range frames { 2535 t.Logf("\t%s:%d (%#x)", frame.Call.File, frame.Call.Line, frame.Current.PC) 2536 } 2537 } 2538 t.Errorf("Program did not continue at expected location (%d) %s:%d [i %d count %d]", seq[i], f, ln, i, count) 2539 anyerr = true 2540 } 2541 if anyerr { 2542 t.FailNow() 2543 } 2544 i = (i + 1) % len(seq) 2545 if i == 0 { 2546 count++ 2547 } 2548 assertNoError(grp.Step(), t, "Step()") 2549 } 2550 2551 if count != 100 { 2552 t.Fatalf("Program did not loop expected number of times: %d", count) 2553 } 2554 }) 2555 } 2556 2557 func TestStepConcurrentPtr(t *testing.T) { 2558 protest.AllowRecording(t) 2559 withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2560 setFileBreakpoint(p, t, fixture.Source, 24) 2561 2562 for _, b := range p.Breakpoints().M { 2563 if b.Logical.Name == proc.UnrecoveredPanic { 2564 err := p.ClearBreakpoint(b.Addr) 2565 assertNoError(err, t, "ClearBreakpoint(unrecovered-panic)") 2566 break 2567 } 2568 } 2569 2570 kvals := map[int64]int64{} 2571 count := 0 2572 for { 2573 err := grp.Continue() 2574 _, exited := err.(proc.ErrProcessExited) 2575 if exited { 2576 break 2577 } 2578 assertNoError(err, t, "Continue()") 2579 2580 f, ln := currentLineNumber(p, t) 2581 if ln != 24 { 2582 for _, th := range p.ThreadList() { 2583 t.Logf("thread %d stopped on breakpoint %v", th.ThreadID(), th.Breakpoint()) 2584 } 2585 curbp := p.CurrentThread().Breakpoint() 2586 t.Fatalf("Program did not continue at expected location (24): %s:%d %#x [%v] (gid %d count %d)", f, ln, currentPC(p, t), curbp, p.SelectedGoroutine().ID, count) 2587 } 2588 2589 gid := p.SelectedGoroutine().ID 2590 2591 kvar := evalVariable(p, t, "k") 2592 k, _ := constant.Int64Val(kvar.Value) 2593 2594 if oldk, ok := kvals[gid]; ok { 2595 if oldk >= k { 2596 t.Fatalf("Goroutine %d did not make progress?", gid) 2597 } 2598 } 2599 kvals[gid] = k 2600 2601 assertNoError(grp.Step(), t, "Step()") 2602 for p.Breakpoints().HasSteppingBreakpoints() { 2603 if p.SelectedGoroutine().ID == gid { 2604 t.Fatalf("step did not step into function call (but internal breakpoints still active?) (%d %d)", gid, p.SelectedGoroutine().ID) 2605 } 2606 assertNoError(grp.Continue(), t, "Continue()") 2607 } 2608 2609 if p.SelectedGoroutine().ID != gid { 2610 t.Fatalf("Step switched goroutines (wanted: %d got: %d)", gid, p.SelectedGoroutine().ID) 2611 } 2612 2613 f, ln = assertLineNumber(p, t, 13, "Step did not step into function call") 2614 2615 count++ 2616 if count > 50 { 2617 // this test could potentially go on for 10000 cycles, since that's 2618 // too slow we cut the execution after 50 cycles 2619 break 2620 } 2621 } 2622 2623 if count == 0 { 2624 t.Fatalf("Breakpoint never hit") 2625 } 2626 }) 2627 } 2628 2629 func TestStepOutBreakpoint(t *testing.T) { 2630 protest.AllowRecording(t) 2631 withTestProcess("testnextprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2632 bp := setFileBreakpoint(p, t, fixture.Source, 13) 2633 assertNoError(grp.Continue(), t, "Continue()") 2634 p.ClearBreakpoint(bp.Addr) 2635 2636 // StepOut should be interrupted by a breakpoint on the same goroutine. 2637 setFileBreakpoint(p, t, fixture.Source, 14) 2638 assertNoError(grp.StepOut(), t, "StepOut()") 2639 assertLineNumber(p, t, 14, "wrong line number") 2640 if p.Breakpoints().HasSteppingBreakpoints() { 2641 t.Fatal("has internal breakpoints after hitting breakpoint on same goroutine") 2642 } 2643 }) 2644 } 2645 2646 func TestNextBreakpoint(t *testing.T) { 2647 protest.AllowRecording(t) 2648 withTestProcess("testnextprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2649 bp := setFileBreakpoint(p, t, fixture.Source, 34) 2650 assertNoError(grp.Continue(), t, "Continue()") 2651 p.ClearBreakpoint(bp.Addr) 2652 2653 // Next should be interrupted by a breakpoint on the same goroutine. 2654 setFileBreakpoint(p, t, fixture.Source, 14) 2655 assertNoError(grp.Next(), t, "Next()") 2656 assertLineNumber(p, t, 14, "wrong line number") 2657 if p.Breakpoints().HasSteppingBreakpoints() { 2658 t.Fatal("has internal breakpoints after hitting breakpoint on same goroutine") 2659 } 2660 }) 2661 } 2662 2663 func TestNextBreakpointKeepsSteppingBreakpoints(t *testing.T) { 2664 protest.AllowRecording(t) 2665 withTestProcess("testnextprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2666 grp.KeepSteppingBreakpoints = proc.TracepointKeepsSteppingBreakpoints 2667 bp := setFileBreakpoint(p, t, fixture.Source, 34) 2668 assertNoError(grp.Continue(), t, "Continue()") 2669 p.ClearBreakpoint(bp.Addr) 2670 2671 // Next should be interrupted by a tracepoint on the same goroutine. 2672 bp = setFileBreakpoint(p, t, fixture.Source, 14) 2673 bp.Logical.Tracepoint = true 2674 assertNoError(grp.Next(), t, "Next()") 2675 assertLineNumber(p, t, 14, "wrong line number") 2676 if !p.Breakpoints().HasSteppingBreakpoints() { 2677 t.Fatal("does not have internal breakpoints after hitting tracepoint on same goroutine") 2678 } 2679 2680 // Continue to complete next. 2681 assertNoError(grp.Continue(), t, "Continue()") 2682 assertLineNumber(p, t, 35, "wrong line number") 2683 if p.Breakpoints().HasSteppingBreakpoints() { 2684 t.Fatal("has internal breakpoints after completing next") 2685 } 2686 }) 2687 } 2688 2689 func TestStepOutDefer(t *testing.T) { 2690 protest.AllowRecording(t) 2691 withTestProcess("testnextdefer", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2692 bp := setFileBreakpoint(p, t, fixture.Source, 9) 2693 assertNoError(grp.Continue(), t, "Continue()") 2694 p.ClearBreakpoint(bp.Addr) 2695 2696 assertLineNumber(p, t, 9, "wrong line number") 2697 2698 assertNoError(grp.StepOut(), t, "StepOut()") 2699 2700 f, l, _ := p.BinInfo().PCToLine(currentPC(p, t)) 2701 if f == fixture.Source || l == 6 { 2702 t.Fatalf("wrong location %s:%d, expected to end somewhere in runtime", f, l) 2703 } 2704 }) 2705 } 2706 2707 func TestStepOutDeferReturnAndDirectCall(t *testing.T) { 2708 // StepOut should not step into a deferred function if it is called 2709 // directly, only if it is called through a panic. 2710 // Here we test the case where the function is called by a deferreturn 2711 testseq2(t, "defercall", "", []seqTest{ 2712 {contContinue, 11}, 2713 {contStepout, 28}}) 2714 } 2715 2716 func TestStepOnCallPtrInstr(t *testing.T) { 2717 protest.AllowRecording(t) 2718 withTestProcess("teststepprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2719 setFileBreakpoint(p, t, fixture.Source, 10) 2720 2721 assertNoError(grp.Continue(), t, "Continue()") 2722 2723 found := false 2724 2725 for { 2726 _, ln := currentLineNumber(p, t) 2727 if ln != 10 { 2728 break 2729 } 2730 regs, err := p.CurrentThread().Registers() 2731 assertNoError(err, t, "Registers()") 2732 pc := regs.PC() 2733 text, err := proc.Disassemble(p.Memory(), regs, p.Breakpoints(), p.BinInfo(), pc, pc+uint64(p.BinInfo().Arch.MaxInstructionLength())) 2734 assertNoError(err, t, "Disassemble()") 2735 if text[0].IsCall() { 2736 found = true 2737 break 2738 } 2739 assertNoError(grp.StepInstruction(false), t, "StepInstruction()") 2740 } 2741 2742 if !found { 2743 t.Fatal("Could not find CALL instruction") 2744 } 2745 2746 assertNoError(grp.Step(), t, "Step()") 2747 2748 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) && !protest.RegabiSupported() { 2749 assertLineNumber(p, t, 6, "Step continued to wrong line,") 2750 } else { 2751 assertLineNumber(p, t, 5, "Step continued to wrong line,") 2752 } 2753 }) 2754 } 2755 2756 func TestIssue594(t *testing.T) { 2757 skipOn(t, "upstream issue", "darwin", "lldb") 2758 // debugserver will receive an EXC_BAD_ACCESS for this, at that point 2759 // there is no way to reconvert this exception into a unix signal and send 2760 // it to the process. 2761 // This is a bug in debugserver/lldb: 2762 // https://bugs.llvm.org//show_bug.cgi?id=22868 2763 2764 // Exceptions that aren't caused by breakpoints should be propagated 2765 // back to the target. 2766 // In particular the target should be able to cause a nil pointer 2767 // dereference panic and recover from it. 2768 protest.AllowRecording(t) 2769 withTestProcess("issue594", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2770 assertNoError(grp.Continue(), t, "Continue()") 2771 var f string 2772 var ln int 2773 if testBackend == "rr" { 2774 frame, err := findFirstNonRuntimeFrame(p) 2775 assertNoError(err, t, "findFirstNonRuntimeFrame") 2776 f, ln = frame.Current.File, frame.Current.Line 2777 } else { 2778 f, ln = currentLineNumber(p, t) 2779 } 2780 if ln != 21 { 2781 t.Fatalf("Program stopped at %s:%d, expected :21", f, ln) 2782 } 2783 }) 2784 } 2785 2786 func TestStepOutPanicAndDirectCall(t *testing.T) { 2787 // StepOut should not step into a deferred function if it is called 2788 // directly, only if it is called through a panic. 2789 // Here we test the case where the function is called by a panic 2790 testseq2(t, "defercall", "", []seqTest{ 2791 {contContinue, 17}, 2792 {contStepout, 6}}) 2793 } 2794 2795 func TestWorkDir(t *testing.T) { 2796 wd := os.TempDir() 2797 // For Darwin `os.TempDir()` returns `/tmp` which is symlink to `/private/tmp`. 2798 if runtime.GOOS == "darwin" { 2799 wd = "/private/tmp" 2800 } 2801 protest.AllowRecording(t) 2802 withTestProcessArgs("workdir", t, wd, []string{}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2803 setFileBreakpoint(p, t, fixture.Source, 14) 2804 grp.Continue() 2805 v := evalVariable(p, t, "pwd") 2806 str := constant.StringVal(v.Value) 2807 if wd != str { 2808 t.Fatalf("Expected %s got %s\n", wd, str) 2809 } 2810 }) 2811 } 2812 2813 func TestNegativeIntEvaluation(t *testing.T) { 2814 testcases := []struct { 2815 name string 2816 typ string 2817 value interface{} 2818 }{ 2819 {"ni8", "int8", int64(-5)}, 2820 {"ni16", "int16", int64(-5)}, 2821 {"ni32", "int32", int64(-5)}, 2822 } 2823 protest.AllowRecording(t) 2824 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2825 assertNoError(grp.Continue(), t, "Continue()") 2826 for _, tc := range testcases { 2827 v := evalVariable(p, t, tc.name) 2828 if typ := v.RealType.String(); typ != tc.typ { 2829 t.Fatalf("Wrong type for variable %q: %q (expected: %q)", tc.name, typ, tc.typ) 2830 } 2831 if val, _ := constant.Int64Val(v.Value); val != tc.value { 2832 t.Fatalf("Wrong value for variable %q: %v (expected: %v)", tc.name, val, tc.value) 2833 } 2834 } 2835 }) 2836 } 2837 2838 func TestIssue683(t *testing.T) { 2839 // Step panics when source file can not be found 2840 protest.AllowRecording(t) 2841 withTestProcess("issue683", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2842 setFunctionBreakpoint(p, t, "main.main") 2843 assertNoError(grp.Continue(), t, "First Continue()") 2844 for i := 0; i < 20; i++ { 2845 // eventually an error about the source file not being found will be 2846 // returned, the important thing is that we shouldn't panic 2847 err := grp.Step() 2848 if err != nil { 2849 break 2850 } 2851 } 2852 }) 2853 } 2854 2855 func TestIssue664(t *testing.T) { 2856 protest.AllowRecording(t) 2857 withTestProcess("issue664", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2858 setFileBreakpoint(p, t, fixture.Source, 4) 2859 assertNoError(grp.Continue(), t, "Continue()") 2860 assertNoError(grp.Next(), t, "Next()") 2861 assertLineNumber(p, t, 5, "Did not continue to correct location,") 2862 }) 2863 } 2864 2865 // Benchmarks (*Process).Continue + (*Scope).FunctionArguments 2866 func BenchmarkTrace(b *testing.B) { 2867 withTestProcess("traceperf", b, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2868 setFunctionBreakpoint(p, b, "main.PerfCheck") 2869 b.ResetTimer() 2870 for i := 0; i < b.N; i++ { 2871 assertNoError(grp.Continue(), b, "Continue()") 2872 s, err := proc.GoroutineScope(p, p.CurrentThread()) 2873 assertNoError(err, b, "Scope()") 2874 _, err = s.FunctionArguments(proc.LoadConfig{false, 0, 64, 0, 3, 0}) 2875 assertNoError(err, b, "FunctionArguments()") 2876 } 2877 b.StopTimer() 2878 }) 2879 } 2880 2881 func TestNextInDeferReturn(t *testing.T) { 2882 // runtime.deferreturn updates the G struct in a way that for one 2883 // instruction leaves the curg._defer field non-nil but with curg._defer.fn 2884 // field being nil. 2885 // We need to deal with this without panicking. 2886 protest.AllowRecording(t) 2887 withTestProcess("defercall", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2888 setFunctionBreakpoint(p, t, "runtime.deferreturn") 2889 assertNoError(grp.Continue(), t, "First Continue()") 2890 2891 // Set a breakpoint on the deferred function so that the following loop 2892 // can not step out of the runtime.deferreturn and all the way to the 2893 // point where the target program panics. 2894 setFunctionBreakpoint(p, t, "main.sampleFunction") 2895 for i := 0; i < 20; i++ { 2896 loc, err := p.CurrentThread().Location() 2897 assertNoError(err, t, "CurrentThread().Location()") 2898 t.Logf("at %#x %s:%d", loc.PC, loc.File, loc.Line) 2899 if loc.Fn != nil && loc.Fn.Name == "main.sampleFunction" { 2900 break 2901 } 2902 assertNoError(grp.Next(), t, fmt.Sprintf("Next() %d", i)) 2903 } 2904 }) 2905 } 2906 2907 func TestAttachDetach(t *testing.T) { 2908 if testBackend == "lldb" && runtime.GOOS == "linux" { 2909 bs, _ := os.ReadFile("/proc/sys/kernel/yama/ptrace_scope") 2910 if bs == nil || strings.TrimSpace(string(bs)) != "0" { 2911 t.Logf("can not run TestAttachDetach: %v\n", bs) 2912 return 2913 } 2914 } 2915 if testBackend == "rr" { 2916 return 2917 } 2918 var buildFlags protest.BuildFlags 2919 if buildMode == "pie" { 2920 buildFlags |= protest.BuildModePIE 2921 } 2922 fixture := protest.BuildFixture("testnextnethttp", buildFlags) 2923 cmd := exec.Command(fixture.Path) 2924 cmd.Stdout = os.Stdout 2925 cmd.Stderr = os.Stderr 2926 assertNoError(cmd.Start(), t, "starting fixture") 2927 2928 // wait for testnextnethttp to start listening 2929 t0 := time.Now() 2930 for { 2931 conn, err := net.Dial("tcp", "127.0.0.1:9191") 2932 if err == nil { 2933 conn.Close() 2934 break 2935 } 2936 time.Sleep(50 * time.Millisecond) 2937 if time.Since(t0) > 10*time.Second { 2938 t.Fatal("fixture did not start") 2939 } 2940 } 2941 2942 var p *proc.TargetGroup 2943 var err error 2944 2945 switch testBackend { 2946 case "native": 2947 p, err = native.Attach(cmd.Process.Pid, nil, []string{}) 2948 case "lldb": 2949 path := "" 2950 if runtime.GOOS == "darwin" { 2951 path = fixture.Path 2952 } 2953 p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path, nil, []string{}) 2954 default: 2955 err = fmt.Errorf("unknown backend %q", testBackend) 2956 } 2957 2958 assertNoError(err, t, "Attach") 2959 go func() { 2960 time.Sleep(1 * time.Second) 2961 resp, err := http.Get("http://127.0.0.1:9191") 2962 if err == nil { 2963 resp.Body.Close() 2964 } 2965 }() 2966 2967 assertNoError(p.Continue(), t, "Continue") 2968 assertLineNumber(p.Selected, t, 11, "Did not continue to correct location,") 2969 2970 assertNoError(p.Detach(false), t, "Detach") 2971 2972 if runtime.GOOS != "darwin" { 2973 // Debugserver sometimes will leave a zombie process after detaching, this 2974 // seems to be a bug with debugserver. 2975 resp, err := http.Get("http://127.0.0.1:9191/nobp") 2976 assertNoError(err, t, "Page request after detach") 2977 bs, err := io.ReadAll(resp.Body) 2978 assertNoError(err, t, "Reading /nobp page") 2979 defer resp.Body.Close() 2980 if out := string(bs); !strings.Contains(out, "hello, world!") { 2981 t.Fatalf("/nobp page does not contain \"hello, world!\": %q", out) 2982 } 2983 } 2984 2985 cmd.Process.Kill() 2986 } 2987 2988 func TestVarSum(t *testing.T) { 2989 protest.AllowRecording(t) 2990 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 2991 assertNoError(grp.Continue(), t, "Continue()") 2992 sumvar := evalVariable(p, t, "s1[0] + s1[1]") 2993 sumvarstr := constant.StringVal(sumvar.Value) 2994 if sumvarstr != "onetwo" { 2995 t.Fatalf("s1[0] + s1[1] == %q (expected \"onetwo\")", sumvarstr) 2996 } 2997 if sumvar.Len != int64(len(sumvarstr)) { 2998 t.Fatalf("sumvar.Len == %d (expected %d)", sumvar.Len, len(sumvarstr)) 2999 } 3000 }) 3001 } 3002 3003 func TestPackageWithPathVar(t *testing.T) { 3004 protest.AllowRecording(t) 3005 withTestProcess("pkgrenames", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3006 assertNoError(grp.Continue(), t, "Continue()") 3007 evalVariable(p, t, "pkg.SomeVar") 3008 evalVariable(p, t, "pkg.SomeVar.X") 3009 }) 3010 } 3011 3012 func TestEnvironment(t *testing.T) { 3013 protest.AllowRecording(t) 3014 t.Setenv("SOMEVAR", "bah") 3015 withTestProcess("testenv", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3016 assertNoError(grp.Continue(), t, "Continue()") 3017 v := evalVariable(p, t, "x") 3018 vv := constant.StringVal(v.Value) 3019 t.Logf("v = %q", vv) 3020 if vv != "bah" { 3021 t.Fatalf("value of v is %q (expected \"bah\")", vv) 3022 } 3023 }) 3024 } 3025 3026 func getFrameOff(p *proc.Target, t *testing.T) int64 { 3027 frameoffvar := evalVariable(p, t, "runtime.frameoff") 3028 frameoff, _ := constant.Int64Val(frameoffvar.Value) 3029 return frameoff 3030 } 3031 3032 func TestRecursiveNext(t *testing.T) { 3033 protest.AllowRecording(t) 3034 testcases := []nextTest{ 3035 {6, 7}, 3036 {7, 10}, 3037 {10, 11}, 3038 {11, 17}, 3039 } 3040 testseq("increment", contNext, testcases, "main.Increment", t) 3041 3042 withTestProcess("increment", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3043 bp := setFunctionBreakpoint(p, t, "main.Increment") 3044 assertNoError(grp.Continue(), t, "Continue") 3045 err := p.ClearBreakpoint(bp.Addr) 3046 assertNoError(err, t, "ClearBreakpoint") 3047 assertNoError(grp.Next(), t, "Next 1") 3048 assertNoError(grp.Next(), t, "Next 2") 3049 assertNoError(grp.Next(), t, "Next 3") 3050 frameoff0 := getFrameOff(p, t) 3051 assertNoError(grp.Step(), t, "Step") 3052 frameoff1 := getFrameOff(p, t) 3053 if frameoff0 == frameoff1 { 3054 t.Fatalf("did not step into function?") 3055 } 3056 assertLineNumber(p, t, 6, "program did not continue to expected location,") 3057 assertNoError(grp.Next(), t, "Next 4") 3058 assertLineNumber(p, t, 7, "program did not continue to expected location,") 3059 assertNoError(grp.StepOut(), t, "StepOut") 3060 assertLineNumber(p, t, 11, "program did not continue to expected location,") 3061 frameoff2 := getFrameOff(p, t) 3062 if frameoff0 != frameoff2 { 3063 t.Fatalf("frame offset mismatch %x != %x", frameoff0, frameoff2) 3064 } 3065 }) 3066 } 3067 3068 // TestIssue877 ensures that the environment variables starting with DYLD_ and LD_ 3069 // are passed when executing the binary on OSX via debugserver 3070 func TestIssue877(t *testing.T) { 3071 if runtime.GOOS != "darwin" && testBackend == "lldb" { 3072 return 3073 } 3074 const envval = "/usr/local/lib" 3075 t.Setenv("DYLD_LIBRARY_PATH", envval) 3076 withTestProcess("issue877", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3077 assertNoError(grp.Continue(), t, "Continue()") 3078 v := evalVariable(p, t, "dyldenv") 3079 vv := constant.StringVal(v.Value) 3080 t.Logf("v = %q", vv) 3081 if vv != envval { 3082 t.Fatalf("value of v is %q (expected %q)", vv, envval) 3083 } 3084 }) 3085 } 3086 3087 func TestIssue893(t *testing.T) { 3088 // Test what happens when next is called immediately after launching the 3089 // executable, acceptable behaviors are: (a) no error, (b) no source at PC 3090 // error, (c) program runs to completion 3091 protest.AllowRecording(t) 3092 withTestProcess("increment", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3093 err := grp.Next() 3094 if err == nil { 3095 return 3096 } 3097 if _, ok := err.(*frame.ErrNoFDEForPC); ok { 3098 return 3099 } 3100 if _, ok := err.(*proc.ErrNoSourceForPC); ok { 3101 return 3102 } 3103 if _, ok := err.(proc.ErrProcessExited); ok { 3104 return 3105 } 3106 assertNoError(err, t, "Next") 3107 }) 3108 } 3109 3110 func TestStepInstructionNoGoroutine(t *testing.T) { 3111 protest.AllowRecording(t) 3112 withTestProcess("increment", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3113 // Call StepInstruction immediately after launching the program, it should 3114 // work even though no goroutine is selected. 3115 assertNoError(grp.StepInstruction(false), t, "StepInstruction") 3116 }) 3117 } 3118 3119 func TestIssue871(t *testing.T) { 3120 protest.AllowRecording(t) 3121 withTestProcess("issue871", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3122 assertNoError(grp.Continue(), t, "Continue") 3123 3124 var scope *proc.EvalScope 3125 var err error 3126 if testBackend == "rr" { 3127 var frame proc.Stackframe 3128 frame, err = findFirstNonRuntimeFrame(p) 3129 if err == nil { 3130 scope = proc.FrameToScope(p, p.Memory(), nil, 0, frame) 3131 } 3132 } else { 3133 scope, err = proc.GoroutineScope(p, p.CurrentThread()) 3134 } 3135 assertNoError(err, t, "scope") 3136 3137 locals, err := scope.LocalVariables(normalLoadConfig) 3138 assertNoError(err, t, "LocalVariables") 3139 3140 foundA, foundB := false, false 3141 3142 for _, v := range locals { 3143 t.Logf("local %v", v) 3144 switch v.Name { 3145 case "a": 3146 foundA = true 3147 if v.Flags&proc.VariableEscaped == 0 { 3148 t.Errorf("variable a not flagged as escaped") 3149 } 3150 case "b": 3151 foundB = true 3152 } 3153 } 3154 3155 if !foundA { 3156 t.Errorf("variable a not found") 3157 } 3158 3159 if !foundB { 3160 t.Errorf("variable b not found") 3161 } 3162 }) 3163 } 3164 3165 func TestShadowedFlag(t *testing.T) { 3166 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) { 3167 return 3168 } 3169 withTestProcess("testshadow", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3170 assertNoError(grp.Continue(), t, "Continue") 3171 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 3172 assertNoError(err, t, "GoroutineScope") 3173 locals, err := scope.LocalVariables(normalLoadConfig) 3174 assertNoError(err, t, "LocalVariables") 3175 foundShadowed := false 3176 foundNonShadowed := false 3177 for _, v := range locals { 3178 if v.Flags&proc.VariableShadowed != 0 { 3179 if v.Name != "a" { 3180 t.Errorf("wrong shadowed variable %s", v.Name) 3181 } 3182 foundShadowed = true 3183 if n, _ := constant.Int64Val(v.Value); n != 0 { 3184 t.Errorf("wrong value for shadowed variable a: %d", n) 3185 } 3186 } else { 3187 if v.Name != "a" { 3188 t.Errorf("wrong non-shadowed variable %s", v.Name) 3189 } 3190 foundNonShadowed = true 3191 if n, _ := constant.Int64Val(v.Value); n != 1 { 3192 t.Errorf("wrong value for non-shadowed variable a: %d", n) 3193 } 3194 } 3195 } 3196 if !foundShadowed { 3197 t.Error("could not find any shadowed variable") 3198 } 3199 if !foundNonShadowed { 3200 t.Error("could not find any non-shadowed variable") 3201 } 3202 }) 3203 } 3204 3205 func TestDebugStripped(t *testing.T) { 3206 // Currently only implemented for Linux ELF and macOS Mach-O executables. 3207 // TODO(derekparker): Add support for PE. 3208 skipOn(t, "not working on windows", "windows") 3209 skipOn(t, "not working on freebsd", "freebsd") 3210 skipOn(t, "not working on linux/386", "linux", "386") 3211 skipOn(t, "not working on linux/ppc64le when -gcflags=-N -l is passed", "linux", "ppc64le") 3212 ver, _ := goversion.Parse(runtime.Version()) 3213 if ver.IsDevel() { 3214 t.Skip("not supported") 3215 } 3216 withTestProcessArgs("testnextprog", t, "", []string{}, protest.LinkStrip, func(p *proc.Target, grp *proc.TargetGroup, f protest.Fixture) { 3217 setFunctionBreakpoint(p, t, "main.main") 3218 assertNoError(grp.Continue(), t, "Continue") 3219 assertCurrentLocationFunction(p, t, "main.main") 3220 assertLineNumber(p, t, 37, "first continue") 3221 assertNoError(grp.Next(), t, "Next") 3222 assertLineNumber(p, t, 38, "after next") 3223 3224 // Assert that commands like "args", "locals", etc... will 3225 // return an error instead of panic. 3226 s, err := proc.ThreadScope(p, p.CurrentThread()) 3227 assertNoError(err, t, "ThreadScope") 3228 _, err = s.Locals(0) 3229 if err == nil { 3230 t.Error("expected an error to be returned from scope.Locals in stripped binary") 3231 } 3232 }) 3233 } 3234 3235 func TestDebugStripped2(t *testing.T) { 3236 // TODO(derekparker): Add support for PE. 3237 skipOn(t, "not working on windows", "windows") 3238 skipOn(t, "not working on freebsd", "freebsd") 3239 skipOn(t, "not working on linux/386", "linux", "386") 3240 skipOn(t, "not working on linux/ppc64le when -gcflags=-N -l is passed", "linux", "ppc64le") 3241 ver, _ := goversion.Parse(runtime.Version()) 3242 if ver.IsDevel() { 3243 t.Skip("not supported") 3244 } 3245 if ver.Major > 0 && ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 22, Rev: -1}) { 3246 skipOn(t, "not working on linux/arm64 with Go 1.22", "linux", "arm64") 3247 } 3248 withTestProcessArgs("inlinestripped", t, "", []string{}, protest.EnableInlining|protest.LinkStrip|protest.LinkDisableDWARF, func(p *proc.Target, grp *proc.TargetGroup, f protest.Fixture) { 3249 setFunctionBreakpointAll(p, t, "fmt.Println") 3250 3251 for i, line := range []int{12, 13, 14} { 3252 assertNoError(grp.Continue(), t, "Continue") 3253 assertCurrentLocationFunction(p, t, "main.main") 3254 assertLineNumber(p, t, line, fmt.Sprintf("continue %d", i)) 3255 } 3256 }) 3257 } 3258 3259 func TestIssue844(t *testing.T) { 3260 // Conditional breakpoints should not prevent next from working if their 3261 // condition isn't met. 3262 withTestProcess("nextcond", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3263 setFileBreakpoint(p, t, fixture.Source, 9) 3264 condbp := setFileBreakpoint(p, t, fixture.Source, 10) 3265 condbp.UserBreaklet().Cond = &ast.BinaryExpr{ 3266 Op: token.EQL, 3267 X: &ast.Ident{Name: "n"}, 3268 Y: &ast.BasicLit{Kind: token.INT, Value: "11"}, 3269 } 3270 assertNoError(grp.Continue(), t, "Continue") 3271 assertNoError(grp.Next(), t, "Next") 3272 assertLineNumber(p, t, 10, "continued to wrong location,") 3273 }) 3274 } 3275 3276 func logStacktrace(t *testing.T, p *proc.Target, frames []proc.Stackframe) { 3277 w := tabwriter.NewWriter(os.Stderr, 0, 0, 3, ' ', 0) 3278 fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n", "Call PC", "Frame Offset", "Frame Pointer Offset", "PC", "Return", "Function", "Location", "Top Defer", "Defers") 3279 for j := range frames { 3280 name := "?" 3281 if frames[j].Current.Fn != nil { 3282 name = frames[j].Current.Fn.Name 3283 } 3284 if frames[j].Call.Fn != nil && frames[j].Current.Fn != frames[j].Call.Fn { 3285 name = fmt.Sprintf("%s inlined in %s", frames[j].Call.Fn.Name, frames[j].Current.Fn.Name) 3286 } 3287 3288 topmostdefer := "" 3289 if frames[j].TopmostDefer != nil { 3290 _, _, fn := frames[j].TopmostDefer.DeferredFunc(p) 3291 fnname := "" 3292 if fn != nil { 3293 fnname = fn.Name 3294 } 3295 topmostdefer = fmt.Sprintf("%#x %s", frames[j].TopmostDefer.DwrapPC, fnname) 3296 } 3297 3298 defers := "" 3299 for deferIdx, _defer := range frames[j].Defers { 3300 _, _, fn := _defer.DeferredFunc(p) 3301 fnname := "" 3302 if fn != nil { 3303 fnname = fn.Name 3304 } 3305 defers += fmt.Sprintf("%d %#x %s |", deferIdx, _defer.DwrapPC, fnname) 3306 } 3307 3308 frame := frames[j] 3309 fmt.Fprintf(w, "%#x\t%#x\t%#x\t%#x\t%#x\t%s\t%s:%d\t%s\t%s\t\n", 3310 frame.Call.PC, frame.FrameOffset(), frame.FramePointerOffset(), frame.Current.PC, frame.Ret, 3311 name, filepath.Base(frame.Call.File), frame.Call.Line, topmostdefer, defers) 3312 3313 } 3314 w.Flush() 3315 } 3316 3317 // stacktraceCheck checks that all the functions listed in tc appear in 3318 // frames in the same order. 3319 // Checks that all the functions in tc starting with "C." or with "!" are in 3320 // a systemstack frame. 3321 // Returns a slice m where m[i] is the index in frames of the function tc[i] 3322 // or nil if any check fails. 3323 func stacktraceCheck(t *testing.T, tc []string, frames []proc.Stackframe) []int { 3324 m := make([]int, len(tc)) 3325 i, j := 0, 0 3326 for i < len(tc) { 3327 tcname := tc[i] 3328 tcsystem := strings.HasPrefix(tcname, "C.") 3329 if tcname[0] == '!' { 3330 tcsystem = true 3331 tcname = tcname[1:] 3332 } 3333 for j < len(frames) { 3334 name := "?" 3335 if frames[j].Current.Fn != nil { 3336 name = frames[j].Current.Fn.Name 3337 } 3338 if name == tcname { 3339 m[i] = j 3340 if tcsystem != frames[j].SystemStack { 3341 t.Logf("system stack check failed for frame %d (expected %v got %v)", j, tcsystem, frames[j].SystemStack) 3342 t.Logf("expected: %v\n", tc) 3343 return nil 3344 } 3345 break 3346 } 3347 3348 j++ 3349 } 3350 if j >= len(frames) { 3351 t.Logf("couldn't find frame %d %s", i, tc) 3352 t.Logf("expected: %v\n", tc) 3353 return nil 3354 } 3355 3356 i++ 3357 } 3358 return m 3359 } 3360 3361 func frameInFile(frame proc.Stackframe, file string) bool { 3362 for _, loc := range []proc.Location{frame.Current, frame.Call} { 3363 if !strings.HasSuffix(loc.File, file) && !strings.HasSuffix(loc.File, "/"+file) && !strings.HasSuffix(loc.File, "\\"+file) { 3364 return false 3365 } 3366 if loc.Line <= 0 { 3367 return false 3368 } 3369 } 3370 return true 3371 } 3372 3373 func TestCgoStacktrace(t *testing.T) { 3374 if runtime.GOOS == "windows" { 3375 ver, _ := goversion.Parse(runtime.Version()) 3376 if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) { 3377 t.Skip("disabled on windows with go before version 1.9") 3378 } 3379 } 3380 if runtime.GOOS == "darwin" { 3381 ver, _ := goversion.Parse(runtime.Version()) 3382 if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 8, Rev: -1}) { 3383 t.Skip("disabled on macOS with go before version 1.8") 3384 } 3385 } 3386 skipOn(t, "broken - cgo stacktraces", "386") 3387 skipOn(t, "broken - cgo stacktraces", "windows", "arm64") 3388 skipOn(t, "broken - cgo stacktraces", "linux", "ppc64le") 3389 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 21) { 3390 skipOn(t, "broken - cgo stacktraces", "windows", "arm64") 3391 } 3392 protest.MustHaveCgo(t) 3393 3394 // Tests that: 3395 // a) we correctly identify the goroutine while we are executing cgo code 3396 // b) that we can stitch together the system stack (where cgo code 3397 // executes) and the normal goroutine stack 3398 3399 // Each test case describes how the stack trace should appear after a 3400 // continue. The first function on each test case is the topmost function 3401 // that should be found on the stack, the actual stack trace can have more 3402 // frame than those listed here but all the frames listed must appear in 3403 // the specified order. 3404 testCases := [][]string{ 3405 {"main.main"}, 3406 {"C.helloworld_pt2", "C.helloworld", "main.main"}, 3407 {"main.helloWorldS", "main.helloWorld", "C.helloworld_pt2", "C.helloworld", "main.main"}, 3408 {"C.helloworld_pt4", "C.helloworld_pt3", "main.helloWorldS", "main.helloWorld", "C.helloworld_pt2", "C.helloworld", "main.main"}, 3409 {"main.helloWorld2", "C.helloworld_pt4", "C.helloworld_pt3", "main.helloWorldS", "main.helloWorld", "C.helloworld_pt2", "C.helloworld", "main.main"}} 3410 3411 var gid int64 3412 3413 frameOffs := map[string]int64{} 3414 framePointerOffs := map[string]int64{} 3415 3416 withTestProcess("cgostacktest/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3417 for itidx, tc := range testCases { 3418 t.Logf("iteration step %d", itidx) 3419 3420 assertNoError(grp.Continue(), t, fmt.Sprintf("Continue at iteration step %d", itidx)) 3421 3422 g, err := proc.GetG(p.CurrentThread()) 3423 assertNoError(err, t, fmt.Sprintf("GetG at iteration step %d", itidx)) 3424 3425 if itidx == 0 { 3426 gid = g.ID 3427 } else { 3428 if gid != g.ID { 3429 t.Fatalf("wrong goroutine id at iteration step %d (expected %d got %d)", itidx, gid, g.ID) 3430 } 3431 } 3432 3433 frames, err := proc.GoroutineStacktrace(p, g, 100, 0) 3434 assertNoError(err, t, fmt.Sprintf("Stacktrace at iteration step %d", itidx)) 3435 3436 logStacktrace(t, p, frames) 3437 3438 m := stacktraceCheck(t, tc, frames) 3439 mismatch := m == nil 3440 3441 for i, j := range m { 3442 if strings.HasPrefix(tc[i], "C.hellow") { 3443 if !frameInFile(frames[j], "hello.c") { 3444 t.Logf("position in %q is %s:%d (call %s:%d)", tc[i], frames[j].Current.File, frames[j].Current.Line, frames[j].Call.File, frames[j].Call.Line) 3445 mismatch = true 3446 break 3447 } 3448 } 3449 if frameOff, ok := frameOffs[tc[i]]; ok { 3450 if frameOff != frames[j].FrameOffset() { 3451 t.Logf("frame %s offset mismatch", tc[i]) 3452 } 3453 if framePointerOffs[tc[i]] != frames[j].FramePointerOffset() { 3454 t.Logf("frame %s pointer offset mismatch, expected: %#v actual: %#v", tc[i], framePointerOffs[tc[i]], frames[j].FramePointerOffset()) 3455 } 3456 } else { 3457 frameOffs[tc[i]] = frames[j].FrameOffset() 3458 framePointerOffs[tc[i]] = frames[j].FramePointerOffset() 3459 } 3460 } 3461 3462 // also check that ThreadStacktrace produces the same list of frames 3463 threadFrames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 100) 3464 assertNoError(err, t, fmt.Sprintf("ThreadStacktrace at iteration step %d", itidx)) 3465 3466 if len(threadFrames) != len(frames) { 3467 mismatch = true 3468 } else { 3469 for j := range frames { 3470 if frames[j].Current.File != threadFrames[j].Current.File || frames[j].Current.Line != threadFrames[j].Current.Line { 3471 t.Logf("stack mismatch between goroutine stacktrace and thread stacktrace") 3472 t.Logf("thread stacktrace:") 3473 logStacktrace(t, p, threadFrames) 3474 mismatch = true 3475 break 3476 } 3477 } 3478 } 3479 if mismatch { 3480 t.Fatal("see previous loglines") 3481 } 3482 } 3483 }) 3484 } 3485 3486 func TestCgoSources(t *testing.T) { 3487 if runtime.GOOS == "windows" { 3488 ver, _ := goversion.Parse(runtime.Version()) 3489 if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) { 3490 t.Skip("disabled on windows with go before version 1.9") 3491 } 3492 } 3493 3494 if runtime.GOARCH == "386" { 3495 t.Skip("cgo stacktraces not supported on i386 for now") 3496 } 3497 3498 protest.MustHaveCgo(t) 3499 3500 withTestProcess("cgostacktest/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3501 sources := p.BinInfo().Sources 3502 for _, needle := range []string{"main.go", "hello.c"} { 3503 found := false 3504 for _, k := range sources { 3505 if strings.HasSuffix(k, needle) || strings.HasSuffix(k, "/"+needle) || strings.HasSuffix(k, "\\"+needle) { 3506 found = true 3507 break 3508 } 3509 } 3510 if !found { 3511 t.Errorf("File %s not found", needle) 3512 } 3513 } 3514 }) 3515 } 3516 3517 func TestSystemstackStacktrace(t *testing.T) { 3518 skipOn(t, "broken", "ppc64le") 3519 // check that we can follow a stack switch initiated by runtime.systemstack() 3520 withTestProcess("panic", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3521 setFunctionBreakpoint(p, t, "runtime.startpanic_m") 3522 assertNoError(grp.Continue(), t, "first continue") 3523 assertNoError(grp.Continue(), t, "second continue") 3524 g, err := proc.GetG(p.CurrentThread()) 3525 assertNoError(err, t, "GetG") 3526 frames, err := proc.GoroutineStacktrace(p, g, 100, 0) 3527 assertNoError(err, t, "stacktrace") 3528 logStacktrace(t, p, frames) 3529 m := stacktraceCheck(t, []string{"!runtime.startpanic_m", "runtime.gopanic", "main.main"}, frames) 3530 if m == nil { 3531 t.Fatal("see previous loglines") 3532 } 3533 }) 3534 } 3535 3536 func TestSystemstackOnRuntimeNewstack(t *testing.T) { 3537 skipOn(t, "broken", "ppc64le") 3538 // The bug being tested here manifests as follows: 3539 // - set a breakpoint somewhere or interrupt the program with Ctrl-C 3540 // - try to look at stacktraces of other goroutines 3541 // If one of the other goroutines is resizing its own stack the stack 3542 // command won't work for it. 3543 withTestProcess("binarytrees", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3544 setFunctionBreakpoint(p, t, "main.main") 3545 assertNoError(grp.Continue(), t, "first continue") 3546 3547 g, err := proc.GetG(p.CurrentThread()) 3548 assertNoError(err, t, "GetG") 3549 mainGoroutineID := g.ID 3550 3551 setFunctionBreakpointAll(p, t, "runtime.newstack") 3552 for { 3553 assertNoError(grp.Continue(), t, "second continue") 3554 g, err = proc.GetG(p.CurrentThread()) 3555 assertNoError(err, t, "GetG") 3556 if g.ID == mainGoroutineID { 3557 break 3558 } 3559 } 3560 frames, err := proc.GoroutineStacktrace(p, g, 100, 0) 3561 assertNoError(err, t, "stacktrace") 3562 logStacktrace(t, p, frames) 3563 m := stacktraceCheck(t, []string{"!runtime.newstack", "main.main"}, frames) 3564 if m == nil { 3565 t.Fatal("see previous loglines") 3566 } 3567 }) 3568 } 3569 3570 func TestIssue1034(t *testing.T) { 3571 skipOn(t, "broken - cgo stacktraces", "386") 3572 protest.MustHaveCgo(t) 3573 3574 // The external linker on macOS produces an abbrev for DW_TAG_subprogram 3575 // without the "has children" flag, we should support this. 3576 withTestProcess("cgostacktest/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3577 setFunctionBreakpoint(p, t, "main.main") 3578 assertNoError(grp.Continue(), t, "Continue()") 3579 frames, err := proc.GoroutineStacktrace(p, p.SelectedGoroutine(), 10, 0) 3580 assertNoError(err, t, "Stacktrace") 3581 scope := proc.FrameToScope(p, p.Memory(), nil, 0, frames[2:]...) 3582 args, _ := scope.FunctionArguments(normalLoadConfig) 3583 assertNoError(err, t, "FunctionArguments()") 3584 if len(args) > 0 { 3585 t.Fatalf("wrong number of arguments for frame %v (%d)", frames[2], len(args)) 3586 } 3587 }) 3588 } 3589 3590 func TestIssue1008(t *testing.T) { 3591 skipOn(t, "broken - cgo stacktraces", "386") 3592 protest.MustHaveCgo(t) 3593 3594 // The external linker on macOS inserts "end of sequence" extended opcodes 3595 // in debug_line. which we should support correctly. 3596 withTestProcess("cgostacktest/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3597 setFunctionBreakpoint(p, t, "main.main") 3598 assertNoError(grp.Continue(), t, "Continue()") 3599 loc, err := p.CurrentThread().Location() 3600 assertNoError(err, t, "CurrentThread().Location()") 3601 t.Logf("location %v\n", loc) 3602 if !strings.HasSuffix(loc.File, "/main.go") { 3603 t.Errorf("unexpected location %s:%d\n", loc.File, loc.Line) 3604 } 3605 if loc.Line > 35 { 3606 t.Errorf("unexpected location %s:%d (file only has 34 lines)\n", loc.File, loc.Line) 3607 } 3608 }) 3609 } 3610 3611 func testDeclLineCount(t *testing.T, p *proc.Target, lineno int, tgtvars []string) { 3612 sort.Strings(tgtvars) 3613 3614 assertLineNumber(p, t, lineno, "Program did not continue to correct next location") 3615 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 3616 assertNoError(err, t, fmt.Sprintf("GoroutineScope (:%d)", lineno)) 3617 vars, err := scope.Locals(0) 3618 assertNoError(err, t, fmt.Sprintf("Locals (:%d)", lineno)) 3619 if len(vars) != len(tgtvars) { 3620 t.Fatalf("wrong number of variables %d (:%d)", len(vars), lineno) 3621 } 3622 outvars := make([]string, len(vars)) 3623 for i, v := range vars { 3624 outvars[i] = v.Name 3625 } 3626 sort.Strings(outvars) 3627 3628 for i := range outvars { 3629 if tgtvars[i] != outvars[i] { 3630 t.Fatalf("wrong variables, got: %q expected %q\n", outvars, tgtvars) 3631 } 3632 } 3633 } 3634 3635 func TestDeclLine(t *testing.T) { 3636 ver, _ := goversion.Parse(runtime.Version()) 3637 if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 3638 t.Skip("go 1.9 and prior versions do not emit DW_AT_decl_line") 3639 } 3640 3641 withTestProcess("decllinetest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3642 setFileBreakpoint(p, t, fixture.Source, 8) 3643 setFileBreakpoint(p, t, fixture.Source, 9) 3644 setFileBreakpoint(p, t, fixture.Source, 10) 3645 setFileBreakpoint(p, t, fixture.Source, 11) 3646 b := setFunctionBreakpoint(p, t, "main.f1") 3647 if b.Line != 14 { 3648 // Line 14 is hard-coded below. 3649 t.Fatalf("expected \"main.f1\" breakpoint to be set on line 14, but got line: %d", b.Line) 3650 } 3651 3652 assertNoError(grp.Continue(), t, "Continue 1") 3653 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) { 3654 testDeclLineCount(t, p, 8, []string{}) 3655 } else { 3656 testDeclLineCount(t, p, 8, []string{"a"}) 3657 } 3658 3659 assertNoError(grp.Continue(), t, "Continue 2") 3660 testDeclLineCount(t, p, 9, []string{"a"}) 3661 3662 assertNoError(grp.Continue(), t, "Continue 3") 3663 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) { 3664 testDeclLineCount(t, p, 10, []string{"a"}) 3665 } else { 3666 testDeclLineCount(t, p, 10, []string{"a", "b"}) 3667 } 3668 3669 assertNoError(grp.Continue(), t, "Continue 4") 3670 testDeclLineCount(t, p, 11, []string{"a", "b"}) 3671 3672 assertNoError(grp.Continue(), t, "Continue 5") 3673 // On line 14, we expect the function's arguments to be available, even 3674 // though their DW_AT_decl_line declares higher line numbers. The decl_line 3675 // is supposed to be ignored for the visibility of arguments. 3676 testDeclLineCount(t, p, 14, []string{"a", "b"}) 3677 }) 3678 } 3679 3680 func TestIssue1137(t *testing.T) { 3681 withTestProcess("dotpackagesiface", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3682 assertNoError(grp.Continue(), t, "Continue()") 3683 v := evalVariable(p, t, "iface") 3684 assertNoError(v.Unreadable, t, "iface unreadable") 3685 v2 := evalVariable(p, t, "iface2") 3686 assertNoError(v2.Unreadable, t, "iface2 unreadable") 3687 }) 3688 } 3689 3690 func TestIssue1101(t *testing.T) { 3691 // If a breakpoint is hit close to process death on a thread that isn't the 3692 // group leader the process could die while we are trying to stop it. 3693 // 3694 // This can be easily reproduced by having the goroutine that's executing 3695 // main.main (which will almost always run on the thread group leader) wait 3696 // for a second goroutine before exiting, then setting a breakpoint on the 3697 // second goroutine and stepping through it (see TestIssue1101 in 3698 // proc_test.go). 3699 // 3700 // When stepping over the return instruction of main.f the deferred 3701 // wg.Done() call will be executed which will cause the main goroutine to 3702 // resume and proceed to exit. Both the temporary breakpoint on wg.Done and 3703 // the temporary breakpoint on the return address of main.f will be in 3704 // close proximity to main.main calling os.Exit() and causing the death of 3705 // the thread group leader. 3706 3707 withTestProcess("issue1101", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3708 setFunctionBreakpoint(p, t, "main.f") 3709 assertNoError(grp.Continue(), t, "Continue()") 3710 assertNoError(grp.Next(), t, "Next() 1") 3711 assertNoError(grp.Next(), t, "Next() 2") 3712 lastCmd := "Next() 3" 3713 exitErr := grp.Next() 3714 if exitErr == nil { 3715 lastCmd = "final Continue()" 3716 exitErr = grp.Continue() 3717 } 3718 if pexit, exited := exitErr.(proc.ErrProcessExited); exited { 3719 if pexit.Status != 2 && testBackend != "lldb" && (runtime.GOOS != "linux" || runtime.GOARCH != "386") { 3720 // Looks like there's a bug with debugserver on macOS that sometimes 3721 // will report exit status 0 instead of the proper exit status. 3722 // 3723 // Also it seems that sometimes on linux/386 we will not receive the 3724 // exit status. This happens if the process exits at the same time as it 3725 // receives a signal. 3726 t.Fatalf("process exited status %d (expected 2) (last command = %s) (%#v)", pexit.Status, lastCmd, pexit) 3727 } 3728 } else { 3729 assertNoError(exitErr, t, lastCmd) 3730 t.Fatalf("process did not exit after %s", lastCmd) 3731 } 3732 }) 3733 } 3734 3735 func TestIssue1145(t *testing.T) { 3736 withTestProcess("sleep", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3737 setFileBreakpoint(p, t, fixture.Source, 18) 3738 assertNoError(grp.Continue(), t, "Continue()") 3739 resumeChan := make(chan struct{}, 1) 3740 grp.ResumeNotify(resumeChan) 3741 go func() { 3742 <-resumeChan 3743 time.Sleep(100 * time.Millisecond) 3744 grp.RequestManualStop() 3745 }() 3746 3747 assertNoError(grp.Next(), t, "Next()") 3748 if p.Breakpoints().HasSteppingBreakpoints() { 3749 t.Fatal("has internal breakpoints after manual stop request") 3750 } 3751 }) 3752 } 3753 3754 func TestHaltKeepsSteppingBreakpoints(t *testing.T) { 3755 withTestProcess("sleep", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3756 grp.KeepSteppingBreakpoints = proc.HaltKeepsSteppingBreakpoints 3757 setFileBreakpoint(p, t, fixture.Source, 18) 3758 assertNoError(grp.Continue(), t, "Continue()") 3759 resumeChan := make(chan struct{}, 1) 3760 grp.ResumeNotify(resumeChan) 3761 go func() { 3762 <-resumeChan 3763 time.Sleep(100 * time.Millisecond) 3764 grp.RequestManualStop() 3765 }() 3766 3767 assertNoError(grp.Next(), t, "Next()") 3768 if !p.Breakpoints().HasSteppingBreakpoints() { 3769 t.Fatal("does not have internal breakpoints after manual stop request") 3770 } 3771 }) 3772 } 3773 3774 func TestDisassembleGlobalVars(t *testing.T) { 3775 skipOn(t, "broken - global variable symbolication", "arm64") // On ARM64 symLookup can't look up variables due to how they are loaded, see issue #1778 3776 skipOn(t, "broken - global variable symbolication", "ppc64le") // See comment on ARM64 above. 3777 // On 386 linux when pie, the generated code use __x86.get_pc_thunk to ensure position-independent. 3778 // Locate global variable by 3779 // `CALL __x86.get_pc_thunk.ax(SB) 0xb0f7f 3780 // LEAL 0xc0a19(AX), AX` 3781 // dynamically. 3782 if runtime.GOARCH == "386" && runtime.GOOS == "linux" && buildMode == "pie" { 3783 t.Skip("On 386 linux when pie, symLookup can't look up global variables") 3784 } 3785 withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3786 mainfn := p.BinInfo().LookupFunc()["main.main"][0] 3787 regs, _ := p.CurrentThread().Registers() 3788 text, err := proc.Disassemble(p.Memory(), regs, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End) 3789 assertNoError(err, t, "Disassemble") 3790 found := false 3791 for i := range text { 3792 if strings.Index(text[i].Text(proc.IntelFlavour, p.BinInfo()), "main.v") > 0 { 3793 found = true 3794 break 3795 } 3796 } 3797 if !found { 3798 t.Fatalf("could not find main.v reference in disassembly") 3799 } 3800 }) 3801 } 3802 3803 func checkFrame(frame proc.Stackframe, fnname, file string, line int, inlined bool) error { 3804 if frame.Call.Fn == nil || frame.Call.Fn.Name != fnname { 3805 return fmt.Errorf("wrong function name: %s", fnname) 3806 } 3807 if file != "" { 3808 if frame.Call.File != file || frame.Call.Line != line { 3809 return fmt.Errorf("wrong file:line %s:%d", frame.Call.File, frame.Call.Line) 3810 } 3811 } 3812 if frame.Inlined != inlined { 3813 if inlined { 3814 return fmt.Errorf("not inlined") 3815 } 3816 return fmt.Errorf("inlined") 3817 } 3818 return nil 3819 } 3820 3821 func TestAllPCsForFileLines(t *testing.T) { 3822 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 3823 // Versions of go before 1.10 do not have DWARF information for inlined calls 3824 t.Skip("inlining not supported") 3825 } 3826 withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3827 l2pcs := p.BinInfo().AllPCsForFileLines(fixture.Source, []int{7, 20}) 3828 if len(l2pcs) != 2 { 3829 t.Fatalf("expected two map entries for %s:{%d,%d} (got %d: %v)", fixture.Source, 7, 20, len(l2pcs), l2pcs) 3830 } 3831 pcs := l2pcs[20] 3832 if len(pcs) < 1 { 3833 t.Fatalf("expected at least one location for %s:%d (got %d: %#x)", fixture.Source, 20, len(pcs), pcs) 3834 } 3835 pcs = l2pcs[7] 3836 if len(pcs) < 2 { 3837 t.Fatalf("expected at least two locations for %s:%d (got %d: %#x)", fixture.Source, 7, len(pcs), pcs) 3838 } 3839 }) 3840 } 3841 3842 func TestInlinedStacktraceAndVariables(t *testing.T) { 3843 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 3844 // Versions of go before 1.10 do not have DWARF information for inlined calls 3845 t.Skip("inlining not supported") 3846 } 3847 3848 firstCallCheck := &scopeCheck{ 3849 line: 7, 3850 ok: false, 3851 varChecks: []varCheck{ 3852 { 3853 name: "a", 3854 typ: "int", 3855 kind: reflect.Int, 3856 hasVal: true, 3857 intVal: 3, 3858 }, 3859 { 3860 name: "z", 3861 typ: "int", 3862 kind: reflect.Int, 3863 hasVal: true, 3864 intVal: 9, 3865 }, 3866 }, 3867 } 3868 3869 secondCallCheck := &scopeCheck{ 3870 line: 7, 3871 ok: false, 3872 varChecks: []varCheck{ 3873 { 3874 name: "a", 3875 typ: "int", 3876 kind: reflect.Int, 3877 hasVal: true, 3878 intVal: 4, 3879 }, 3880 { 3881 name: "z", 3882 typ: "int", 3883 kind: reflect.Int, 3884 hasVal: true, 3885 intVal: 16, 3886 }, 3887 }, 3888 } 3889 3890 withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 3891 pcs, err := proc.FindFileLocation(p, fixture.Source, 7) 3892 assertNoError(err, t, "LineToPC") 3893 if len(pcs) < 2 { 3894 t.Fatalf("expected at least two locations for %s:%d (got %d: %#x)", fixture.Source, 7, len(pcs), pcs) 3895 } 3896 for _, pc := range pcs { 3897 t.Logf("setting breakpoint at %#x\n", pc) 3898 _, err := p.SetBreakpoint(0, pc, proc.UserBreakpoint, nil) 3899 assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%#x)", pc)) 3900 } 3901 3902 // first inlined call 3903 assertNoError(grp.Continue(), t, "Continue") 3904 frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 20) 3905 assertNoError(err, t, "ThreadStacktrace") 3906 t.Logf("Stacktrace:\n") 3907 for i := range frames { 3908 t.Logf("\t%s at %s:%d (%#x)\n", frames[i].Call.Fn.Name, frames[i].Call.File, frames[i].Call.Line, frames[i].Current.PC) 3909 } 3910 3911 if err := checkFrame(frames[0], "main.inlineThis", fixture.Source, 7, true); err != nil { 3912 t.Fatalf("Wrong frame 0: %v", err) 3913 } 3914 if err := checkFrame(frames[1], "main.main", fixture.Source, 18, false); err != nil { 3915 t.Fatalf("Wrong frame 1: %v", err) 3916 } 3917 3918 if avar, _ := constant.Int64Val(evalVariable(p, t, "a").Value); avar != 3 { 3919 t.Fatalf("value of 'a' variable is not 3 (%d)", avar) 3920 } 3921 if zvar, _ := constant.Int64Val(evalVariable(p, t, "z").Value); zvar != 9 { 3922 t.Fatalf("value of 'z' variable is not 9 (%d)", zvar) 3923 } 3924 3925 if _, ok := firstCallCheck.checkLocalsAndArgs(p, t); !ok { 3926 t.Fatalf("exiting for past errors") 3927 } 3928 3929 // second inlined call 3930 assertNoError(grp.Continue(), t, "Continue") 3931 frames, err = proc.ThreadStacktrace(p, p.CurrentThread(), 20) 3932 assertNoError(err, t, "ThreadStacktrace (2)") 3933 t.Logf("Stacktrace 2:\n") 3934 for i := range frames { 3935 t.Logf("\t%s at %s:%d (%#x)\n", frames[i].Call.Fn.Name, frames[i].Call.File, frames[i].Call.Line, frames[i].Current.PC) 3936 } 3937 3938 if err := checkFrame(frames[0], "main.inlineThis", fixture.Source, 7, true); err != nil { 3939 t.Fatalf("Wrong frame 0: %v", err) 3940 } 3941 if err := checkFrame(frames[1], "main.main", fixture.Source, 19, false); err != nil { 3942 t.Fatalf("Wrong frame 1: %v", err) 3943 } 3944 3945 if avar, _ := constant.Int64Val(evalVariable(p, t, "a").Value); avar != 4 { 3946 t.Fatalf("value of 'a' variable is not 3 (%d)", avar) 3947 } 3948 if zvar, _ := constant.Int64Val(evalVariable(p, t, "z").Value); zvar != 16 { 3949 t.Fatalf("value of 'z' variable is not 9 (%d)", zvar) 3950 } 3951 if bvar, err := evalVariableOrError(p, "b"); err == nil { 3952 t.Fatalf("expected error evaluating 'b', but it succeeded instead: %v", bvar) 3953 } 3954 3955 if _, ok := secondCallCheck.checkLocalsAndArgs(p, t); !ok { 3956 t.Fatalf("exiting for past errors") 3957 } 3958 }) 3959 } 3960 3961 func TestInlineStep(t *testing.T) { 3962 skipOn(t, "broken", "ppc64le") 3963 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 3964 // Versions of go before 1.10 do not have DWARF information for inlined calls 3965 t.Skip("inlining not supported") 3966 } 3967 testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{ 3968 {contContinue, 18}, 3969 {contStep, 6}, 3970 {contStep, 7}, 3971 {contStep, 24}, 3972 {contStep, 25}, 3973 {contStep, 7}, 3974 {contStep, 18}, 3975 {contStep, 19}, 3976 }) 3977 } 3978 3979 func TestInlineNext(t *testing.T) { 3980 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 3981 // Versions of go before 1.10 do not have DWARF information for inlined calls 3982 t.Skip("inlining not supported") 3983 } 3984 testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{ 3985 {contContinue, 18}, 3986 {contStep, 6}, 3987 {contNext, 7}, 3988 {contNext, 18}, 3989 {contNext, 19}, 3990 }) 3991 } 3992 3993 func TestInlineStepOver(t *testing.T) { 3994 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 3995 // Versions of go before 1.10 do not have DWARF information for inlined calls 3996 t.Skip("inlining not supported") 3997 } 3998 testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{ 3999 {contContinue, 18}, 4000 {contNext, 19}, 4001 {contNext, 20}, 4002 }) 4003 } 4004 4005 func TestInlineStepOut(t *testing.T) { 4006 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 4007 // Versions of go before 1.10 do not have DWARF information for inlined calls 4008 t.Skip("inlining not supported") 4009 } 4010 testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{ 4011 {contContinue, 18}, 4012 {contStep, 6}, 4013 {contStepout, 18}, 4014 }) 4015 } 4016 4017 func TestInlineFunctionList(t *testing.T) { 4018 // We should be able to list all functions, even inlined ones. 4019 ver, _ := goversion.Parse(runtime.Version()) 4020 if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 4021 // Versions of go before 1.10 do not have DWARF information for inlined calls 4022 t.Skip("inlining not supported") 4023 } 4024 if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" { 4025 // TODO(qmuntal): seems to be an upstream issue, investigate. 4026 t.Skip("inlining not supported") 4027 } 4028 withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4029 var found bool 4030 for _, fn := range p.BinInfo().Functions { 4031 if strings.Contains(fn.Name, "inlineThis") { 4032 found = true 4033 break 4034 } 4035 } 4036 if !found { 4037 t.Fatal("inline function not returned") 4038 } 4039 }) 4040 } 4041 4042 func TestInlineBreakpoint(t *testing.T) { 4043 // We should be able to set a breakpoint on the call site of an inlined function. 4044 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 4045 // Versions of go before 1.10 do not have DWARF information for inlined calls 4046 t.Skip("inlining not supported") 4047 } 4048 withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4049 pcs, err := proc.FindFileLocation(p, fixture.Source, 17) 4050 if err != nil { 4051 t.Fatal(err) 4052 } 4053 t.Logf("%#v\n", pcs) 4054 if len(pcs) != 1 { 4055 t.Fatalf("unable to get PC for inlined function call: %v", pcs) 4056 } 4057 fn := p.BinInfo().PCToFunc(pcs[0]) 4058 expectedFn := "main.main" 4059 if fn.Name != expectedFn { 4060 t.Fatalf("incorrect function returned, expected %s, got %s", expectedFn, fn.Name) 4061 } 4062 _, err = p.SetBreakpoint(0, pcs[0], proc.UserBreakpoint, nil) 4063 if err != nil { 4064 t.Fatalf("unable to set breakpoint: %v", err) 4065 } 4066 }) 4067 } 4068 4069 func TestDoubleInlineBreakpoint(t *testing.T) { 4070 // We should be able to set a breakpoint on an inlined function that 4071 // has been inlined within an inlined function. 4072 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 4073 // Versions of go before 1.10 do not have DWARF information for inlined calls 4074 t.Skip("inlining not supported") 4075 } 4076 withTestProcessArgs("doubleinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4077 fns, err := p.BinInfo().FindFunction("main.(*Rectangle).Height") 4078 if err != nil { 4079 t.Fatal(err) 4080 } 4081 if len(fns) != 1 { 4082 t.Fatalf("expected one function for Height, got %d", len(fns)) 4083 } 4084 if len(fns[0].InlinedCalls) != 1 { 4085 t.Fatalf("expected one inlined call for Height, got %d", len(fns[0].InlinedCalls)) 4086 } 4087 }) 4088 } 4089 4090 func TestIssue951(t *testing.T) { 4091 if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) { 4092 t.Skip("scopes not implemented in <=go1.8") 4093 } 4094 4095 withTestProcess("issue951", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4096 assertNoError(grp.Continue(), t, "Continue()") 4097 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 4098 assertNoError(err, t, "GoroutineScope") 4099 args, err := scope.FunctionArguments(normalLoadConfig) 4100 assertNoError(err, t, "FunctionArguments") 4101 t.Logf("%#v", args[0]) 4102 if args[0].Flags&proc.VariableShadowed == 0 { 4103 t.Error("argument is not shadowed") 4104 } 4105 vars, err := scope.LocalVariables(normalLoadConfig) 4106 assertNoError(err, t, "LocalVariables") 4107 shadowed, notShadowed := 0, 0 4108 for i := range vars { 4109 t.Logf("var %d: %#v\n", i, vars[i]) 4110 if vars[i].Flags&proc.VariableShadowed != 0 { 4111 shadowed++ 4112 } else { 4113 notShadowed++ 4114 } 4115 } 4116 if shadowed != 1 || notShadowed != 1 { 4117 t.Errorf("Wrong number of shadowed/non-shadowed local variables: %d %d", shadowed, notShadowed) 4118 } 4119 }) 4120 } 4121 4122 func TestDWZCompression(t *testing.T) { 4123 skipOn(t, "broken", "ppc64le") 4124 // If dwz is not available in the system, skip this test 4125 if _, err := exec.LookPath("dwz"); err != nil { 4126 t.Skip("dwz not installed") 4127 } 4128 4129 withTestProcessArgs("dwzcompression", t, ".", []string{}, protest.EnableDWZCompression, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4130 setFunctionBreakpoint(p, t, "C.fortytwo") 4131 assertNoError(grp.Continue(), t, "first Continue()") 4132 val := evalVariable(p, t, "stdin") 4133 if val.RealType == nil { 4134 t.Errorf("Can't find type for \"stdin\" global variable") 4135 } 4136 }) 4137 } 4138 4139 func TestMapLoadConfigWithReslice(t *testing.T) { 4140 // Check that load configuration is respected for resliced maps. 4141 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4142 zolotovLoadCfg := proc.LoadConfig{FollowPointers: true, MaxStructFields: -1, MaxVariableRecurse: 3, MaxStringLen: 10, MaxArrayValues: 10} 4143 assertNoError(grp.Continue(), t, "First Continue()") 4144 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 4145 assertNoError(err, t, "GoroutineScope") 4146 m1, err := scope.EvalExpression("m1", zolotovLoadCfg) 4147 assertNoError(err, t, "EvalVariable") 4148 t.Logf("m1 returned children %d (%d)", len(m1.Children)/2, m1.Len) 4149 4150 expr := fmt.Sprintf("(*(*%q)(%d))[10:]", m1.DwarfType.String(), m1.Addr) 4151 t.Logf("expr %q\n", expr) 4152 4153 m1cont, err := scope.EvalExpression(expr, zolotovLoadCfg) 4154 assertNoError(err, t, "EvalVariable") 4155 4156 t.Logf("m1cont returned children %d", len(m1cont.Children)/2) 4157 4158 if len(m1cont.Children) != 20 { 4159 t.Fatalf("wrong number of children returned %d\n", len(m1cont.Children)/2) 4160 } 4161 }) 4162 } 4163 4164 func TestStepOutReturn(t *testing.T) { 4165 ver, _ := goversion.Parse(runtime.Version()) 4166 if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) { 4167 t.Skip("return variables aren't marked on 1.9 or earlier") 4168 } 4169 withTestProcess("stepoutret", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4170 setFunctionBreakpoint(p, t, "main.stepout") 4171 assertNoError(grp.Continue(), t, "Continue") 4172 assertNoError(grp.StepOut(), t, "StepOut") 4173 ret := p.CurrentThread().Common().ReturnValues(normalLoadConfig) 4174 if len(ret) != 2 { 4175 t.Fatalf("wrong number of return values %v", ret) 4176 } 4177 4178 stridx := 0 4179 numidx := 1 4180 4181 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 12) { 4182 // in 1.11 and earlier the order of return values in DWARF is 4183 // unspecified, in 1.11 and later it follows the order of definition 4184 // specified by the user 4185 for i := range ret { 4186 if ret[i].Name == "str" { 4187 stridx = i 4188 numidx = 1 - i 4189 break 4190 } 4191 } 4192 } 4193 4194 if ret[stridx].Name != "str" { 4195 t.Fatalf("(str) bad return value name %s", ret[stridx].Name) 4196 } 4197 if ret[stridx].Kind != reflect.String { 4198 t.Fatalf("(str) bad return value kind %v", ret[stridx].Kind) 4199 } 4200 if s := constant.StringVal(ret[stridx].Value); s != "return 47" { 4201 t.Fatalf("(str) bad return value %q", s) 4202 } 4203 4204 if ret[numidx].Name != "num" { 4205 t.Fatalf("(num) bad return value name %s", ret[numidx].Name) 4206 } 4207 if ret[numidx].Kind != reflect.Int { 4208 t.Fatalf("(num) bad return value kind %v", ret[numidx].Kind) 4209 } 4210 if n, _ := constant.Int64Val(ret[numidx].Value); n != 48 { 4211 t.Fatalf("(num) bad return value %d", n) 4212 } 4213 }) 4214 } 4215 4216 func TestOptimizationCheck(t *testing.T) { 4217 withTestProcess("continuetestprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4218 fn := p.BinInfo().LookupFunc()["main.main"][0] 4219 if fn.Optimized() { 4220 t.Fatalf("main.main is optimized") 4221 } 4222 }) 4223 4224 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) { 4225 withTestProcessArgs("continuetestprog", t, ".", []string{}, protest.EnableOptimization|protest.EnableInlining, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4226 fn := p.BinInfo().LookupFunc()["main.main"][0] 4227 if !fn.Optimized() { 4228 t.Fatalf("main.main is not optimized") 4229 } 4230 }) 4231 } 4232 } 4233 4234 func TestIssue1264(t *testing.T) { 4235 // It should be possible to set a breakpoint condition that consists only 4236 // of evaluating a single boolean variable. 4237 withTestProcess("issue1264", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4238 bp := setFileBreakpoint(p, t, fixture.Source, 8) 4239 bp.UserBreaklet().Cond = &ast.Ident{Name: "equalsTwo"} 4240 assertNoError(grp.Continue(), t, "Continue()") 4241 assertLineNumber(p, t, 8, "after continue") 4242 }) 4243 } 4244 4245 func TestReadDefer(t *testing.T) { 4246 withTestProcess("deferstack", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4247 assertNoError(grp.Continue(), t, "Continue") 4248 frames, err := proc.GoroutineStacktrace(p, p.SelectedGoroutine(), 10, proc.StacktraceReadDefers) 4249 assertNoError(err, t, "Stacktrace") 4250 4251 logStacktrace(t, p, frames) 4252 4253 examples := []struct { 4254 frameIdx int 4255 topmostDefer string 4256 defers []string 4257 }{ 4258 // main.call3 (defers nothing, topmost defer main.f2) 4259 {0, "main.f2", []string{}}, 4260 4261 // main.call2 (defers main.f2, main.f3, topmost defer main.f2) 4262 {1, "main.f2", []string{"main.f2", "main.f3"}}, 4263 4264 // main.call1 (defers main.f1, main.f2, topmost defer main.f1) 4265 {2, "main.f1", []string{"main.f1", "main.f2"}}, 4266 4267 // main.main (defers nothing) 4268 {3, "", []string{}}} 4269 4270 defercheck := func(d *proc.Defer, deferName, tgt string, frameIdx int) { 4271 if d == nil { 4272 t.Fatalf("expected %q as %s of frame %d, got nothing", tgt, deferName, frameIdx) 4273 } 4274 if d.Unreadable != nil { 4275 t.Fatalf("expected %q as %s of frame %d, got unreadable defer: %v", tgt, deferName, frameIdx, d.Unreadable) 4276 } 4277 _, _, dfn := d.DeferredFunc(p) 4278 if dfn == nil { 4279 t.Fatalf("expected %q as %s of frame %d, got %#x", tgt, deferName, frameIdx, d.DwrapPC) 4280 } 4281 if dfn.Name != tgt { 4282 t.Fatalf("expected %q as %s of frame %d, got %q", tgt, deferName, frameIdx, dfn.Name) 4283 } 4284 } 4285 4286 for _, example := range examples { 4287 frame := &frames[example.frameIdx] 4288 4289 if example.topmostDefer != "" { 4290 defercheck(frame.TopmostDefer, "topmost defer", example.topmostDefer, example.frameIdx) 4291 } 4292 4293 if len(example.defers) != len(frames[example.frameIdx].Defers) { 4294 t.Fatalf("expected %d defers for %d, got %v", len(example.defers), example.frameIdx, frame.Defers) 4295 } 4296 4297 for deferIdx := range example.defers { 4298 defercheck(frame.Defers[deferIdx], fmt.Sprintf("defer %d", deferIdx), example.defers[deferIdx], example.frameIdx) 4299 } 4300 } 4301 }) 4302 } 4303 4304 func TestReadDeferArgs(t *testing.T) { 4305 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) { 4306 // When regabi is enabled in Go 1.17 and later, reading arguments of 4307 // deferred functions becomes significantly more complicated because of 4308 // the autogenerated code used to unpack the argument frame stored in 4309 // runtime._defer into registers. 4310 // We either need to know how to do the translation, implementing the ABI1 4311 // rules in Delve, or have some assistance from the compiler (for example 4312 // have the dwrap function contain entries for each of the captured 4313 // variables with a location describing their offset from DX). 4314 // Ultimately this feature is unimportant enough that we can leave it 4315 // disabled for now. 4316 t.Skip("unsupported") 4317 } 4318 var tests = []struct { 4319 frame, deferCall int 4320 a, b int64 4321 }{ 4322 {1, 1, 42, 61}, 4323 {2, 2, 1, -1}, 4324 } 4325 4326 withTestProcess("deferstack", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4327 assertNoError(grp.Continue(), t, "Continue()") 4328 4329 for _, test := range tests { 4330 scope, err := proc.ConvertEvalScope(p, -1, test.frame, test.deferCall) 4331 assertNoError(err, t, fmt.Sprintf("ConvertEvalScope(-1, %d, %d)", test.frame, test.deferCall)) 4332 4333 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) { 4334 // In Go 1.17 deferred function calls can end up inside a wrapper, and 4335 // the scope for this evaluation needs to be the wrapper. 4336 if scope.Fn.Name != "main.f2" { 4337 t.Fatalf("expected function \"main.f2\" got %q", scope.Fn.Name) 4338 } 4339 } 4340 4341 avar, err := scope.EvalExpression("a", normalLoadConfig) 4342 if err != nil { 4343 t.Fatal(err) 4344 } 4345 bvar, err := scope.EvalExpression("b", normalLoadConfig) 4346 if err != nil { 4347 t.Fatal(err) 4348 } 4349 4350 a, _ := constant.Int64Val(avar.Value) 4351 b, _ := constant.Int64Val(bvar.Value) 4352 4353 if a != test.a { 4354 t.Errorf("value of argument 'a' at frame %d, deferred call %d: %d (expected %d)", test.frame, test.deferCall, a, test.a) 4355 } 4356 4357 if b != test.b { 4358 t.Errorf("value of argument 'b' at frame %d, deferred call %d: %d (expected %d)", test.frame, test.deferCall, b, test.b) 4359 } 4360 } 4361 }) 4362 } 4363 4364 func TestIssue1374(t *testing.T) { 4365 // Continue did not work when stopped at a breakpoint immediately after calling CallFunction. 4366 skipOn(t, "broken - pie mode", "linux", "ppc64le", "native", "pie") 4367 4368 protest.MustSupportFunctionCalls(t, testBackend) 4369 withTestProcess("issue1374", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4370 setFileBreakpoint(p, t, fixture.Source, 7) 4371 assertNoError(grp.Continue(), t, "First Continue") 4372 assertLineNumber(p, t, 7, "Did not continue to correct location (first continue),") 4373 assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "getNum()", normalLoadConfig, true), t, "Call") 4374 err := grp.Continue() 4375 if _, isexited := err.(proc.ErrProcessExited); !isexited { 4376 regs, _ := p.CurrentThread().Registers() 4377 f, l, _ := p.BinInfo().PCToLine(regs.PC()) 4378 t.Fatalf("expected process exited error got %v at %s:%d", err, f, l) 4379 } 4380 }) 4381 } 4382 4383 func TestIssue1432(t *testing.T) { 4384 // Check that taking the address of a struct, casting it into a pointer to 4385 // the struct's type and then accessing a member field will still: 4386 // - perform auto-dereferencing on struct member access 4387 // - yield a Variable that's ultimately assignable (i.e. has an address) 4388 withTestProcess("issue1432", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4389 assertNoError(grp.Continue(), t, "Continue") 4390 svar := evalVariable(p, t, "s") 4391 t.Logf("%#x", svar.Addr) 4392 4393 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 4394 assertNoError(err, t, "GoroutineScope()") 4395 4396 err = scope.SetVariable(fmt.Sprintf("(*\"main.s\")(%#x).i", svar.Addr), "10") 4397 assertNoError(err, t, "SetVariable") 4398 }) 4399 } 4400 4401 func TestGoroutinesInfoLimit(t *testing.T) { 4402 withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4403 setFileBreakpoint(p, t, fixture.Source, 37) 4404 assertNoError(grp.Continue(), t, "Continue()") 4405 4406 gcount := 0 4407 nextg := 0 4408 const goroutinesInfoLimit = 10 4409 for nextg >= 0 { 4410 oldnextg := nextg 4411 var gs []*proc.G 4412 var err error 4413 gs, nextg, err = proc.GoroutinesInfo(p, nextg, goroutinesInfoLimit) 4414 assertNoError(err, t, fmt.Sprintf("GoroutinesInfo(%d, %d)", oldnextg, goroutinesInfoLimit)) 4415 gcount += len(gs) 4416 t.Logf("got %d goroutines\n", len(gs)) 4417 } 4418 4419 t.Logf("number of goroutines: %d\n", gcount) 4420 4421 gs, _, err := proc.GoroutinesInfo(p, 0, 0) 4422 assertNoError(err, t, "GoroutinesInfo(0, 0)") 4423 t.Logf("number of goroutines (full scan): %d\n", gcount) 4424 if len(gs) != gcount { 4425 t.Fatalf("mismatch in the number of goroutines %d %d\n", gcount, len(gs)) 4426 } 4427 }) 4428 } 4429 4430 func TestIssue1469(t *testing.T) { 4431 withTestProcess("issue1469", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4432 setFileBreakpoint(p, t, fixture.Source, 13) 4433 assertNoError(grp.Continue(), t, "Continue()") 4434 4435 gid2thread := make(map[int64][]proc.Thread) 4436 for _, thread := range p.ThreadList() { 4437 g, _ := proc.GetG(thread) 4438 if g == nil { 4439 continue 4440 } 4441 gid2thread[g.ID] = append(gid2thread[g.ID], thread) 4442 } 4443 4444 for gid := range gid2thread { 4445 if len(gid2thread[gid]) > 1 { 4446 t.Logf("too many threads running goroutine %d", gid) 4447 for _, thread := range gid2thread[gid] { 4448 t.Logf("\tThread %d", thread.ThreadID()) 4449 frames, err := proc.ThreadStacktrace(p, thread, 20) 4450 if err != nil { 4451 t.Logf("\t\tcould not get stacktrace %v", err) 4452 } 4453 for _, frame := range frames { 4454 t.Logf("\t\t%#x at %s:%d (systemstack: %v)", frame.Call.PC, frame.Call.File, frame.Call.Line, frame.SystemStack) 4455 } 4456 } 4457 } 4458 } 4459 }) 4460 } 4461 4462 func TestDeadlockBreakpoint(t *testing.T) { 4463 skipOn(t, "upstream issue - https://github.com/golang/go/issues/29322", "pie") 4464 deadlockBp := proc.FatalThrow 4465 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) { 4466 deadlockBp = proc.UnrecoveredPanic 4467 } 4468 withTestProcess("testdeadlock", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4469 assertNoError(grp.Continue(), t, "Continue()") 4470 4471 bp := p.CurrentThread().Breakpoint() 4472 if bp.Breakpoint == nil || bp.Logical.Name != deadlockBp { 4473 t.Fatalf("did not stop at deadlock breakpoint %v", bp) 4474 } 4475 }) 4476 } 4477 4478 func findSource(source string, sources []string) bool { 4479 for _, s := range sources { 4480 if s == source { 4481 return true 4482 } 4483 } 4484 return false 4485 } 4486 4487 func TestListImages(t *testing.T) { 4488 protest.MustHaveCgo(t) 4489 pluginFixtures := protest.WithPlugins(t, protest.AllNonOptimized, "plugin1/", "plugin2/") 4490 4491 withTestProcessArgs("plugintest", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, protest.AllNonOptimized, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4492 if !findSource(fixture.Source, p.BinInfo().Sources) { 4493 t.Fatalf("could not find %s in sources: %q\n", fixture.Source, p.BinInfo().Sources) 4494 } 4495 4496 assertNoError(grp.Continue(), t, "first continue") 4497 f, l := currentLineNumber(p, t) 4498 plugin1Found := false 4499 t.Logf("Libraries before %s:%d:", f, l) 4500 for _, image := range p.BinInfo().Images { 4501 t.Logf("\t%#x %q err:%v", image.StaticBase, image.Path, image.LoadError()) 4502 if image.Path == pluginFixtures[0].Path { 4503 plugin1Found = true 4504 } 4505 } 4506 if !plugin1Found { 4507 t.Fatalf("Could not find plugin1") 4508 } 4509 if !findSource(fixture.Source, p.BinInfo().Sources) { 4510 // Source files for the base program must be available even after a plugin is loaded. Issue #2074. 4511 t.Fatalf("could not find %s in sources (after loading plugin): %q\n", fixture.Source, p.BinInfo().Sources) 4512 } 4513 assertNoError(grp.Continue(), t, "second continue") 4514 f, l = currentLineNumber(p, t) 4515 plugin1Found, plugin2Found := false, false 4516 t.Logf("Libraries after %s:%d:", f, l) 4517 for _, image := range p.BinInfo().Images { 4518 t.Logf("\t%#x %q err:%v", image.StaticBase, image.Path, image.LoadError()) 4519 switch image.Path { 4520 case pluginFixtures[0].Path: 4521 plugin1Found = true 4522 case pluginFixtures[1].Path: 4523 plugin2Found = true 4524 } 4525 } 4526 if !plugin1Found { 4527 t.Fatalf("Could not find plugin1") 4528 } 4529 if !plugin2Found { 4530 t.Fatalf("Could not find plugin2") 4531 } 4532 }) 4533 } 4534 4535 func TestAncestors(t *testing.T) { 4536 t.Setenv("GODEBUG", "tracebackancestors=100") 4537 withTestProcess("testnextprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4538 setFunctionBreakpoint(p, t, "main.testgoroutine") 4539 assertNoError(grp.Continue(), t, "Continue()") 4540 as, err := proc.Ancestors(p, p.SelectedGoroutine(), 1000) 4541 assertNoError(err, t, "Ancestors") 4542 t.Logf("ancestors: %#v\n", as) 4543 if len(as) != 1 { 4544 t.Fatalf("expected only one ancestor got %d", len(as)) 4545 } 4546 mainFound := false 4547 for i, a := range as { 4548 astack, err := a.Stack(100) 4549 assertNoError(err, t, fmt.Sprintf("Ancestor %d stack", i)) 4550 t.Logf("ancestor %d\n", i) 4551 logStacktrace(t, p, astack) 4552 for _, frame := range astack { 4553 if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.main" { 4554 mainFound = true 4555 } 4556 } 4557 } 4558 if !mainFound { 4559 t.Fatal("could not find main.main function in ancestors") 4560 } 4561 }) 4562 } 4563 4564 func testCallConcurrentCheckReturns(p *proc.Target, t *testing.T, gid1, gid2 int64) int { 4565 found := 0 4566 for _, thread := range p.ThreadList() { 4567 g, _ := proc.GetG(thread) 4568 if g == nil || (g.ID != gid1 && g.ID != gid2) { 4569 continue 4570 } 4571 retvals := thread.Common().ReturnValues(normalLoadConfig) 4572 if len(retvals) == 0 { 4573 continue 4574 } 4575 n, _ := constant.Int64Val(retvals[0].Value) 4576 t.Logf("injection on goroutine %d (thread %d) returned %v\n", g.ID, thread.ThreadID(), n) 4577 switch g.ID { 4578 case gid1: 4579 if n != 11 { 4580 t.Errorf("wrong return value for goroutine %d", g.ID) 4581 } 4582 found++ 4583 case gid2: 4584 if n != 12 { 4585 t.Errorf("wrong return value for goroutine %d", g.ID) 4586 } 4587 found++ 4588 } 4589 } 4590 return found 4591 } 4592 4593 func TestCallConcurrent(t *testing.T) { 4594 skipOn(t, "broken - pie mode", "linux", "ppc64le", "native", "pie") 4595 4596 protest.MustSupportFunctionCalls(t, testBackend) 4597 withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4598 bp := setFileBreakpoint(p, t, fixture.Source, 24) 4599 assertNoError(grp.Continue(), t, "Continue()") 4600 //_, err := p.ClearBreakpoint(bp.Addr) 4601 //assertNoError(err, t, "ClearBreakpoint() returned an error") 4602 4603 gid1 := p.SelectedGoroutine().ID 4604 t.Logf("starting injection in %d / %d", p.SelectedGoroutine().ID, p.CurrentThread().ThreadID()) 4605 assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "Foo(10, 1)", normalLoadConfig, false), t, "EvalExpressionWithCalls()") 4606 4607 returned := testCallConcurrentCheckReturns(p, t, gid1, -1) 4608 4609 curthread := p.CurrentThread() 4610 if curbp := curthread.Breakpoint(); curbp.Breakpoint == nil || curbp.LogicalID() != bp.LogicalID() || returned > 0 { 4611 t.Logf("skipping test, the call injection terminated before we hit a breakpoint in a different thread") 4612 return 4613 } 4614 4615 err := p.ClearBreakpoint(bp.Addr) 4616 assertNoError(err, t, "ClearBreakpoint() returned an error") 4617 4618 gid2 := p.SelectedGoroutine().ID 4619 t.Logf("starting second injection in %d / %d", p.SelectedGoroutine().ID, p.CurrentThread().ThreadID()) 4620 assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "Foo(10, 2)", normalLoadConfig, false), t, "EvalExpressionWithCalls") 4621 4622 for { 4623 returned += testCallConcurrentCheckReturns(p, t, gid1, gid2) 4624 if returned >= 2 { 4625 break 4626 } 4627 t.Logf("Continuing... %d", returned) 4628 assertNoError(grp.Continue(), t, "Continue()") 4629 } 4630 4631 grp.Continue() 4632 }) 4633 } 4634 4635 func TestPluginStepping(t *testing.T) { 4636 protest.MustHaveCgo(t) 4637 pluginFixtures := protest.WithPlugins(t, protest.AllNonOptimized, "plugin1/", "plugin2/") 4638 4639 testseq2Args(".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, protest.AllNonOptimized, t, "plugintest2", "", []seqTest{ 4640 {contContinue, 41}, 4641 {contStep, "plugin1.go:9"}, 4642 {contStep, "plugin1.go:10"}, 4643 {contStep, "plugin1.go:11"}, 4644 {contNext, "plugin1.go:12"}, 4645 {contNext, "plugintest2.go:41"}, 4646 {contNext, "plugintest2.go:42"}, 4647 {contStep, "plugin2.go:22"}, 4648 {contNext, "plugin2.go:23"}, 4649 {contNext, "plugin2.go:26"}, 4650 {contNext, "plugintest2.go:42"}}) 4651 } 4652 4653 func TestIssue1601(t *testing.T) { 4654 protest.MustHaveCgo(t) 4655 // Tests that recursive types involving C qualifiers and typedefs are parsed correctly 4656 withTestProcess("issue1601", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4657 assertNoError(grp.Continue(), t, "Continue") 4658 evalVariable(p, t, "C.globalq") 4659 }) 4660 } 4661 4662 func TestIssue1615(t *testing.T) { 4663 // A breakpoint condition that tests for string equality with a constant string shouldn't fail with 'string too long for comparison' error 4664 4665 withTestProcess("issue1615", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4666 bp := setFileBreakpoint(p, t, fixture.Source, 19) 4667 bp.UserBreaklet().Cond = &ast.BinaryExpr{ 4668 Op: token.EQL, 4669 X: &ast.Ident{Name: "s"}, 4670 Y: &ast.BasicLit{Kind: token.STRING, Value: `"projects/my-gcp-project-id-string/locations/us-central1/queues/my-task-queue-name"`}, 4671 } 4672 4673 assertNoError(grp.Continue(), t, "Continue") 4674 assertLineNumber(p, t, 19, "") 4675 }) 4676 } 4677 4678 func TestCgoStacktrace2(t *testing.T) { 4679 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 21) { 4680 skipOn(t, "upstream issue", "windows") 4681 skipOn(t, "broken", "arm64") 4682 } 4683 skipOn(t, "broken", "386") 4684 skipOn(t, "broken - cgo stacktraces", "darwin", "arm64") 4685 skipOn(t, "broken", "ppc64le") 4686 protest.MustHaveCgo(t) 4687 // If a panic happens during cgo execution the stacktrace should show the C 4688 // function that caused the problem. 4689 withTestProcess("cgosigsegvstack", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4690 err := grp.Continue() 4691 if _, exited := err.(proc.ErrProcessExited); exited { 4692 t.Fatal("process exited") 4693 } 4694 frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 100) 4695 assertNoError(err, t, "Stacktrace()") 4696 logStacktrace(t, p, frames) 4697 m := stacktraceCheck(t, []string{"C.sigsegv", "C.testfn", "main.main"}, frames) 4698 if m == nil { 4699 t.Fatal("see previous loglines") 4700 } 4701 }) 4702 } 4703 4704 func TestIssue1736(t *testing.T) { 4705 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4706 assertNoError(grp.Continue(), t, "Continue()") 4707 ch1BufVar := evalVariable(p, t, "*(ch1.buf)") 4708 q := fmt.Sprintf("*(*%q)(%d)", ch1BufVar.DwarfType.Common().Name, ch1BufVar.Addr) 4709 t.Logf("%s", q) 4710 ch1BufVar2 := evalVariable(p, t, q) 4711 if ch1BufVar2.Unreadable != nil { 4712 t.Fatal(ch1BufVar2.Unreadable) 4713 } 4714 }) 4715 } 4716 4717 func TestIssue1817(t *testing.T) { 4718 // Setting a breakpoint on a line that doesn't have any PC addresses marked 4719 // is_stmt should work. 4720 withTestProcess("issue1817", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4721 setFileBreakpoint(p, t, fixture.Source, 16) 4722 }) 4723 } 4724 4725 func TestListPackagesBuildInfo(t *testing.T) { 4726 withTestProcess("pkgrenames", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4727 pkgs := p.BinInfo().ListPackagesBuildInfo(true) 4728 t.Logf("returned %d", len(pkgs)) 4729 if len(pkgs) < 10 { 4730 t.Errorf("very few packages returned") 4731 } 4732 for _, pkg := range pkgs { 4733 t.Logf("%q %q", pkg.ImportPath, pkg.DirectoryPath) 4734 const _fixtures = "_fixtures" 4735 fidx := strings.Index(pkg.ImportPath, _fixtures) 4736 if fidx < 0 { 4737 continue 4738 } 4739 if !strings.HasSuffix(strings.ReplaceAll(pkg.DirectoryPath, "\\", "/"), pkg.ImportPath[fidx:]) { 4740 t.Errorf("unexpected suffix: %q %q", pkg.ImportPath, pkg.DirectoryPath) 4741 } 4742 } 4743 }) 4744 } 4745 4746 func TestIssue1795(t *testing.T) { 4747 // When doing midstack inlining the Go compiler sometimes (always?) emits 4748 // the toplevel inlined call with ranges that do not cover the inlining of 4749 // other nested inlined calls. 4750 // For example if a function A calls B which calls C and both the calls to 4751 // B and C are inlined the DW_AT_inlined_subroutine entry for A might have 4752 // ranges that do not cover the ranges of the inlined call to C. 4753 // This is probably a violation of the DWARF standard (it's unclear) but we 4754 // might as well support it as best as possible anyway. 4755 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) { 4756 t.Skip("Test not relevant to Go < 1.13") 4757 } 4758 skipOn(t, "broken", "ppc64le") 4759 withTestProcessArgs("issue1795", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4760 assertNoError(grp.Continue(), t, "Continue()") 4761 assertLineNumber(p, t, 12, "wrong line number after Continue,") 4762 assertNoError(grp.Next(), t, "Next()") 4763 assertLineNumber(p, t, 13, "wrong line number after Next,") 4764 }) 4765 withTestProcessArgs("issue1795", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4766 setFunctionBreakpoint(p, t, "regexp.(*Regexp).doExecute") 4767 assertNoError(grp.Continue(), t, "Continue()") 4768 assertLineNumber(p, t, 12, "wrong line number after Continue (1),") 4769 assertNoError(grp.Continue(), t, "Continue()") 4770 frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 40) 4771 assertNoError(err, t, "ThreadStacktrace()") 4772 logStacktrace(t, p, frames) 4773 if err := checkFrame(frames[0], "regexp.(*Regexp).doExecute", "", 0, false); err != nil { 4774 t.Errorf("Wrong frame 0: %v", err) 4775 } 4776 if err := checkFrame(frames[1], "regexp.(*Regexp).doMatch", "", 0, true); err != nil { 4777 t.Errorf("Wrong frame 1: %v", err) 4778 } 4779 if err := checkFrame(frames[2], "regexp.(*Regexp).MatchString", "", 0, true); err != nil { 4780 t.Errorf("Wrong frame 2: %v", err) 4781 } 4782 if err := checkFrame(frames[3], "main.main", fixture.Source, 12, false); err != nil { 4783 t.Errorf("Wrong frame 3: %v", err) 4784 } 4785 }) 4786 } 4787 4788 func BenchmarkConditionalBreakpoints(b *testing.B) { 4789 b.N = 1 4790 withTestProcess("issue1549", b, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4791 bp := setFileBreakpoint(p, b, fixture.Source, 12) 4792 bp.UserBreaklet().Cond = &ast.BinaryExpr{ 4793 Op: token.EQL, 4794 X: &ast.Ident{Name: "value"}, 4795 Y: &ast.BasicLit{Kind: token.INT, Value: "-1"}, 4796 } 4797 err := grp.Continue() 4798 if _, exited := err.(proc.ErrProcessExited); !exited { 4799 b.Fatalf("Unexpected error on Continue(): %v", err) 4800 } 4801 }) 4802 } 4803 4804 func TestBackwardNextGeneral(t *testing.T) { 4805 if testBackend != "rr" { 4806 t.Skip("Reverse stepping test needs rr") 4807 } 4808 testseq2(t, "testnextprog", "main.helloworld", []seqTest{ 4809 {contContinue, 13}, 4810 {contNext, 14}, 4811 {contReverseNext, 13}, 4812 {contReverseNext, 34}, 4813 {contReverseNext, 28}, 4814 {contReverseNext, 27}, 4815 {contReverseNext, 26}, 4816 {contReverseNext, 24}, 4817 {contReverseNext, 23}, 4818 {contReverseNext, 31}, 4819 {contReverseNext, 26}, 4820 {contReverseNext, 24}, 4821 {contReverseNext, 23}, 4822 {contReverseNext, 31}, 4823 {contReverseNext, 26}, 4824 {contReverseNext, 24}, 4825 {contReverseNext, 23}, 4826 {contReverseNext, 20}, 4827 {contReverseNext, 19}, 4828 {contReverseNext, 17}, 4829 {contReverseNext, 39}, 4830 {contReverseNext, 38}, 4831 {contReverseNext, 37}, 4832 }) 4833 } 4834 4835 func TestBackwardStepOutGeneral(t *testing.T) { 4836 if testBackend != "rr" { 4837 t.Skip("Reverse stepping test needs rr") 4838 } 4839 testseq2(t, "testnextprog", "main.helloworld", []seqTest{ 4840 {contContinue, 13}, 4841 {contNext, 14}, 4842 {contReverseStepout, 34}, 4843 {contReverseStepout, 39}, 4844 }) 4845 } 4846 4847 func TestBackwardStepGeneral(t *testing.T) { 4848 if testBackend != "rr" { 4849 t.Skip("Reverse stepping test needs rr") 4850 } 4851 testseq2(t, "testnextprog", "main.helloworld", []seqTest{ 4852 {contContinue, 13}, 4853 {contNext, 14}, 4854 {contReverseStep, 13}, 4855 {contReverseStep, 34}, 4856 {contReverseStep, 28}, 4857 {contReverseNext, 27}, // skip fmt.Printf 4858 {contReverseStep, 26}, 4859 {contReverseStep, 24}, 4860 {contReverseStep, 23}, 4861 {contReverseStep, 11}, 4862 {contReverseNext, 10}, // skip time.Sleep 4863 {contReverseStep, 9}, 4864 4865 {contReverseStep, 31}, 4866 {contReverseStep, 26}, 4867 {contReverseStep, 24}, 4868 {contReverseStep, 23}, 4869 {contReverseStep, 11}, 4870 {contReverseNext, 10}, // skip time.Sleep 4871 {contReverseStep, 9}, 4872 4873 {contReverseStep, 31}, 4874 {contReverseStep, 26}, 4875 {contReverseStep, 24}, 4876 {contReverseStep, 23}, 4877 {contReverseStep, 20}, 4878 {contReverseStep, 19}, 4879 {contReverseStep, 17}, 4880 {contReverseStep, 39}, 4881 {contReverseStep, 38}, 4882 {contReverseStep, 37}, 4883 }) 4884 } 4885 4886 func TestBackwardNextDeferPanic(t *testing.T) { 4887 if testBackend != "rr" { 4888 t.Skip("Reverse stepping test needs rr") 4889 } 4890 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) { 4891 testseq2(t, "defercall", "", []seqTest{ 4892 {contContinue, 12}, 4893 {contReverseNext, 11}, 4894 {contReverseNext, 10}, 4895 {contReverseNext, 9}, 4896 {contReverseNext, 27}, 4897 4898 {contContinueToBreakpoint, 12}, // skip first call to sampleFunction 4899 {contContinueToBreakpoint, 6}, // go to call to sampleFunction through deferreturn 4900 {contReverseNext, -1}, // runtime.deferreturn, maybe we should try to skip this 4901 {contReverseStepout, 13}, 4902 {contReverseNext, 12}, 4903 {contReverseNext, 11}, 4904 {contReverseNext, 10}, 4905 {contReverseNext, 9}, 4906 {contReverseNext, 27}, 4907 4908 {contContinueToBreakpoint, 18}, // go to panic call 4909 {contNext, 6}, // panic so the deferred call happens 4910 {contReverseNext, 18}, 4911 {contReverseNext, 17}, 4912 {contReverseNext, 16}, 4913 {contReverseNext, 15}, 4914 {contReverseNext, 23}, 4915 {contReverseNext, 22}, 4916 {contReverseNext, 21}, 4917 {contReverseNext, 28}, 4918 }) 4919 } else { 4920 testseq2(t, "defercall", "", []seqTest{ 4921 {contContinue, 12}, 4922 {contReverseNext, 11}, 4923 {contReverseNext, 10}, 4924 {contReverseNext, 9}, 4925 {contReverseNext, 27}, 4926 4927 {contContinueToBreakpoint, 12}, // skip first call to sampleFunction 4928 {contContinueToBreakpoint, 6}, // go to call to sampleFunction through deferreturn 4929 {contReverseNext, 13}, 4930 {contReverseNext, 12}, 4931 {contReverseNext, 11}, 4932 {contReverseNext, 10}, 4933 {contReverseNext, 9}, 4934 {contReverseNext, 27}, 4935 4936 {contContinueToBreakpoint, 18}, // go to panic call 4937 {contNext, 6}, // panic so the deferred call happens 4938 {contReverseNext, 18}, 4939 {contReverseNext, 17}, 4940 {contReverseNext, 16}, 4941 {contReverseNext, 15}, 4942 {contReverseNext, 23}, 4943 {contReverseNext, 22}, 4944 {contReverseNext, 21}, 4945 {contReverseNext, 28}, 4946 }) 4947 } 4948 } 4949 4950 func TestIssue1925(t *testing.T) { 4951 // Calling a function should not leave cached goroutine information in an 4952 // inconsistent state. 4953 // In particular the stepInstructionOut function called at the end of a 4954 // 'call' procedure should clean the G cache like every other function 4955 // altering the state of the target process. 4956 skipOn(t, "broken - pie mode", "linux", "ppc64le", "native", "pie") 4957 protest.MustSupportFunctionCalls(t, testBackend) 4958 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 4959 assertNoError(grp.Continue(), t, "Continue()") 4960 assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "afunc(2)", normalLoadConfig, true), t, "Call") 4961 t.Logf("%v\n", p.SelectedGoroutine().CurrentLoc) 4962 if loc := p.SelectedGoroutine().CurrentLoc; loc.File != fixture.Source { 4963 t.Errorf("wrong location for selected goroutine after call: %s:%d", loc.File, loc.Line) 4964 } 4965 }) 4966 } 4967 4968 func TestStepIntoWrapperForEmbeddedPointer(t *testing.T) { 4969 skipOn(t, "N/A", "linux", "386", "pie") // skipping wrappers doesn't work on linux/386/PIE due to the use of get_pc_thunk 4970 // Under some circumstances (when using an interface to call a method on an 4971 // embedded field, see _fixtures/ifaceembcall.go) the compiler will 4972 // autogenerate a wrapper function that uses a tail call (i.e. it ends in 4973 // an unconditional jump instruction to a different function). 4974 // Delve should be able to step into this tail call. 4975 testseq2(t, "ifaceembcall", "", []seqTest{ 4976 {contContinue, 28}, // main.main, the line calling iface.PtrReceiver() 4977 {contStep, 18}, // main.(*A).PtrReceiver 4978 {contStep, 19}, 4979 {contStepout, 28}, 4980 {contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver() 4981 {contStep, 22}, // main.(A).NonPtrReceiver 4982 {contStep, 23}, 4983 {contStepout, 29}}) 4984 4985 // same test but with next instead of stepout 4986 if goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) && runtime.GOARCH != "386" && !goversion.VersionAfterOrEqualRev(runtime.Version(), 1, 15, 4) { 4987 // Line numbers generated for versions 1.14 through 1.15.3 on any system except linux/386 4988 testseq2(t, "ifaceembcall", "", []seqTest{ 4989 {contContinue, 28}, // main.main, the line calling iface.PtrReceiver() 4990 {contStep, 18}, // main.(*A).PtrReceiver 4991 {contNext, 19}, 4992 {contNext, 19}, 4993 {contNext, 28}, 4994 {contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver() 4995 {contStep, 22}, 4996 {contNext, 23}, 4997 {contNext, 23}, 4998 {contNext, 29}}) 4999 } else { 5000 testseq2(t, "ifaceembcall", "", []seqTest{ 5001 {contContinue, 28}, // main.main, the line calling iface.PtrReceiver() 5002 {contStep, 18}, // main.(*A).PtrReceiver 5003 {contNext, 19}, 5004 {contNext, 28}, 5005 {contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver() 5006 {contStep, 22}, 5007 {contNext, 23}, 5008 {contNext, 29}}) 5009 5010 } 5011 } 5012 5013 func TestStepoutOneliner(t *testing.T) { 5014 // The heuristic detecting autogenerated wrappers when stepping out should 5015 // not skip oneliner functions. 5016 withTestProcess("issue2086", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5017 assertNoError(grp.Continue(), t, "Continue()") 5018 assertLineNumber(p, t, 15, "after first continue") 5019 assertNoError(grp.StepOut(), t, "StepOut()") 5020 if fn := p.BinInfo().PCToFunc(currentPC(p, t)); fn == nil || fn.Name != "main.T.m" { 5021 t.Fatalf("wrong function after stepout %#v", fn) 5022 } 5023 assertNoError(grp.StepOut(), t, "second StepOut()") 5024 if fn := p.BinInfo().PCToFunc(currentPC(p, t)); fn == nil || fn.Name != "main.main" { 5025 t.Fatalf("wrong fnuction after second stepout %#v", fn) 5026 } 5027 }) 5028 } 5029 5030 func TestRequestManualStopWhileStopped(t *testing.T) { 5031 // Requesting a manual stop while stopped shouldn't cause problems (issue #2138). 5032 withTestProcess("issue2138", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5033 resumed := make(chan struct{}) 5034 setFileBreakpoint(p, t, fixture.Source, 8) 5035 assertNoError(grp.Continue(), t, "Continue() 1") 5036 grp.ResumeNotify(resumed) 5037 go func() { 5038 <-resumed 5039 time.Sleep(1 * time.Second) 5040 grp.RequestManualStop() 5041 }() 5042 t.Logf("at time.Sleep call") 5043 assertNoError(grp.Continue(), t, "Continue() 2") 5044 t.Logf("manually stopped") 5045 grp.RequestManualStop() 5046 grp.RequestManualStop() 5047 grp.RequestManualStop() 5048 5049 resumed = make(chan struct{}) 5050 grp.ResumeNotify(resumed) 5051 go func() { 5052 <-resumed 5053 time.Sleep(1 * time.Second) 5054 grp.RequestManualStop() 5055 }() 5056 t.Logf("resuming sleep") 5057 assertNoError(grp.Continue(), t, "Continue() 3") 5058 t.Logf("done") 5059 }) 5060 } 5061 5062 func TestStepOutPreservesGoroutine(t *testing.T) { 5063 // Checks that StepOut preserves the currently selected goroutine. 5064 rand.Seed(time.Now().Unix()) 5065 withTestProcess("issue2113", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5066 assertNoError(grp.Continue(), t, "Continue()") 5067 5068 logState := func() { 5069 g := p.SelectedGoroutine() 5070 var goid int64 = -42 5071 if g != nil { 5072 goid = g.ID 5073 } 5074 pc := currentPC(p, t) 5075 f, l, fn := p.BinInfo().PCToLine(pc) 5076 var fnname = "???" 5077 if fn != nil { 5078 fnname = fn.Name 5079 } 5080 t.Logf("goroutine %d at %s:%d in %s", goid, f, l, fnname) 5081 } 5082 5083 logState() 5084 5085 gs, _, err := proc.GoroutinesInfo(p, 0, 0) 5086 assertNoError(err, t, "GoroutinesInfo") 5087 candg := []*proc.G{} 5088 bestg := []*proc.G{} 5089 for _, g := range gs { 5090 t.Logf("stacktracing goroutine %d (%v)\n", g.ID, g.CurrentLoc) 5091 frames, err := proc.GoroutineStacktrace(p, g, 20, 0) 5092 assertNoError(err, t, "Stacktrace") 5093 for _, frame := range frames { 5094 if frame.Call.Fn != nil && frame.Call.Fn.Name == "main.coroutine" { 5095 candg = append(candg, g) 5096 if g.Thread != nil && frames[0].Call.Fn != nil && strings.HasPrefix(frames[0].Call.Fn.Name, "runtime.") { 5097 bestg = append(bestg, g) 5098 } 5099 break 5100 } 5101 } 5102 } 5103 var pickg *proc.G 5104 if len(bestg) > 0 { 5105 pickg = bestg[rand.Intn(len(bestg))] 5106 t.Logf("selected goroutine %d (best)\n", pickg.ID) 5107 } else { 5108 pickg = candg[rand.Intn(len(candg))] 5109 t.Logf("selected goroutine %d\n", pickg.ID) 5110 5111 } 5112 goid := pickg.ID 5113 assertNoError(p.SwitchGoroutine(pickg), t, "SwitchGoroutine") 5114 5115 logState() 5116 5117 err = grp.StepOut() 5118 if err != nil { 5119 _, isexited := err.(proc.ErrProcessExited) 5120 if !isexited { 5121 assertNoError(err, t, "StepOut()") 5122 } else { 5123 return 5124 } 5125 } 5126 5127 logState() 5128 5129 g2 := p.SelectedGoroutine() 5130 if g2 == nil { 5131 t.Fatalf("no selected goroutine after stepout") 5132 } else if g2.ID != goid { 5133 t.Fatalf("unexpected selected goroutine %d", g2.ID) 5134 } 5135 }) 5136 } 5137 5138 func TestIssue2319(t *testing.T) { 5139 // Check to make sure we don't crash on startup when the target is 5140 // a binary with a mix of DWARF-5 C++ compilation units and 5141 // DWARF-4 Go compilation units. 5142 5143 // Require CGO, since we need to use the external linker for this test. 5144 protest.MustHaveCgo(t) 5145 5146 // The test fixture uses linux/amd64 assembly and a *.syso file 5147 // that is linux/amd64, so skip for other architectures. 5148 if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { 5149 t.Skipf("skipping since not linux/amd64") 5150 } 5151 5152 // Skip unless on 1.14 or later. The test fixture uses a *.syso 5153 // file, which in 1.13 is not loaded unless we're in internal 5154 // linking mode (we need external linking here). 5155 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) { 5156 t.Skip("test contains fixture that is specific to go 1.14+") 5157 } 5158 5159 fixture := protest.BuildFixture("issue2319/", protest.BuildModeExternalLinker) 5160 5161 // Load up the binary and make sure there are no crashes. 5162 bi := proc.NewBinaryInfo("linux", "amd64") 5163 assertNoError(bi.LoadBinaryInfo(fixture.Path, 0, nil), t, "LoadBinaryInfo") 5164 } 5165 5166 func TestDump(t *testing.T) { 5167 if (runtime.GOOS == "darwin" && testBackend == "native") || (runtime.GOOS == "windows" && runtime.GOARCH != "amd64") { 5168 t.Skip("not supported") 5169 } 5170 skipOn(t, "not implemented", "ppc64le") 5171 5172 convertRegisters := func(arch *proc.Arch, dregs op.DwarfRegisters) string { 5173 dregs.Reg(^uint64(0)) 5174 buf := new(bytes.Buffer) 5175 for i := 0; i < dregs.CurrentSize(); i++ { 5176 reg := dregs.Reg(uint64(i)) 5177 if reg == nil { 5178 continue 5179 } 5180 name, _, repr := arch.DwarfRegisterToString(i, reg) 5181 fmt.Fprintf(buf, " %s=%s", name, repr) 5182 } 5183 return buf.String() 5184 } 5185 5186 convertThread := func(thread proc.Thread) string { 5187 regs, err := thread.Registers() 5188 assertNoError(err, t, fmt.Sprintf("Thread registers %d", thread.ThreadID())) 5189 arch := thread.BinInfo().Arch 5190 dregs := arch.RegistersToDwarfRegisters(0, regs) 5191 return fmt.Sprintf("%08d %s", thread.ThreadID(), convertRegisters(arch, *dregs)) 5192 } 5193 5194 convertThreads := func(threads []proc.Thread) []string { 5195 r := make([]string, len(threads)) 5196 for i := range threads { 5197 r[i] = convertThread(threads[i]) 5198 } 5199 sort.Strings(r) 5200 return r 5201 } 5202 5203 convertGoroutine := func(g *proc.G) string { 5204 threadID := 0 5205 if g.Thread != nil { 5206 threadID = g.Thread.ThreadID() 5207 } 5208 return fmt.Sprintf("%d pc=%#x sp=%#x bp=%#x lr=%#x gopc=%#x startpc=%#x systemstack=%v thread=%d", g.ID, g.PC, g.SP, g.BP, g.LR, g.GoPC, g.StartPC, g.SystemStack, threadID) 5209 } 5210 5211 convertFrame := func(arch *proc.Arch, frame *proc.Stackframe) string { 5212 return fmt.Sprintf("currentPC=%#x callPC=%#x frameOff=%#x\n", frame.Current.PC, frame.Call.PC, frame.FrameOffset()) 5213 } 5214 5215 makeDump := func(p *proc.Target, corePath, exePath string, flags proc.DumpFlags) *proc.Target { 5216 fh, err := os.Create(corePath) 5217 assertNoError(err, t, "Create()") 5218 var state proc.DumpState 5219 p.Dump(fh, flags, &state) 5220 assertNoError(state.Err, t, "Dump()") 5221 if state.ThreadsDone != state.ThreadsTotal || state.MemDone != state.MemTotal || !state.AllDone || state.Dumping || state.Canceled { 5222 t.Fatalf("bad DumpState %#v", &state) 5223 } 5224 c, err := core.OpenCore(corePath, exePath, nil) 5225 assertNoError(err, t, "OpenCore()") 5226 return c.Selected 5227 } 5228 5229 testDump := func(p, c *proc.Target) { 5230 if p.Pid() != c.Pid() { 5231 t.Errorf("Pid mismatch %x %x", p.Pid(), c.Pid()) 5232 } 5233 5234 threads := convertThreads(p.ThreadList()) 5235 cthreads := convertThreads(c.ThreadList()) 5236 5237 if len(threads) != len(cthreads) { 5238 t.Errorf("Thread number mismatch %d %d", len(threads), len(cthreads)) 5239 } 5240 5241 for i := range threads { 5242 if threads[i] != cthreads[i] { 5243 t.Errorf("Thread mismatch\nlive:\t%s\ncore:\t%s", threads[i], cthreads[i]) 5244 } 5245 } 5246 5247 gos, _, err := proc.GoroutinesInfo(p, 0, 0) 5248 assertNoError(err, t, "GoroutinesInfo() - live process") 5249 cgos, _, err := proc.GoroutinesInfo(c, 0, 0) 5250 assertNoError(err, t, "GoroutinesInfo() - core dump") 5251 5252 if len(gos) != len(cgos) { 5253 t.Errorf("Goroutine number mismatch %d %d", len(gos), len(cgos)) 5254 } 5255 5256 var scope, cscope *proc.EvalScope 5257 5258 for i := range gos { 5259 if convertGoroutine(gos[i]) != convertGoroutine(cgos[i]) { 5260 t.Errorf("Goroutine mismatch\nlive:\t%s\ncore:\t%s", convertGoroutine(gos[i]), convertGoroutine(cgos[i])) 5261 } 5262 5263 frames, err := proc.GoroutineStacktrace(p, gos[i], 20, 0) 5264 assertNoError(err, t, fmt.Sprintf("Stacktrace for goroutine %d - live process", gos[i].ID)) 5265 cframes, err := proc.GoroutineStacktrace(c, cgos[i], 20, 0) 5266 assertNoError(err, t, fmt.Sprintf("Stacktrace for goroutine %d - core dump", gos[i].ID)) 5267 5268 if len(frames) != len(cframes) { 5269 t.Errorf("Frame number mismatch for goroutine %d: %d %d", gos[i].ID, len(frames), len(cframes)) 5270 } 5271 5272 for j := range frames { 5273 if convertFrame(p.BinInfo().Arch, &frames[j]) != convertFrame(p.BinInfo().Arch, &cframes[j]) { 5274 t.Errorf("Frame mismatch %d.%d\nlive:\t%s\ncore:\t%s", gos[i].ID, j, convertFrame(p.BinInfo().Arch, &frames[j]), convertFrame(p.BinInfo().Arch, &cframes[j])) 5275 } 5276 if frames[j].Call.Fn != nil && frames[j].Call.Fn.Name == "main.main" { 5277 scope = proc.FrameToScope(p, p.Memory(), gos[i], 0, frames[j:]...) 5278 cscope = proc.FrameToScope(c, c.Memory(), cgos[i], 0, cframes[j:]...) 5279 } 5280 } 5281 } 5282 5283 vars, err := scope.LocalVariables(normalLoadConfig) 5284 assertNoError(err, t, "LocalVariables - live process") 5285 cvars, err := cscope.LocalVariables(normalLoadConfig) 5286 assertNoError(err, t, "LocalVariables - core dump") 5287 5288 if len(vars) != len(cvars) { 5289 t.Errorf("Variable number mismatch %d %d", len(vars), len(cvars)) 5290 } 5291 5292 for i := range vars { 5293 varstr := vars[i].Name + "=" + api.ConvertVar(vars[i]).SinglelineString() 5294 cvarstr := cvars[i].Name + "=" + api.ConvertVar(cvars[i]).SinglelineString() 5295 if strings.Contains(varstr, "(unreadable") { 5296 // errors reading from unmapped memory differ between live process and core 5297 continue 5298 } 5299 if varstr != cvarstr { 5300 t.Errorf("Variable mismatch %s %s", varstr, cvarstr) 5301 } 5302 } 5303 } 5304 5305 withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5306 assertNoError(grp.Continue(), t, "Continue()") 5307 corePath := filepath.Join(fixture.BuildDir, "coredump") 5308 corePathPlatIndep := filepath.Join(fixture.BuildDir, "coredump-indep") 5309 5310 t.Logf("testing normal dump") 5311 5312 c := makeDump(p, corePath, fixture.Path, 0) 5313 defer os.Remove(corePath) 5314 testDump(p, c) 5315 5316 if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" { 5317 // No reason to do this test on other goos/goarch because they use the 5318 // platform-independent format anyway. 5319 t.Logf("testing platform-independent dump") 5320 c2 := makeDump(p, corePathPlatIndep, fixture.Path, proc.DumpPlatformIndependent) 5321 defer os.Remove(corePathPlatIndep) 5322 testDump(p, c2) 5323 } 5324 }) 5325 } 5326 5327 func TestCompositeMemoryWrite(t *testing.T) { 5328 if runtime.GOARCH != "amd64" { 5329 t.Skip("only valid on amd64") 5330 } 5331 skipOn(t, "not implemented", "freebsd") 5332 withTestProcess("fputest/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5333 getregs := func() (pc, rax, xmm1 uint64) { 5334 regs, err := p.CurrentThread().Registers() 5335 assertNoError(err, t, "Registers") 5336 fmtregs, err := regs.Slice(true) 5337 assertNoError(err, t, "register slice") 5338 5339 var xmm1buf []byte 5340 5341 for _, reg := range fmtregs { 5342 switch strings.ToLower(reg.Name) { 5343 case "rax": 5344 rax = reg.Reg.Uint64Val 5345 case "xmm1": 5346 xmm1buf = reg.Reg.Bytes 5347 } 5348 } 5349 5350 xmm1 = binary.LittleEndian.Uint64(xmm1buf[:8]) 5351 5352 return regs.PC(), rax, xmm1 5353 } 5354 5355 const fakeAddress = 0xbeef0000 5356 5357 getmem := func(mem proc.MemoryReader) uint64 { 5358 buf := make([]byte, 8) 5359 _, err := mem.ReadMemory(buf, fakeAddress) 5360 assertNoError(err, t, "ReadMemory") 5361 return binary.LittleEndian.Uint64(buf) 5362 } 5363 5364 assertNoError(grp.Continue(), t, "Continue()") 5365 oldPc, oldRax, oldXmm1 := getregs() 5366 t.Logf("PC %#x AX %#x XMM1 %#x", oldPc, oldRax, oldXmm1) 5367 5368 memRax, err := proc.NewCompositeMemory(p, []op.Piece{{Size: 0, Val: 0, Kind: op.RegPiece}}, fakeAddress) 5369 assertNoError(err, t, "NewCompositeMemory (rax)") 5370 memXmm1, err := proc.NewCompositeMemory(p, []op.Piece{{Size: 0, Val: 18, Kind: op.RegPiece}}, fakeAddress) 5371 assertNoError(err, t, "NewCompositeMemory (xmm1)") 5372 5373 if memRax := getmem(memRax); memRax != oldRax { 5374 t.Errorf("reading rax memory, expected %#x got %#x", oldRax, memRax) 5375 } 5376 if memXmm1 := getmem(memXmm1); memXmm1 != oldXmm1 { 5377 t.Errorf("reading xmm1 memory, expected %#x got %#x", oldXmm1, memXmm1) 5378 } 5379 5380 _, err = memRax.WriteMemory(0xbeef0000, []byte{0xef, 0xbe, 0x0d, 0xf0, 0xef, 0xbe, 0x0d, 0xf0}) 5381 assertNoError(err, t, "WriteMemory (rax)") 5382 _, err = memXmm1.WriteMemory(0xbeef0000, []byte{0xef, 0xbe, 0x0d, 0xf0, 0xef, 0xbe, 0x0d, 0xf0}) 5383 assertNoError(err, t, "WriteMemory (xmm1)") 5384 5385 newPc, newRax, newXmm1 := getregs() 5386 t.Logf("PC %#x AX %#x XMM1 %#x", newPc, newRax, newXmm1) 5387 5388 const tgt = 0xf00dbeeff00dbeef 5389 if newRax != tgt { 5390 t.Errorf("reading rax register, expected %#x, got %#x", uint64(tgt), newRax) 5391 } 5392 if newXmm1 != tgt { 5393 t.Errorf("reading xmm1 register, expected %#x, got %#x", uint64(tgt), newXmm1) 5394 } 5395 }) 5396 } 5397 5398 func TestVariablesWithExternalLinking(t *testing.T) { 5399 protest.MustHaveCgo(t) 5400 // Tests that macOSDebugFrameBugWorkaround works. 5401 // See: 5402 // https://github.com/golang/go/issues/25841 5403 // https://gitlab.com/Raven-IO/raven-delve/issues/2346 5404 withTestProcessArgs("testvariables2", t, ".", []string{}, protest.BuildModeExternalLinker, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5405 assertNoError(grp.Continue(), t, "Continue()") 5406 str1Var := evalVariable(p, t, "str1") 5407 if str1Var.Unreadable != nil { 5408 t.Fatalf("variable str1 is unreadable: %v", str1Var.Unreadable) 5409 } 5410 t.Logf("%#v", str1Var) 5411 if constant.StringVal(str1Var.Value) != "01234567890" { 5412 t.Fatalf("wrong value for str1: %v", str1Var.Value) 5413 } 5414 }) 5415 } 5416 5417 func TestWatchpointsBasic(t *testing.T) { 5418 skipOn(t, "not implemented", "freebsd") 5419 skipOn(t, "not implemented", "386") 5420 skipOn(t, "not implemented", "ppc64le") 5421 skipOn(t, "see https://gitlab.com/Raven-IO/raven-delve/issues/2768", "windows") 5422 protest.AllowRecording(t) 5423 5424 position1 := 19 5425 position5 := 41 5426 5427 if runtime.GOARCH == "arm64" { 5428 position1 = 18 5429 position5 = 40 5430 } 5431 5432 withTestProcess("databpeasy", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5433 setFunctionBreakpoint(p, t, "main.main") 5434 setFileBreakpoint(p, t, fixture.Source, 21) // Position 2 breakpoint 5435 setFileBreakpoint(p, t, fixture.Source, 27) // Position 4 breakpoint 5436 assertNoError(grp.Continue(), t, "Continue 0") 5437 assertLineNumber(p, t, 13, "Continue 0") // Position 0 5438 5439 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 5440 assertNoError(err, t, "GoroutineScope") 5441 5442 bp, err := p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite, nil) 5443 assertNoError(err, t, "SetDataBreakpoint(write-only)") 5444 5445 assertNoError(grp.Continue(), t, "Continue 1") 5446 assertLineNumber(p, t, position1, "Continue 1") // Position 1 5447 5448 if curbp := p.CurrentThread().Breakpoint().Breakpoint; curbp == nil || (curbp.LogicalID() != bp.LogicalID()) { 5449 t.Fatal("breakpoint not set") 5450 } 5451 5452 assertNoError(p.ClearBreakpoint(bp.Addr), t, "ClearBreakpoint") 5453 5454 assertNoError(grp.Continue(), t, "Continue 2") 5455 assertLineNumber(p, t, 21, "Continue 2") // Position 2 5456 5457 _, err = p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite|proc.WatchRead, nil) 5458 assertNoError(err, t, "SetDataBreakpoint(read-write)") 5459 5460 assertNoError(grp.Continue(), t, "Continue 3") 5461 assertLineNumber(p, t, 22, "Continue 3") // Position 3 5462 5463 p.ClearBreakpoint(bp.Addr) 5464 5465 assertNoError(grp.Continue(), t, "Continue 4") 5466 assertLineNumber(p, t, 27, "Continue 4") // Position 4 5467 5468 t.Logf("setting final breakpoint") 5469 _, err = p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite, nil) 5470 assertNoError(err, t, "SetDataBreakpoint(write-only, again)") 5471 5472 assertNoError(grp.Continue(), t, "Continue 5") 5473 assertLineNumber(p, t, position5, "Continue 5") // Position 5 5474 }) 5475 } 5476 5477 func TestWatchpointCounts(t *testing.T) { 5478 skipOn(t, "not implemented", "freebsd") 5479 skipOn(t, "not implemented", "386") 5480 skipOn(t, "see https://gitlab.com/Raven-IO/raven-delve/issues/2768", "windows") 5481 skipOn(t, "not implemented", "ppc64le") 5482 protest.AllowRecording(t) 5483 5484 withTestProcess("databpcountstest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5485 setFunctionBreakpoint(p, t, "main.main") 5486 assertNoError(grp.Continue(), t, "Continue 0") 5487 5488 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 5489 assertNoError(err, t, "GoroutineScope") 5490 5491 bp, err := p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite, nil) 5492 assertNoError(err, t, "SetWatchpoint(write-only)") 5493 5494 for { 5495 if err := grp.Continue(); err != nil { 5496 if _, exited := err.(proc.ErrProcessExited); exited { 5497 break 5498 } 5499 assertNoError(err, t, "Continue()") 5500 } 5501 } 5502 5503 t.Logf("TotalHitCount: %d", bp.Logical.TotalHitCount) 5504 if bp.Logical.TotalHitCount != 200 { 5505 t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.Logical.TotalHitCount) 5506 } 5507 5508 if len(bp.Logical.HitCount) != 2 { 5509 t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.Logical.HitCount)) 5510 } 5511 5512 for _, v := range bp.Logical.HitCount { 5513 if v != 100 { 5514 t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.Logical.HitCount) 5515 } 5516 } 5517 }) 5518 } 5519 5520 func TestManualStopWhileStopped(t *testing.T) { 5521 // Checks that RequestManualStop sent to a stopped thread does not cause the target process to die. 5522 withTestProcess("loopprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5523 asyncCont := func(done chan struct{}) { 5524 defer close(done) 5525 err := grp.Continue() 5526 t.Logf("%v\n", err) 5527 if err != nil { 5528 panic(err) 5529 } 5530 for _, th := range p.ThreadList() { 5531 if th.Breakpoint().Breakpoint != nil { 5532 t.Logf("unexpected stop at breakpoint: %v", th.Breakpoint().Breakpoint) 5533 panic("unexpected stop at breakpoint") 5534 } 5535 } 5536 } 5537 5538 const ( 5539 repeatsSlow = 3 5540 repeatsFast = 5 5541 ) 5542 5543 for i := 0; i < repeatsSlow; i++ { 5544 t.Logf("Continue %d (slow)", i) 5545 done := make(chan struct{}) 5546 go asyncCont(done) 5547 time.Sleep(1 * time.Second) 5548 grp.RequestManualStop() 5549 time.Sleep(1 * time.Second) 5550 grp.RequestManualStop() 5551 time.Sleep(1 * time.Second) 5552 <-done 5553 } 5554 for i := 0; i < repeatsFast; i++ { 5555 t.Logf("Continue %d (fast)", i) 5556 rch := make(chan struct{}) 5557 done := make(chan struct{}) 5558 grp.ResumeNotify(rch) 5559 go asyncCont(done) 5560 <-rch 5561 grp.RequestManualStop() 5562 grp.RequestManualStop() 5563 <-done 5564 } 5565 }) 5566 } 5567 5568 func TestDwrapStartLocation(t *testing.T) { 5569 // Tests that the start location of a goroutine is unwrapped in Go 1.17 and later. 5570 withTestProcess("goroutinestackprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5571 setFunctionBreakpoint(p, t, "main.stacktraceme") 5572 assertNoError(grp.Continue(), t, "Continue()") 5573 gs, _, err := proc.GoroutinesInfo(p, 0, 0) 5574 assertNoError(err, t, "GoroutinesInfo") 5575 found := false 5576 for _, g := range gs { 5577 startLoc := g.StartLoc(p) 5578 if startLoc.Fn == nil { 5579 continue 5580 } 5581 t.Logf("%#v\n", startLoc.Fn.Name) 5582 if startLoc.Fn.Name == "main.agoroutine" { 5583 found = true 5584 break 5585 } 5586 } 5587 if !found { 5588 t.Errorf("could not find any goroutine with a start location of main.agoroutine") 5589 } 5590 }) 5591 } 5592 5593 func TestWatchpointStack(t *testing.T) { 5594 skipOn(t, "not implemented", "freebsd") 5595 skipOn(t, "not implemented", "386") 5596 skipOn(t, "not implemented", "ppc64le") 5597 skipOn(t, "see https://gitlab.com/Raven-IO/raven-delve/issues/2768", "windows") 5598 protest.AllowRecording(t) 5599 5600 position1 := 17 5601 5602 if runtime.GOARCH == "arm64" { 5603 position1 = 16 5604 } 5605 5606 withTestProcess("databpstack", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5607 setFileBreakpoint(p, t, fixture.Source, 11) // Position 0 breakpoint 5608 clearlen := len(p.Breakpoints().M) 5609 5610 assertNoError(grp.Continue(), t, "Continue 0") 5611 assertLineNumber(p, t, 11, "Continue 0") // Position 0 5612 5613 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 5614 assertNoError(err, t, "GoroutineScope") 5615 5616 _, err = p.SetWatchpoint(0, scope, "w", proc.WatchWrite, nil) 5617 assertNoError(err, t, "SetDataBreakpoint(write-only)") 5618 5619 watchbpnum := 3 5620 if recorded, _ := grp.Recorded(); recorded { 5621 watchbpnum = 4 5622 } 5623 5624 if len(p.Breakpoints().M) != clearlen+watchbpnum { 5625 // want 1 watchpoint, 1 stack resize breakpoint, 1 out of scope sentinel (2 if recorded) 5626 t.Errorf("wrong number of breakpoints after setting watchpoint: %d", len(p.Breakpoints().M)-clearlen) 5627 } 5628 5629 var retaddr uint64 5630 for _, bp := range p.Breakpoints().M { 5631 for _, breaklet := range bp.Breaklets { 5632 if breaklet.Kind&proc.WatchOutOfScopeBreakpoint != 0 { 5633 retaddr = bp.Addr 5634 break 5635 } 5636 } 5637 } 5638 5639 // Note: for recorded processes retaddr will not always be the return 5640 // address, ~50% of the times it will be the address of the CALL 5641 // instruction preceding the return address, this does not matter for this 5642 // test. 5643 5644 _, err = p.SetBreakpoint(0, retaddr, proc.UserBreakpoint, nil) 5645 assertNoError(err, t, "SetBreakpoint") 5646 5647 if len(p.Breakpoints().M) != clearlen+watchbpnum { 5648 // want 1 watchpoint, 1 stack resize breakpoint, 1 out of scope sentinel (which is also a user breakpoint) (and another out of scope sentinel if recorded) 5649 t.Errorf("wrong number of breakpoints after setting watchpoint: %d", len(p.Breakpoints().M)-clearlen) 5650 } 5651 5652 assertNoError(grp.Continue(), t, "Continue 1") 5653 assertLineNumber(p, t, position1, "Continue 1") // Position 1 5654 5655 assertNoError(grp.Continue(), t, "Continue 2") 5656 t.Logf("%#v", p.CurrentThread().Breakpoint().Breakpoint) 5657 assertLineNumber(p, t, 24, "Continue 2") // Position 2 (watchpoint gone out of scope) 5658 5659 if len(p.Breakpoints().M) != clearlen+1 { 5660 // want 1 user breakpoint set at retaddr 5661 t.Errorf("wrong number of breakpoints after watchpoint goes out of scope: %d", len(p.Breakpoints().M)-clearlen) 5662 } 5663 5664 if len(p.Breakpoints().WatchOutOfScope) != 1 { 5665 t.Errorf("wrong number of out-of-scope watchpoints after watchpoint goes out of scope: %d", len(p.Breakpoints().WatchOutOfScope)) 5666 } 5667 5668 err = p.ClearBreakpoint(retaddr) 5669 assertNoError(err, t, "ClearBreakpoint") 5670 5671 if len(p.Breakpoints().M) != clearlen { 5672 // want 1 user breakpoint set at retaddr 5673 t.Errorf("wrong number of breakpoints after removing user breakpoint: %d", len(p.Breakpoints().M)-clearlen) 5674 } 5675 }) 5676 } 5677 5678 func TestWatchpointStackBackwardsOutOfScope(t *testing.T) { 5679 skipUnlessOn(t, "only for recorded targets", "rr") 5680 protest.AllowRecording(t) 5681 5682 withTestProcess("databpstack", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5683 setFileBreakpoint(p, t, fixture.Source, 11) // Position 0 breakpoint 5684 clearlen := len(p.Breakpoints().M) 5685 5686 assertNoError(grp.Continue(), t, "Continue 0") 5687 assertLineNumber(p, t, 11, "Continue 0") // Position 0 5688 5689 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 5690 assertNoError(err, t, "GoroutineScope") 5691 5692 _, err = p.SetWatchpoint(0, scope, "w", proc.WatchWrite, nil) 5693 assertNoError(err, t, "SetDataBreakpoint(write-only)") 5694 5695 assertNoError(grp.Continue(), t, "Continue 1") 5696 assertLineNumber(p, t, 17, "Continue 1") // Position 1 5697 5698 grp.ChangeDirection(proc.Backward) 5699 5700 assertNoError(grp.Continue(), t, "Continue 2") 5701 t.Logf("%#v", p.CurrentThread().Breakpoint().Breakpoint) 5702 assertLineNumber(p, t, 16, "Continue 2") // Position 1 again (because of inverted movement) 5703 5704 assertNoError(grp.Continue(), t, "Continue 3") 5705 t.Logf("%#v", p.CurrentThread().Breakpoint().Breakpoint) 5706 assertLineNumber(p, t, 11, "Continue 3") // Position 0 (breakpoint 1 hit) 5707 5708 assertNoError(grp.Continue(), t, "Continue 4") 5709 t.Logf("%#v", p.CurrentThread().Breakpoint().Breakpoint) 5710 assertLineNumber(p, t, 23, "Continue 4") // Position 2 (watchpoint gone out of scope) 5711 5712 if len(p.Breakpoints().M) != clearlen { 5713 t.Errorf("wrong number of breakpoints after watchpoint goes out of scope: %d", len(p.Breakpoints().M)-clearlen) 5714 } 5715 5716 if len(p.Breakpoints().WatchOutOfScope) != 1 { 5717 t.Errorf("wrong number of out-of-scope watchpoints after watchpoint goes out of scope: %d", len(p.Breakpoints().WatchOutOfScope)) 5718 } 5719 5720 if len(p.Breakpoints().M) != clearlen { 5721 // want 1 user breakpoint set at retaddr 5722 t.Errorf("wrong number of breakpoints after removing user breakpoint: %d", len(p.Breakpoints().M)-clearlen) 5723 } 5724 }) 5725 } 5726 5727 func TestSetOnFunctions(t *testing.T) { 5728 // The set command between function variables should fail with an error 5729 // Issue #2691 5730 withTestProcess("goroutinestackprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5731 setFunctionBreakpoint(p, t, "main.main") 5732 assertNoError(grp.Continue(), t, "Continue()") 5733 scope, err := proc.GoroutineScope(p, p.CurrentThread()) 5734 assertNoError(err, t, "GoroutineScope") 5735 err = scope.SetVariable("main.func1", "main.func2") 5736 if err == nil { 5737 t.Fatal("expected error when assigning between function variables") 5738 } 5739 }) 5740 } 5741 5742 func TestNilPtrDerefInBreakInstr(t *testing.T) { 5743 // Checks that having a breakpoint on the exact instruction that causes a 5744 // nil pointer dereference does not cause problems. 5745 5746 var asmfile string 5747 switch runtime.GOARCH { 5748 case "amd64": 5749 asmfile = "main_amd64.s" 5750 case "arm64": 5751 asmfile = "main_arm64.s" 5752 case "386": 5753 asmfile = "main_386.s" 5754 case "ppc64le": 5755 asmfile = "main_ppc64le.s" 5756 default: 5757 t.Fatalf("assembly file for %s not provided", runtime.GOARCH) 5758 } 5759 5760 withTestProcess("asmnilptr/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5761 f := filepath.Join(fixture.BuildDir, asmfile) 5762 f = strings.Replace(f, "\\", "/", -1) 5763 setFileBreakpoint(p, t, f, 5) 5764 t.Logf("first continue") 5765 assertNoError(grp.Continue(), t, "Continue()") 5766 t.Logf("second continue") 5767 err := grp.Continue() 5768 if runtime.GOOS == "darwin" && err != nil && err.Error() == "bad access" { 5769 // this is also ok 5770 return 5771 } 5772 t.Logf("third continue") 5773 assertNoError(err, t, "Continue()") 5774 bp := p.CurrentThread().Breakpoint() 5775 if bp != nil { 5776 t.Logf("%#v\n", bp.Breakpoint) 5777 } 5778 if bp == nil || (bp.Logical.Name != proc.UnrecoveredPanic) { 5779 t.Fatalf("no breakpoint hit or wrong breakpoint hit: %#v", bp) 5780 } 5781 }) 5782 } 5783 5784 func TestStepIntoAutogeneratedSkip(t *testing.T) { 5785 // Tests that autogenerated functions are skipped with the new naming 5786 // scheme for autogenerated functions (issue #2948). 5787 withTestProcess("stepintobug", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5788 setFileBreakpoint(p, t, fixture.Source, 9) 5789 assertNoError(grp.Continue(), t, "Continue()") 5790 assertNoError(grp.Step(), t, "Step") 5791 assertLineNumber(p, t, 12, "After step") 5792 }) 5793 } 5794 5795 func TestFollowExec(t *testing.T) { 5796 skipOn(t, "follow exec not implemented on freebsd", "freebsd") 5797 skipOn(t, "follow exec not implemented on macOS", "darwin") 5798 withTestProcessArgs("spawn", t, ".", []string{"spawn", "3"}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5799 grp.LogicalBreakpoints[1] = &proc.LogicalBreakpoint{LogicalID: 1, Set: proc.SetBreakpoint{FunctionName: "main.traceme1"}, HitCount: make(map[int64]uint64)} 5800 grp.LogicalBreakpoints[2] = &proc.LogicalBreakpoint{LogicalID: 2, Set: proc.SetBreakpoint{FunctionName: "main.traceme2"}, HitCount: make(map[int64]uint64)} 5801 grp.LogicalBreakpoints[3] = &proc.LogicalBreakpoint{LogicalID: 3, Set: proc.SetBreakpoint{FunctionName: "main.traceme3"}, HitCount: make(map[int64]uint64)} 5802 5803 assertNoError(grp.EnableBreakpoint(grp.LogicalBreakpoints[1]), t, "EnableBreakpoint(main.traceme1)") 5804 assertNoError(grp.EnableBreakpoint(grp.LogicalBreakpoints[3]), t, "EnableBreakpoint(main.traceme3)") 5805 5806 assertNoError(grp.FollowExec(true, ""), t, "FollowExec") 5807 5808 first := true 5809 finished := false 5810 pids := map[int]int{} 5811 ns := map[string]int{} 5812 5813 for { 5814 t.Log("Continuing") 5815 err := grp.Continue() 5816 if err != nil { 5817 _, isexited := err.(proc.ErrProcessExited) 5818 if isexited { 5819 break 5820 } 5821 assertNoError(err, t, "Continue") 5822 } 5823 5824 if first { 5825 first = false 5826 if grp.Selected != p { 5827 t.Fatalf("first breakpoint hit was not on the parent process") 5828 } 5829 if grp.Selected.CurrentThread().Breakpoint().Breakpoint.LogicalID() != 1 { 5830 t.Fatalf("wrong breakpoint %#v", grp.Selected.CurrentThread().Breakpoint().Breakpoint) 5831 } 5832 loc, err := grp.Selected.CurrentThread().Location() 5833 assertNoError(err, t, "Location") 5834 if loc.Fn.Name != "main.traceme1" { 5835 t.Fatalf("wrong stop location %#v", loc) 5836 } 5837 } else if grp.Selected == p { 5838 if finished { 5839 t.Fatalf("breakpoint hit after the last one in the parent process") 5840 } 5841 if p.CurrentThread().Breakpoint().Breakpoint.LogicalID() != 3 { 5842 t.Fatalf("wrong breakpoint %#v", p.CurrentThread().Breakpoint().Breakpoint) 5843 } 5844 loc, err := p.CurrentThread().Location() 5845 assertNoError(err, t, "Location") 5846 if loc.Fn.Name != "main.traceme3" { 5847 t.Fatalf("wrong stop location %#v", loc) 5848 } 5849 finished = true 5850 } else { 5851 if finished { 5852 t.Fatalf("breakpoint hit after the last one in a child process") 5853 } 5854 it := proc.ValidTargets{Group: grp} 5855 for it.Next() { 5856 tgt := it.Target 5857 if !tgt.CurrentThread().Breakpoint().Active { 5858 continue 5859 } 5860 if tgt.CurrentThread().Breakpoint().Breakpoint.LogicalID() != 2 { 5861 t.Fatalf("wrong breakpoint %#v", grp.Selected.CurrentThread().Breakpoint().Breakpoint) 5862 } 5863 pids[tgt.Pid()]++ 5864 loc, err := tgt.CurrentThread().Location() 5865 assertNoError(err, t, "Location") 5866 if loc.Fn.Name != "main.traceme2" { 5867 t.Fatalf("wrong stop location %#v", loc) 5868 } 5869 nvar := evalVariable(tgt, t, "n") 5870 if nvar.Unreadable != nil { 5871 t.Fatalf("unreadable variable 'n' on target %d: %v", tgt.Pid(), nvar.Unreadable) 5872 } 5873 t.Logf("variable 'n' on target %d: %#v (%v)", tgt.Pid(), nvar, nvar.Value) 5874 ns[constant.StringVal(nvar.Value)]++ 5875 } 5876 } 5877 } 5878 5879 if len(ns) != 3 { 5880 t.Errorf("bad contents of ns: %#v", ns) 5881 } 5882 for _, v := range ns { 5883 if v != 1 { 5884 t.Errorf("bad contents of ns: %#v", ns) 5885 } 5886 } 5887 if ns["C0"] != 1 || ns["C1"] != 1 || ns["C2"] != 1 { 5888 t.Errorf("bad contents of ns: %#v", ns) 5889 } 5890 5891 if len(pids) != 3 { 5892 t.Errorf("bad contents of pids: %#v", pids) 5893 } 5894 for _, v := range pids { 5895 if v != 1 { 5896 t.Errorf("bad contents of pids: %#v", pids) 5897 } 5898 } 5899 }) 5900 } 5901 5902 func TestEscapeCheckUnreadable(t *testing.T) { 5903 // A failure in escapeCheck to dereference a field should not cause 5904 // infinite recursion. See issue #3310. 5905 withTestProcessArgs("reflecttypefncall", t, ".", []string{}, protest.AllNonOptimized, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5906 setFileBreakpoint(p, t, fixture.Source, 9) 5907 assertNoError(grp.Continue(), t, "Continue") 5908 proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "value.Type()", normalLoadConfig, true) 5909 }) 5910 } 5911 5912 func TestStepShadowConcurrentBreakpoint(t *testing.T) { 5913 // Checks that a StepBreakpoint can not shadow a concurrently hit user breakpoint 5914 withTestProcess("stepshadow", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5915 break2 := setFunctionBreakpoint(p, t, "main.stacktraceme2") 5916 breakMain := setFileBreakpoint(p, t, fixture.Source, 15) 5917 assertNoError(grp.Continue(), t, "Continue()") 5918 5919 stacktraceme1calls, stacktraceme2calls := 0, 0 5920 5921 for { 5922 t.Logf("stop (%d %d):", stacktraceme1calls, stacktraceme2calls) 5923 for _, th := range p.ThreadList() { 5924 loc, _ := th.Location() 5925 t.Logf("\t%s:%d\n", loc.File, loc.Line) 5926 bp := th.Breakpoint().Breakpoint 5927 if bp != nil && bp.Addr == break2.Addr { 5928 stacktraceme2calls++ 5929 } 5930 // make stop on the breakpoint in main.main the selected goroutine so we can use step later 5931 if bp != nil && bp.Addr == breakMain.Addr { 5932 g, _ := proc.GetG(th) 5933 p.SwitchGoroutine(g) 5934 } 5935 } 5936 5937 file, lineno := currentLineNumber(p, t) 5938 5939 var err error 5940 var reason string 5941 switch lineno { 5942 default: 5943 t.Fatalf("unexpected stop location %s:%d", file, lineno) 5944 case 15: // loop in main.main 5945 reason = "Step()" 5946 err = grp.Step() 5947 case 28: // main.stacktraceme1 5948 stacktraceme1calls++ 5949 reason = "Continue()" 5950 err = grp.Continue() 5951 case 30, 31: // main.stacktraceme2 5952 reason = "Continue()" 5953 err = grp.Continue() 5954 } 5955 if _, isexited := err.(proc.ErrProcessExited); isexited { 5956 break 5957 } 5958 assertNoError(err, t, reason) 5959 } 5960 5961 t.Logf("%d %d\n", stacktraceme1calls, stacktraceme2calls) 5962 5963 if stacktraceme1calls != 100 { 5964 t.Errorf("wrong number of calls to stacktraceme1 found: %d", stacktraceme1calls) 5965 } 5966 if stacktraceme2calls != 100 { 5967 t.Errorf("wrong number of calls to stacktraceme2 found: %d", stacktraceme2calls) 5968 } 5969 }) 5970 } 5971 5972 func TestFollowExecRegexFilter(t *testing.T) { 5973 skipOn(t, "follow exec not implemented on freebsd", "freebsd") 5974 skipOn(t, "follow exec not implemented on macOS", "darwin") 5975 withTestProcessArgs("spawn", t, ".", []string{"spawn", "3"}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 5976 grp.LogicalBreakpoints[1] = &proc.LogicalBreakpoint{LogicalID: 1, Set: proc.SetBreakpoint{FunctionName: "main.traceme1"}, HitCount: make(map[int64]uint64)} 5977 grp.LogicalBreakpoints[2] = &proc.LogicalBreakpoint{LogicalID: 2, Set: proc.SetBreakpoint{FunctionName: "main.traceme2"}, HitCount: make(map[int64]uint64)} 5978 grp.LogicalBreakpoints[3] = &proc.LogicalBreakpoint{LogicalID: 3, Set: proc.SetBreakpoint{FunctionName: "main.traceme3"}, HitCount: make(map[int64]uint64)} 5979 5980 assertNoError(grp.EnableBreakpoint(grp.LogicalBreakpoints[1]), t, "EnableBreakpoint(main.traceme1)") 5981 assertNoError(grp.EnableBreakpoint(grp.LogicalBreakpoints[3]), t, "EnableBreakpoint(main.traceme3)") 5982 5983 assertNoError(grp.FollowExec(true, "spawn.* child C1"), t, "FollowExec") 5984 5985 assertNoError(grp.Continue(), t, "Continue 1") 5986 assertFunctionName(grp.Selected, t, "main.traceme1", "Program did not continue to the expected location (1)") 5987 assertNoError(grp.Continue(), t, "Continue 2") 5988 assertFunctionName(grp.Selected, t, "main.traceme2", "Program did not continue to the expected location (2)") 5989 assertNoError(grp.Continue(), t, "Continue 3") 5990 assertFunctionName(grp.Selected, t, "main.traceme3", "Program did not continue to the expected location (3)") 5991 err := grp.Continue() 5992 if err != nil { 5993 _, isexited := err.(proc.ErrProcessExited) 5994 if !isexited { 5995 assertNoError(err, t, "Continue 4") 5996 } 5997 } else { 5998 t.Fatal("process did not exit after 4 continues") 5999 } 6000 }) 6001 } 6002 6003 func TestReadTargetArguments(t *testing.T) { 6004 protest.AllowRecording(t) 6005 withTestProcessArgs("restartargs", t, ".", []string{"one", "two", "three"}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 6006 t.Logf("command line: %q\n", p.CmdLine) 6007 if !strings.HasSuffix(p.CmdLine, " one two three") { 6008 t.Fatalf("wrong command line") 6009 } 6010 }) 6011 } 6012 6013 func testWaitForSetup(t *testing.T, mu *sync.Mutex, started *bool) (*exec.Cmd, *proc.WaitFor) { 6014 var buildFlags protest.BuildFlags 6015 if buildMode == "pie" { 6016 buildFlags |= protest.BuildModePIE 6017 } 6018 fixture := protest.BuildFixture("loopprog", buildFlags) 6019 6020 cmd := exec.Command(fixture.Path) 6021 6022 go func() { 6023 time.Sleep(2 * time.Second) 6024 cmd.Stdout = os.Stdout 6025 cmd.Stderr = os.Stderr 6026 assertNoError(cmd.Start(), t, "starting fixture") 6027 mu.Lock() 6028 *started = true 6029 mu.Unlock() 6030 }() 6031 6032 waitFor := &proc.WaitFor{Name: fixture.Path, Interval: 100 * time.Millisecond, Duration: 10 * time.Second} 6033 6034 return cmd, waitFor 6035 } 6036 6037 func TestWaitFor(t *testing.T) { 6038 skipOn(t, "waitfor implementation is delegated to debugserver", "darwin") 6039 skipOn(t, "flaky", "freebsd") 6040 6041 var mu sync.Mutex 6042 started := false 6043 6044 cmd, waitFor := testWaitForSetup(t, &mu, &started) 6045 6046 pid, err := native.WaitFor(waitFor) 6047 assertNoError(err, t, "waitFor.Wait()") 6048 if pid != cmd.Process.Pid { 6049 t.Errorf("pid mismatch, expected %d got %d", pid, cmd.Process.Pid) 6050 } 6051 6052 cmd.Process.Kill() 6053 cmd.Wait() 6054 } 6055 6056 func TestWaitForAttach(t *testing.T) { 6057 skipOn(t, "flaky", "freebsd") 6058 if testBackend == "lldb" && runtime.GOOS == "linux" { 6059 bs, _ := os.ReadFile("/proc/sys/kernel/yama/ptrace_scope") 6060 if bs == nil || strings.TrimSpace(string(bs)) != "0" { 6061 t.Logf("can not run TestAttachDetach: %v\n", bs) 6062 return 6063 } 6064 } 6065 if testBackend == "rr" { 6066 return 6067 } 6068 6069 var mu sync.Mutex 6070 started := false 6071 6072 cmd, waitFor := testWaitForSetup(t, &mu, &started) 6073 6074 var p *proc.TargetGroup 6075 var err error 6076 6077 switch testBackend { 6078 case "native": 6079 p, err = native.Attach(0, waitFor, []string{}) 6080 case "lldb": 6081 path := "" 6082 if runtime.GOOS == "darwin" { 6083 path = waitFor.Name 6084 } 6085 p, err = gdbserial.LLDBAttach(0, path, waitFor, []string{}) 6086 default: 6087 err = fmt.Errorf("unknown backend %q", testBackend) 6088 } 6089 6090 assertNoError(err, t, "Attach") 6091 6092 mu.Lock() 6093 if !started { 6094 t.Fatalf("attach succeeded but started is false") 6095 } 6096 mu.Unlock() 6097 6098 p.Detach(true) 6099 6100 cmd.Wait() 6101 } 6102 6103 func TestNextGenericMethodThroughInterface(t *testing.T) { 6104 // Tests that autogenerated wrappers for generic methods called through an 6105 // interface are skipped. 6106 6107 varcheck := func(p *proc.Target) { 6108 yvar := evalVariable(p, t, "y") 6109 yval, _ := constant.Int64Val(yvar.Value) 6110 if yval != 2 { 6111 t.Errorf("expected 2 got %#v", yvar.Value) 6112 } 6113 } 6114 6115 if runtime.GOOS == "linux" && runtime.GOARCH == "386" { 6116 testseq2(t, "genericintoiface", "main.callf", []seqTest{ 6117 {contContinue, 17}, 6118 {contStep, 18}, 6119 {contStep, 10}, 6120 {contNothing, varcheck}, 6121 {contNext, 11}, 6122 {contNext, 19}, 6123 }) 6124 } else { 6125 testseq2(t, "genericintoiface", "main.callf", []seqTest{ 6126 {contContinue, 17}, 6127 {contStep, 18}, 6128 {contStep, 9}, 6129 {contNext, 10}, 6130 {contNothing, varcheck}, 6131 {contNext, 11}, 6132 {contNext, 19}, 6133 }) 6134 } 6135 } 6136 6137 func TestIssue3545(t *testing.T) { 6138 protest.AllowRecording(t) 6139 withTestProcessArgs("nilptr", t, "", []string{}, protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 6140 err := grp.Continue() 6141 if err != nil && err.Error() == "bad access" { 6142 grp.Continue() 6143 } 6144 locations, err := proc.ThreadStacktrace(p, p.CurrentThread(), 40) 6145 assertNoError(err, t, "Stacktrace()") 6146 var foundMain bool 6147 for _, loc := range locations { 6148 if loc.Call.Fn != nil && loc.Call.Fn.Name == "main.main" { 6149 if foundMain { 6150 t.Fatal("main.main found more than once in the stacktrace") 6151 } 6152 foundMain = true 6153 } 6154 } 6155 if !foundMain { 6156 t.Fatal("did not find main.main in stack trace") 6157 } 6158 }) 6159 } 6160 6161 func TestPanicLine(t *testing.T) { 6162 withTestProcess("panicline", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 6163 err := grp.Continue() 6164 if runtime.GOOS == "darwin" && err != nil && err.Error() == "bad access" { 6165 // not supported 6166 return 6167 } 6168 assertNoError(err, t, "Continue()") 6169 frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 20) 6170 assertNoError(err, t, "ThreadStacktrace") 6171 logStacktrace(t, p, frames) 6172 6173 found := false 6174 for _, frame := range frames { 6175 if strings.HasSuffix(frame.Call.File, "panicline.go") && frame.Call.Line == 7 { 6176 found = true 6177 break 6178 } 6179 } 6180 if !found { 6181 t.Fatalf("could not find panicline.go:6") 6182 } 6183 }) 6184 } 6185 6186 func TestReadClosure(t *testing.T) { 6187 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) { 6188 t.Skip("not implemented") 6189 } 6190 withTestProcess("closurecontents", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { 6191 avalues := []int64{0, 3, 9, 27} 6192 for i := 0; i < 4; i++ { 6193 assertNoError(grp.Continue(), t, "Continue()") 6194 accV := evalVariable(p, t, "acc") 6195 t.Log(api.ConvertVar(accV).MultilineString("", "")) 6196 if len(accV.Children) != 2 { 6197 t.Error("wrong number of children") 6198 } else { 6199 found := 0 6200 for j := range accV.Children { 6201 v := &accV.Children[j] 6202 switch v.Name { 6203 case "scale": 6204 found++ 6205 if val, _ := constant.Int64Val(v.Value); val != 3 { 6206 t.Error("wrong value for scale") 6207 } 6208 case "a": 6209 found++ 6210 if val, _ := constant.Int64Val(v.Value); val != avalues[i] { 6211 t.Errorf("wrong value for a: %d", val) 6212 } 6213 } 6214 } 6215 if found != 2 { 6216 t.Error("wrong captured variables") 6217 } 6218 } 6219 } 6220 }) 6221 } 6222 6223 func TestStepIntoGoroutine(t *testing.T) { 6224 testseq2(t, "goroutinestackprog", "", []seqTest{ 6225 {contContinue, 23}, 6226 {contStep, 7}, 6227 {contNothing, func(p *proc.Target) { 6228 vari := api.ConvertVar(evalVariable(p, t, "i")) 6229 varis := vari.SinglelineString() 6230 t.Logf("i = %s", varis) 6231 if varis != "0" { 6232 t.Fatalf("wrong value for variable i: %s", vari.SinglelineString()) 6233 } 6234 }}, 6235 }) 6236 }