src/language: adjust panic trace capture logic

Use '[Info - ' or '[Warning - ' or '[Error - ' as the marker
for the end of the panic trace.

Fixes golang/vscode-go#3248

Change-Id: I3aabc0292855f63ceffeea5c09b59e4e0253e95b
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/571316
Commit-Queue: Hyang-Ah Hana Kim <hyangah@gmail.com>
kokoro-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
diff --git a/extension/src/language/goLanguageServer.ts b/extension/src/language/goLanguageServer.ts
index 9f5352f..1f32c3c 100644
--- a/extension/src/language/goLanguageServer.ts
+++ b/extension/src/language/goLanguageServer.ts
@@ -1530,7 +1530,6 @@
 enum GoplsFailureModes {
 	NO_GOPLS_LOG = 'no gopls log',
 	EMPTY_PANIC_TRACE = 'empty panic trace',
-	INCOMPLETE_PANIC_TRACE = 'incomplete panic trace',
 	INCORRECT_COMMAND_USAGE = 'incorrect gopls command usage',
 	UNRECOGNIZED_CRASH_PATTERN = 'unrecognized crash pattern'
 }
@@ -1544,34 +1543,40 @@
 	const panicMsgBegin = logs.lastIndexOf('panic: ');
 	if (panicMsgBegin > -1) {
 		// panic message was found.
-		const panicMsgEnd = logs.indexOf('Connection to server got closed.', panicMsgBegin);
+		let panicTrace = logs.substr(panicMsgBegin);
+		const panicMsgEnd = panicTrace.search(/\[(Info|Warning|Error)\s+-\s+/);
 		if (panicMsgEnd > -1) {
-			const panicTrace = logs.substr(panicMsgBegin, panicMsgEnd - panicMsgBegin);
-			const filePattern = /(\S+\.go):\d+/;
-			const sanitized = panicTrace
-				.split('\n')
-				.map((line: string) => {
-					// Even though this is a crash from gopls, the file path
-					// can contain user names and user's filesystem directory structure.
-					// We can still locate the corresponding file if the file base is
-					// available because the full package path is part of the function
-					// name. So, leave only the file base.
-					const m = line.match(filePattern);
-					if (!m) {
-						return line;
-					}
-					const filePath = m[1];
-					const fileBase = path.basename(filePath);
-					return line.replace(filePath, '  ' + fileBase);
-				})
-				.join('\n');
-
-			if (sanitized) {
-				return { sanitizedLog: sanitized };
-			}
-			return { failureReason: GoplsFailureModes.EMPTY_PANIC_TRACE };
+			panicTrace = panicTrace.substr(0, panicMsgEnd);
 		}
-		return { failureReason: GoplsFailureModes.INCOMPLETE_PANIC_TRACE };
+		const filePattern = /(\S+\.go):\d+/;
+		const sanitized = panicTrace
+			.split('\n')
+			.map((line: string) => {
+				// Even though this is a crash from gopls, the file path
+				// can contain user names and user's filesystem directory structure.
+				// We can still locate the corresponding file if the file base is
+				// available because the full package path is part of the function
+				// name. So, leave only the file base.
+				const m = line.match(filePattern);
+				if (!m) {
+					return line;
+				}
+				const filePath = m[1];
+				const fileBase = path.basename(filePath);
+				return line.replace(filePath, '  ' + fileBase);
+			})
+			.join('\n');
+
+		if (sanitized) {
+			return { sanitizedLog: sanitized };
+		}
+		return { failureReason: GoplsFailureModes.EMPTY_PANIC_TRACE };
+	}
+	// Capture Fatal
+	//    foo.go:1: the last message (caveat - we capture only the first log line)
+	const m = logs.match(/(^\S+\.go:\d+:.*$)/gm);
+	if (m && m.length > 0) {
+		return { sanitizedLog: m[0].toString() };
 	}
 	const initFailMsgBegin = logs.lastIndexOf('gopls client:');
 	if (initFailMsgBegin > -1) {
@@ -1590,13 +1595,6 @@
 	if (logs.lastIndexOf('Usage:') > -1) {
 		return { failureReason: GoplsFailureModes.INCORRECT_COMMAND_USAGE };
 	}
-	// Capture Fatal
-	//    foo.go:1: the last message (caveat - we capture only the first log line)
-	const m = logs.match(/(^\S+\.go:\d+:.*$)/gm);
-	if (m && m.length > 0) {
-		return { sanitizedLog: m[0].toString() };
-	}
-
 	return { failureReason: GoplsFailureModes.UNRECOGNIZED_CRASH_PATTERN };
 }
 
@@ -1664,6 +1662,6 @@
 	} catch (e) {
 		const duration = new Date().getTime() - start.getTime();
 		console.log(`gopls stats -anon failed: ${JSON.stringify(e)}`);
-		return `gopls stats -anon failed after running for ${duration}ms`; // e may contain user information. don't include in the report.
+		return `gopls stats -anon failed after ${duration} ms. Please check if gopls is killed by OS.`;
 	}
 }
