src/debugAdapter: add 'panic' and 'fatal error' as stopped reasons

Delve sets breakpoints in order to catch uncaught panics and fatal
throws when they happen. The extension was sending a stopped event
with reason breakpoint because it was stopped on a breakpoint, even
though it was not a user set breakpoint.

The extension will now check for these special breakpoints, and set
the reason for stopping as 'panic' and 'fatal error'.

Fixes golang/vscode-go#648

Change-Id: Ie72d35b2d02669cd011197c484cd0f99fbc57cc2
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/256939
Trust: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Polina Sokolova <polina@google.com>
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index 8a51ec9..6dbe0c0 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -247,6 +247,14 @@
 	reason: string;
 }
 
+// Unrecovered panic and fatal throw breakpoint IDs taken from delve:
+// https://github.com/go-delve/delve/blob/f90134eb4db1c423e24fddfbc6eff41b288e6297/pkg/proc/breakpoints.go#L11-L21
+// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
+const unrecoveredPanicID = -1;
+// FatalThrow is the name given to the breakpoint triggered when the target
+// process dies because of a fatal runtime error.
+const fatalThrowID = -2;
+
 // This interface should always match the schema found in `package.json`.
 interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
 	request: 'launch';
@@ -2203,7 +2211,18 @@
 			// Other stopping events (eg pause) create their own StoppedEvents,
 			// if necessary.
 			if (!!state.currentThread.breakPoint) {
-				this.handleReenterDebug('breakpoint');
+				const bp = state.currentThread.breakPoint;
+				if (bp.id === unrecoveredPanicID) {
+					// If the breakpoint is actually caused by a panic,
+					// we want to return on "panic".
+					this.handleReenterDebug('panic');
+				} else if (bp.id === fatalThrowID) {
+					// If the breakpoint is actually caused by a fatal throw,
+					// we want to return on "fatal error".
+					this.handleReenterDebug('fatal error');
+				} else {
+					this.handleReenterDebug('breakpoint');
+				}
 			}
 		};