Repository: go-delve/delve
Pull request #2624 (2021-07-27 20:34:58)

pkg/terminal: Ignore existing breakpoints for continue until
Ignore existing breakpoints when using the continue command to continue
to a specific location such as `continue main.main`. The point of this
command is to continue to a specific location, so if there is already a
breakpoint set there there should be no error returned, just continue
until we hit the breakpoint already set in that location.


Patch:
diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md
index 509bf719..f164a812 100644
--- a/Documentation/cli/README.md
+++ b/Documentation/cli/README.md
@@ -43,7 +43,7 @@ Command | Description
 --------|------------
 [args](#args) | Print function arguments.
 [display](#display) | Print value of an expression every time the program stops.
-[examinemem](#examinemem) | Examine memory:
+[examinemem](#examinemem) | Examine raw memory at the given address.
 [locals](#locals) | Print local variables.
 [print](#print) | Evaluate an expression.
 [regs](#regs) | Print contents of CPU registers.
@@ -291,6 +291,8 @@ If locspec is omitted edit will open the current source file in the editor, othe
 Aliases: ed
 
 ## examinemem
+Examine raw memory at the given address.
+
 Examine memory:
 
 	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
diff --git a/pkg/proc/amd64_arch.go b/pkg/proc/amd64_arch.go
index cd194552..db17533a 100644
--- a/pkg/proc/amd64_arch.go
+++ b/pkg/proc/amd64_arch.go
@@ -219,7 +219,7 @@ func amd64SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
 		return true
 
 	default:
-		if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
+		if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.throw" && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
 			// The runtime switches to the system stack in multiple places.
 			// This usually happens through a call to runtime.systemstack but there
 			// are functions that switch to the system stack manually (for example
@@ -228,7 +228,7 @@ func amd64SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
 			// calls we switch directly to the goroutine stack if we detect that the
 			// function at the top of the stack is a runtime function.
 			//
-			// The function "runtime.fatalthrow" is deliberately excluded from this
+			// The function "runtime.throw" is deliberately excluded from this
 			// because it can end up in the stack during a cgo call and switching to
 			// the goroutine stack will exclude all the C functions from the stack
 			// trace.
diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go
index 52326475..031e1f73 100644
--- a/pkg/proc/arm64_arch.go
+++ b/pkg/proc/arm64_arch.go
@@ -156,7 +156,7 @@ func arm64SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool
 			it.pc = newlr
 			return true
 		default:
-			if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
+			if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.throw" && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
 				// The runtime switches to the system stack in multiple places.
 				// This usually happens through a call to runtime.systemstack but there
 				// are functions that switch to the system stack manually (for example
diff --git a/pkg/proc/i386_arch.go b/pkg/proc/i386_arch.go
index 341b73a1..f2ad59aa 100644
--- a/pkg/proc/i386_arch.go
+++ b/pkg/proc/i386_arch.go
@@ -148,7 +148,7 @@ func i386SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
 		return true
 
 	default:
-		if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
+		if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.throw" && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
 			// The runtime switches to the system stack in multiple places.
 			// This usually happens through a call to runtime.systemstack but there
 			// are functions that switch to the system stack manually (for example
@@ -157,7 +157,7 @@ func i386SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
 			// calls we switch directly to the goroutine stack if we detect that the
 			// function at the top of the stack is a runtime function.
 			//
-			// The function "runtime.fatalthrow" is deliberately excluded from this
+			// The function "runtime.throw" is deliberately excluded from this
 			// because it can end up in the stack during a cgo call and switching to
 			// the goroutine stack will exclude all the C functions from the stack
 			// trace.
diff --git a/pkg/proc/target.go b/pkg/proc/target.go
index ff8af354..c919fd54 100644
--- a/pkg/proc/target.go
+++ b/pkg/proc/target.go
@@ -375,7 +375,7 @@ func (t *Target) createUnrecoveredPanicBreakpoint() {
 
 // createFatalThrowBreakpoint creates the a breakpoint as runtime.fatalthrow.
 func (t *Target) createFatalThrowBreakpoint() {
-	fatalpcs, err := FindFunctionLocation(t.Process, "runtime.fatalthrow", 0)
+	fatalpcs, err := FindFunctionLocation(t.Process, "runtime.throw", 0)
 	if err == nil {
 		bp, err := t.SetBreakpointWithID(fatalThrowID, fatalpcs[0])
 		if err == nil {
diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go
index b0238077..2736e469 100644
--- a/pkg/terminal/command.go
+++ b/pkg/terminal/command.go
@@ -487,7 +487,9 @@ Defines <alias> as an alias to <command> or removes an alias.`},
 If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
 		{aliases: []string{"libraries"}, cmdFn: libraries, helpMsg: `List loaded dynamic libraries`},
 
-		{aliases: []string{"examinemem", "x"}, group: dataCmds, cmdFn: examineMemoryCmd, helpMsg: `Examine memory:
+		{aliases: []string{"examinemem", "x"}, group: dataCmds, cmdFn: examineMemoryCmd, helpMsg: `Examine raw memory at the given address.
+
+Examine memory:
 
 	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
 	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] -x <expression>
@@ -1410,7 +1412,9 @@ func (c *Commands) cont(t *Term, ctx callContext, args string) error {
 	if args != "" {
 		tmp, err := setBreakpoint(t, ctx, false, args)
 		if err != nil {
-			return err
+			if !strings.Contains(err.Error(), "Breakpoint exists") {
+				return err
+			}
 		}
 		defer func() {
 			for _, bp := range tmp {
diff --git a/service/dap/server.go b/service/dap/server.go
index ae87eb53..28c93995 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -2731,7 +2731,7 @@ func (s *Server) onExceptionInfoRequest(request *dap.ExceptionInfoRequest) {
 }
 
 func (s *Server) throwReason(goroutineID int) (string, error) {
-	return s.getExprString("s", goroutineID, 1)
+	return s.getExprString("s", goroutineID, 0)
 }
 
 func (s *Server) panicReason(goroutineID int) (string, error) {


Test patch:
diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go
index 1e60970d..b470dcee 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -1354,14 +1354,7 @@ func TestThreadFrameEvaluation(t *testing.T) {
 		scope, err := proc.ConvertEvalScope(p, 0, 0, 0)
 		assertNoError(err, t, "ConvertEvalScope() on frame 0")
 		_, err = scope.EvalVariable("s", normalLoadConfig)
-		if err == nil {
-			t.Errorf("expected error for EvalVariable(\"s\") on frame 0")
-		}
-
-		scope, err = proc.ConvertEvalScope(p, 0, 1, 0)
-		assertNoError(err, t, "ConvertEvalScope() on frame 1")
-		_, err = scope.EvalVariable("s", normalLoadConfig)
-		assertNoError(err, t, "EvalVariable(\"s\") on frame 1")
+		assertNoError(err, t, "EvalVariable(\"s\") on frame 0")
 	})
 }
 
diff --git a/pkg/terminal/command_test.go b/pkg/terminal/command_test.go
index 4cd90eb9..a80b6203 100644
--- a/pkg/terminal/command_test.go
+++ b/pkg/terminal/command_test.go
@@ -1130,6 +1130,18 @@ func TestContinueUntil(t *testing.T) {
 	})
 }
 
+func TestContinueUntilExistingBreakpoint(t *testing.T) {
+	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
+		term.MustExec("break main.main")
+		if runtime.GOARCH != "386" {
+			listIsAt(t, term, "continue main.main", 16, -1, -1)
+		} else {
+			listIsAt(t, term, "continue main.main", 17, -1, -1)
+		}
+		listIsAt(t, term, "continue main.sayhi", 12, -1, -1)
+	})
+}
+
 func TestPrintFormat(t *testing.T) {
 	withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
 		term.MustExec("continue")
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index bb20000c..982e7cf8 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -3689,10 +3689,6 @@ func TestPanicBreakpointOnNext(t *testing.T) {
 }
 
 func TestFatalThrowBreakpoint(t *testing.T) {
-	// This is not currently flaky for Go 1.17 see https://github.com/golang/go/issues/46425.
-	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) {
-		t.Skip()
-	}
 	runTest(t, "fatalerror", func(client *daptest.Client, fixture protest.Fixture) {
 		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
@@ -3750,9 +3746,9 @@ func TestFatalThrowBreakpoint(t *testing.T) {
 					client.ContinueRequest(1)
 					client.ExpectContinueResponse(t)
 
-					// TODO(suzmue): Enable this test for 1.17 when https://github.com/golang/go/issues/46425 is fixed.
+					// This does not work for Go 1.16 so skip by detecting versions before or after 1.16.
 					var text string
-					if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
+					if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) || goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) {
 						text = "\"all goroutines are asleep - deadlock!\""
 					}
 					se := client.ExpectStoppedEvent(t)