diff --git a/extension/test/gopls/report.test.ts b/extension/test/gopls/report.test.ts
index 4cabab9..186378b 100644
--- a/extension/test/gopls/report.test.ts
+++ b/extension/test/gopls/report.test.ts
@@ -26,9 +26,14 @@
 				want: sanitizedTraceFromIssueVSCodeGo572LSP317
 			},
 			{
+				name: 'panic trace 2024 March',
+				in: trace2024MarchPanic,
+				want: sanitizedTrace2024MarchPanic
+			},
+			{
 				name: 'incomplete panic trace',
-				in: 'panic: \nsecret\n',
-				wantReason: 'incomplete panic trace'
+				in: 'panic: \ntruncated\n',
+				want: 'panic: \ntruncated\n'
 			},
 			{
 				name: 'incomplete initialization error message',
@@ -42,7 +47,7 @@
 			assert.strictEqual(
 				JSON.stringify(sanitizedLog),
 				JSON.stringify(tc.want),
-				`sanitizeGoplsTrace(${tc.name}) returned unexpected sanitizedLog result`
+				`sanitizeGoplsTrace(${tc.name}) returned unexpected sanitizedLog result - ${sanitizedLog}`
 			);
 			assert.strictEqual(
 				failureReason,
@@ -315,7 +320,7 @@
   handler.go:103 +0x86
 created by golang.org/x/tools/internal/jsonrpc2.AsyncHandler.func1
   handler.go:100 +0x171
-[Info - 12:50:26 PM] `;
+`;
 
 const traceFromIssueVSCodeGo572LSP317 = `
 [Error - 12:20:35 PM] Stopping server failed
@@ -335,3 +340,47 @@
 const sanitizedTraceFromIssueVSCodeGo572LSP317 = `gopls client: couldn't create connection to server.
   Message: Socket closed before the connection was established
   Code: -32099 `;
+
+const trace2024MarchPanic = `
+[Info  - 9:58:40 AM] 
+true
+[Error - 9:58:40 AM] gopls client: couldn't create connection to server.
+  Message: Pending response rejected since connection got disposed
+  Code: -32097 
+panic: crash
+
+goroutine 1 [running]:
+golang.org/x/tools/gopls/internal/cmd.(*Serve).Run(0xc000486310?, {0xc0000b8090?, 0x0?}, {0x0?, 0x0?, 0x0?})
+	/Users/Gopher/projects/tools/gopls/internal/cmd/serve.go:81 +0x25
+golang.org/x/tools/internal/tool.Run({0x1012d048, 0xc00019c3f0}, 0xc000486310, {0x1012f9e0, 0xc000159b40}, {0xc0000b8090, 0x0, 0x0})
+	/Users/Gopher/projects/tools/internal/tool/tool.go:192 +0x691
+golang.org/x/tools/gopls/internal/cmd.(*Application).Run(0xc000159b00, {0x1012d010, 0x107d9840}, {0xc0000b8090, 0x0, 0x0})
+	/Users/Gopher/projects/tools/gopls/internal/cmd/cmd.go:240 +0x147
+golang.org/x/tools/internal/tool.Run({0x1012d010, 0x107d9840}, 0xc0004862a0, {0x1012f3a0, 0xc000159b00}, {0xc0000b8060, 0x4, 0x4})
+	/Users/Gopher/projects/tools/internal/tool/tool.go:192 +0x691
+golang.org/x/tools/internal/tool.Main({0x1012d010, 0x107d9840}, {0x1012f3a0, 0xc000159b00}, {0xc0000b8060, 0x4, 0x4})
+	/Users/Gopher/projects/tools/internal/tool/tool.go:93 +0x12a
+main.main()
+	/Users/Gopher/projects/tools/gopls/main.go:34 +0x109
+[Error - 9:58:49 AM] 
+[Error - 9:58:49 AM] gopls client: couldn't create connection to server.
+  Message: Pending response rejected since connection got disposed
+  Code: -32097 
+Error starting language server: Error: Pending response rejected since connection got disposed`;
+
+const sanitizedTrace2024MarchPanic = `panic: crash
+
+goroutine 1 [running]:
+golang.org/x/tools/gopls/internal/cmd.(*Serve).Run(0xc000486310?, {0xc0000b8090?, 0x0?}, {0x0?, 0x0?, 0x0?})
+	  serve.go:81 +0x25
+golang.org/x/tools/internal/tool.Run({0x1012d048, 0xc00019c3f0}, 0xc000486310, {0x1012f9e0, 0xc000159b40}, {0xc0000b8090, 0x0, 0x0})
+	  tool.go:192 +0x691
+golang.org/x/tools/gopls/internal/cmd.(*Application).Run(0xc000159b00, {0x1012d010, 0x107d9840}, {0xc0000b8090, 0x0, 0x0})
+	  cmd.go:240 +0x147
+golang.org/x/tools/internal/tool.Run({0x1012d010, 0x107d9840}, 0xc0004862a0, {0x1012f3a0, 0xc000159b00}, {0xc0000b8060, 0x4, 0x4})
+	  tool.go:192 +0x691
+golang.org/x/tools/internal/tool.Main({0x1012d010, 0x107d9840}, {0x1012f3a0, 0xc000159b00}, {0xc0000b8060, 0x4, 0x4})
+	  tool.go:93 +0x12a
+main.main()
+	  main.go:34 +0x109
+`;