utils: move non-vscode-dependent utils to utils/

Refactor use of killProcessTree to a single function in utils/processUtils

Change-Id: I5f7d6dc57c19241e267add5c23561b3d465b6d85
GitHub-Last-Rev: 29d0561c3412d0d4f470bc9e516bca7303f7cf4f
GitHub-Pull-Request: golang/vscode-go#344
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/242518
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index 3715196..b784148 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -36,8 +36,8 @@
 	getCurrentGoWorkspaceFromGOPATH,
 	getInferredGopath,
 	parseEnvFile
-} from '../goPath';
-import {killProcessTree} from '../processUtils';
+} from '../utils/goPath';
+import {killProcessTree} from '../utils/processUtils';
 
 const fsAccess = util.promisify(fs.access);
 const fsUnlink = util.promisify(fs.unlink);
diff --git a/src/debugAdapter2/goDlvDebug.ts b/src/debugAdapter2/goDlvDebug.ts
index 6e213bbc..6132354 100644
--- a/src/debugAdapter2/goDlvDebug.ts
+++ b/src/debugAdapter2/goDlvDebug.ts
@@ -25,9 +25,9 @@
 	envPath,
 	getBinPathWithPreferredGopathGoroot,
 	parseEnvFile
-} from '../goPath';
+} from '../utils/goPath';
 
-import {killProcessTree} from '../processUtils';
+import {killProcessTree} from '../utils/processUtils';
 
 import { DAPClient } from './dapClient';
 
diff --git a/src/diffUtils.ts b/src/diffUtils.ts
index f001229..e0895a1 100644
--- a/src/diffUtils.ts
+++ b/src/diffUtils.ts
@@ -5,7 +5,7 @@
 
 import jsDiff = require('diff');
 import { Position, Range, TextEditorEdit, Uri, WorkspaceEdit } from 'vscode';
-import { getBinPathFromEnvVar } from './goPath';
+import { getBinPathFromEnvVar } from './utils/goPath';
 
 let diffToolAvailable: boolean | null = null;
 
diff --git a/src/goBrowsePackage.ts b/src/goBrowsePackage.ts
index f27b107..79097a3 100644
--- a/src/goBrowsePackage.ts
+++ b/src/goBrowsePackage.ts
@@ -9,8 +9,8 @@
 import path = require('path');
 import vscode = require('vscode');
 import { getAllPackages } from './goPackages';
-import { envPath, getCurrentGoRoot } from './goPath';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
+import { envPath, getCurrentGoRoot } from './utils/goPath';
 
 export function browsePackages() {
 	let workDir = '';
diff --git a/src/goBuild.ts b/src/goBuild.ts
index 0aaee96..a265e54 100644
--- a/src/goBuild.ts
+++ b/src/goBuild.ts
@@ -9,7 +9,6 @@
 import { buildDiagnosticCollection } from './goMain';
 import { isModSupported } from './goModules';
 import { getNonVendorPackages } from './goPackages';
-import { getCurrentGoWorkspaceFromGOPATH } from './goPath';
 import { diagnosticsStatusBarItem, outputChannel } from './goStatus';
 import { getTestFlags } from './testUtils';
 import {
@@ -22,6 +21,8 @@
 	ICheckResult,
 	runTool
 } from './util';
+import { getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
+
 /**
  * Builds current package or workspace.
  */
diff --git a/src/goCover.ts b/src/goCover.ts
index 3edc6bb..2fa1c92 100644
--- a/src/goCover.ts
+++ b/src/goCover.ts
@@ -11,9 +11,9 @@
 import util = require('util');
 import vscode = require('vscode');
 import { isModSupported } from './goModules';
-import { envPath } from './goPath';
 import { getTestFlags, goTest, showTestOutput, TestConfig } from './testUtils';
 import { getBinPath, getCurrentGoPath, getGoConfig, getWorkspaceFolderPath } from './util';
+import { envPath } from './utils/goPath';
 
 let gutterSvgs: { [key: string]: string };
 let decorators: {
diff --git a/src/goDeclaration.ts b/src/goDeclaration.ts
index 7eb03ee..39542e0 100644
--- a/src/goDeclaration.ts
+++ b/src/goDeclaration.ts
@@ -20,9 +20,9 @@
 	getWorkspaceFolderPath,
 	goKeywords,
 	isPositionInString,
-	killTree,
 	runGodoc
 } from './util';
+import {killProcessTree} from './utils/processUtils';
 
 const missingToolMsg = 'Missing tool: ';
 
@@ -135,7 +135,7 @@
 	const env = toolExecutionEnvironment();
 	let p: cp.ChildProcess;
 	if (token) {
-		token.onCancellationRequested(() => killTree(p.pid));
+		token.onCancellationRequested(() => killProcessTree(p));
 	}
 
 	return new Promise<GoDefinitionInformation>((resolve, reject) => {
@@ -226,7 +226,7 @@
 	const env = toolExecutionEnvironment();
 	let p: cp.ChildProcess;
 	if (token) {
-		token.onCancellationRequested(() => killTree(p.pid));
+		token.onCancellationRequested(() => killProcessTree(p));
 	}
 
 	return new Promise<GoDefinitionInformation>((resolve, reject) => {
@@ -300,7 +300,7 @@
 	const env = toolExecutionEnvironment();
 	let p: cp.ChildProcess;
 	if (token) {
-		token.onCancellationRequested(() => killTree(p.pid));
+		token.onCancellationRequested(() => killProcessTree(p));
 	}
 	return new Promise<GoDefinitionInformation>((resolve, reject) => {
 		p = cp.execFile(
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index 40e1f79..d43ed21 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -13,9 +13,9 @@
 import vscode = require('vscode');
 import WebRequest = require('web-request');
 import { toolInstallationEnvironment } from './goEnv';
-import { getCurrentGoRoot, pathExists } from './goPath';
 import { outputChannel } from './goStatus';
 import { getBinPath, getGoConfig, getGoVersion, getTempFilePath, rmdirRecursive } from './util';
+import { getCurrentGoRoot, pathExists } from './utils/goPath';
 
 export class GoEnvironmentOption {
 	public static fromQuickPickItem({ description, label }: vscode.QuickPickItem): GoEnvironmentOption {
diff --git a/src/goFormat.ts b/src/goFormat.ts
index 6321e66..8ad78d7 100644
--- a/src/goFormat.ts
+++ b/src/goFormat.ts
@@ -10,7 +10,8 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { getBinPath, getGoConfig, killTree } from './util';
+import { getBinPath, getGoConfig } from './util';
+import { killProcessTree } from './utils/processUtils';
 
 export class GoDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider {
 	public provideDocumentFormattingEdits(
@@ -78,7 +79,7 @@
 
 			// Use spawn instead of exec to avoid maxBufferExceeded error
 			const p = cp.spawn(formatCommandBinPath, formatFlags, { env, cwd });
-			token.onCancellationRequested(() => !p.killed && killTree(p.pid));
+			token.onCancellationRequested(() => !p.killed && killProcessTree(p));
 			p.stdout.setEncoding('utf8');
 			p.stdout.on('data', (data) => (stdout += data));
 			p.stderr.on('data', (data) => (stderr += data));
diff --git a/src/goGetPackage.ts b/src/goGetPackage.ts
index 272a995..bffefeb 100644
--- a/src/goGetPackage.ts
+++ b/src/goGetPackage.ts
@@ -8,9 +8,9 @@
 import cp = require('child_process');
 import vscode = require('vscode');
 import { buildCode } from './goBuild';
-import { envPath, getCurrentGoRoot } from './goPath';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getImportPath } from './util';
+import { envPath, getCurrentGoRoot } from './utils/goPath';
 
 export function goGetPackage() {
 	const editor = vscode.window.activeTextEditor;
diff --git a/src/goImplementations.ts b/src/goImplementations.ts
index da39a51..05e44bc 100644
--- a/src/goImplementations.ts
+++ b/src/goImplementations.ts
@@ -10,15 +10,15 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
-import { envPath, getCurrentGoRoot } from './goPath';
 import {
 	byteOffsetAt,
 	canonicalizeGOPATHPrefix,
 	getBinPath,
 	getGoConfig,
-	getWorkspaceFolderPath,
-	killTree,
+	getWorkspaceFolderPath
 } from './util';
+import { envPath, getCurrentGoRoot } from './utils/goPath';
+import {killProcessTree} from './utils/processUtils';
 
 interface GoListOutput {
 	Dir: string;
@@ -130,10 +130,10 @@
 
 						return resolve(results);
 					});
-					token.onCancellationRequested(() => killTree(guruProcess.pid));
+					token.onCancellationRequested(() => killProcessTree(guruProcess));
 				}
 			);
-			token.onCancellationRequested(() => killTree(listProcess.pid));
+			token.onCancellationRequested(() => killProcessTree(listProcess));
 		});
 	}
 }
diff --git a/src/goImport.ts b/src/goImport.ts
index 5a9df7e..82fa2a9 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -11,8 +11,8 @@
 import { promptForMissingTool } from './goInstallTools';
 import { documentSymbols, GoOutlineImportsOptions } from './goOutline';
 import { getImportablePackages } from './goPackages';
-import { envPath, getCurrentGoRoot } from './goPath';
 import { getBinPath, getImportPath, parseFilePrelude } from './util';
+import { envPath, getCurrentGoRoot } from './utils/goPath';
 
 const missingToolMsg = 'Missing tool: ';
 
diff --git a/src/goInstall.ts b/src/goInstall.ts
index 859da2a..d2131ac 100644
--- a/src/goInstall.ts
+++ b/src/goInstall.ts
@@ -8,9 +8,9 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { isModSupported } from './goModules';
-import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './goPath';
 import { outputChannel } from './goStatus';
 import { getBinPath, getCurrentGoPath, getGoConfig, getModuleCache } from './util';
+import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
 
 export async function installCurrentPackage(): Promise<void> {
 	const editor = vscode.window.activeTextEditor;
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index 62b22fc..0479a18 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -15,7 +15,6 @@
 import { initGoStatusBar } from './goEnvironmentStatus';
 import { getLanguageServerToolPath } from './goLanguageServer';
 import { restartLanguageServer } from './goMain';
-import { envPath, getCurrentGoRoot, getToolFromToolPath, setCurrentGoRoot } from './goPath';
 import { hideGoStatus, outputChannel, showGoStatus } from './goStatus';
 import {
 	containsTool,
@@ -36,6 +35,7 @@
 	GoVersion,
 	rmdirRecursive,
 } from './util';
+import { envPath, getCurrentGoRoot, getToolFromToolPath, setCurrentGoRoot } from './utils/goPath';
 
 // declinedUpdates tracks the tools that the user has declined to update.
 const declinedUpdates: Tool[] = [];
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index 6b2c53b..1a29751 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -32,7 +32,6 @@
 import { restartLanguageServer } from './goMain';
 import { GO_MODE } from './goMode';
 import { GoDocumentSymbolProvider } from './goOutline';
-import { getToolFromToolPath } from './goPath';
 import { GoReferenceProvider } from './goReferences';
 import { GoRenameProvider } from './goRename';
 import { GoSignatureHelpProvider } from './goSignature';
@@ -42,6 +41,7 @@
 import { GoTypeDefinitionProvider } from './goTypeDefinition';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getCurrentGoPath, getGoConfig } from './util';
+import { getToolFromToolPath } from './utils/goPath';
 
 interface LanguageServerConfig {
 	serverName: string;
diff --git a/src/goMain.ts b/src/goMain.ts
index 55bf8dc..88a528c 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -35,7 +35,6 @@
 import { GO_MODE } from './goMode';
 import { addTags, removeTags } from './goModifytags';
 import { GO111MODULE, isModSupported } from './goModules';
-import { clearCacheForTools, envPath, fileExists, getCurrentGoRoot, setCurrentGoRoot } from './goPath';
 import { playgroundCommand } from './goPlayground';
 import { GoReferencesCodeLensProvider } from './goReferencesCodelens';
 import { GoRunTestCodeLensProvider } from './goRunTestCodelens';
@@ -60,6 +59,7 @@
 	handleDiagnosticErrors,
 	isGoPathSet
 } from './util';
+import { clearCacheForTools, envPath, fileExists, getCurrentGoRoot, setCurrentGoRoot } from './utils/goPath';
 
 export let buildDiagnosticCollection: vscode.DiagnosticCollection;
 export let lintDiagnosticCollection: vscode.DiagnosticCollection;
@@ -73,7 +73,8 @@
 export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
 	if (!getBinPath('go', false)) {
 		const choice = await vscode.window.showInformationMessage(
-			`Go could not be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath}. Install Go and reload the window.`,
+			`Go could not be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath}. ` +
+			`Install Go and reload the window.`,
 			'Go to Download Page'
 		);
 		if (choice === 'Go to Download Page') {
diff --git a/src/goModules.ts b/src/goModules.ts
index 2a41455..de41add 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -8,10 +8,10 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { installTools } from './goInstallTools';
-import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './goPath';
 import { getTool } from './goTools';
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getGoConfig, getGoVersion, getModuleCache } from './util';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './utils/goPath';
 
 export let GO111MODULE: string;
 
diff --git a/src/goOutline.ts b/src/goOutline.ts
index 97af953..9e52f6d 100644
--- a/src/goOutline.ts
+++ b/src/goOutline.ts
@@ -13,9 +13,9 @@
 	getBinPath,
 	getFileArchive,
 	getGoConfig,
-	killProcess,
 	makeMemoizedByteOffsetConverter
 } from './util';
+import {killProcess} from './utils/processUtils';
 
 // Keep in sync with https://github.com/ramya-rao-a/go-outline
 export interface GoOutlineRange {
diff --git a/src/goPackages.ts b/src/goPackages.ts
index 5c7fce7..182cd90 100644
--- a/src/goPackages.ts
+++ b/src/goPackages.ts
@@ -8,8 +8,8 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { envPath, fixDriveCasingInWindows, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './goPath';
 import { getBinPath, getCurrentGoPath, getGoVersion, isVendorSupported } from './util';
+import { envPath, fixDriveCasingInWindows, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
 
 type GopkgsDone = (res: Map<string, PackageInfo>) => void;
 interface Cache {
diff --git a/src/goReferences.ts b/src/goReferences.ts
index 44a86fc..1aba8d9 100644
--- a/src/goReferences.ts
+++ b/src/goReferences.ts
@@ -15,9 +15,9 @@
 	canonicalizeGOPATHPrefix,
 	getBinPath,
 	getFileArchive,
-	getGoConfig,
-	killTree
+	getGoConfig
 } from './util';
+import {killProcessTree} from './utils/processUtils';
 
 export class GoReferenceProvider implements vscode.ReferenceProvider {
 	public provideReferences(
@@ -102,7 +102,7 @@
 			if (process.pid) {
 				process.stdin.end(getFileArchive(document));
 			}
-			token.onCancellationRequested(() => killTree(process.pid));
+			token.onCancellationRequested(() => killProcessTree(process));
 		});
 	}
 }
diff --git a/src/goRename.ts b/src/goRename.ts
index 55a97f7..ff71aa3 100644
--- a/src/goRename.ts
+++ b/src/goRename.ts
@@ -11,7 +11,8 @@
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool } from './goInstallTools';
 import { outputChannel } from './goStatus';
-import { byteOffsetAt, canonicalizeGOPATHPrefix, getBinPath, getGoConfig, killTree } from './util';
+import { byteOffsetAt, canonicalizeGOPATHPrefix, getBinPath, getGoConfig } from './util';
+import {killProcessTree} from './utils/processUtils';
 
 export class GoRenameProvider implements vscode.RenameProvider {
 	public provideRenameEdits(
@@ -50,7 +51,7 @@
 
 			let p: cp.ChildProcess;
 			if (token) {
-				token.onCancellationRequested(() => killTree(p.pid));
+				token.onCancellationRequested(() => killProcessTree(p));
 			}
 
 			p = cp.execFile(gorename, gorenameArgs, { env }, (err, stdout, stderr) => {
diff --git a/src/goSuggest.ts b/src/goSuggest.ts
index e676ca0..80f3278 100644
--- a/src/goSuggest.ts
+++ b/src/goSuggest.ts
@@ -13,7 +13,6 @@
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
 import { isModSupported } from './goModules';
 import { getImportablePackages, PackageInfo } from './goPackages';
-import { getCurrentGoWorkspaceFromGOPATH } from './goPath';
 import {
 	byteOffsetAt,
 	getBinPath,
@@ -28,6 +27,7 @@
 	parseFilePrelude,
 	runGodoc
 } from './util';
+import { getCurrentGoWorkspaceFromGOPATH } from './utils/goPath';
 
 function vscodeKindFromGoCodeClass(kind: string, type: string): vscode.CompletionItemKind {
 	switch (kind) {
diff --git a/src/goSymbol.ts b/src/goSymbol.ts
index b97e018..9c3bb15 100644
--- a/src/goSymbol.ts
+++ b/src/goSymbol.ts
@@ -9,8 +9,9 @@
 import vscode = require('vscode');
 import { toolExecutionEnvironment } from './goEnv';
 import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
-import { getCurrentGoRoot } from './goPath';
-import { getBinPath, getGoConfig, getWorkspaceFolderPath, killTree } from './util';
+import { getBinPath, getGoConfig, getWorkspaceFolderPath} from './util';
+import { getCurrentGoRoot } from './utils/goPath';
+import {killProcessTree} from './utils/processUtils';
 
 // Keep in sync with github.com/acroca/go-symbols'
 interface GoSymbolDeclaration {
@@ -118,7 +119,7 @@
 	let p: cp.ChildProcess;
 
 	if (token) {
-		token.onCancellationRequested(() => killTree(p.pid));
+		token.onCancellationRequested(() => killProcessTree(p));
 	}
 
 	return new Promise((resolve, reject) => {
diff --git a/src/goTypeDefinition.ts b/src/goTypeDefinition.ts
index 100deb1..0487d70 100644
--- a/src/goTypeDefinition.ts
+++ b/src/goTypeDefinition.ts
@@ -17,9 +17,9 @@
 	getBinPath,
 	getFileArchive,
 	getGoConfig,
-	goBuiltinTypes,
-	killTree
+	goBuiltinTypes
 } from './util';
+import {killProcessTree} from './utils/processUtils';
 
 interface GuruDescribeOutput {
 	desc: string;
@@ -130,7 +130,7 @@
 			if (process.pid) {
 				process.stdin.end(getFileArchive(document));
 			}
-			token.onCancellationRequested(() => killTree(process.pid));
+			token.onCancellationRequested(() => killProcessTree(process));
 		});
 	}
 }
diff --git a/src/testUtils.ts b/src/testUtils.ts
index f983e0b..f384c39 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -12,16 +12,16 @@
 import { getCurrentPackage } from './goModules';
 import { GoDocumentSymbolProvider } from './goOutline';
 import { getNonVendorPackages } from './goPackages';
-import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH, parseEnvFile } from './goPath';
 import {
 	getBinPath,
 	getCurrentGoPath,
 	getGoVersion,
 	getTempFilePath,
-	killTree,
 	LineBuffer,
 	resolvePath
 } from './util';
+import { envPath, getCurrentGoRoot, getCurrentGoWorkspaceFromGOPATH, parseEnvFile } from './utils/goPath';
+import {killProcessTree} from './utils/processUtils';
 
 const outputChannel = vscode.window.createOutputChannel('Go Tests');
 const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
@@ -420,7 +420,7 @@
 export function cancelRunningTests(): Thenable<boolean> {
 	return new Promise<boolean>((resolve, reject) => {
 		runningTestProcesses.forEach((tp) => {
-			killTree(tp.pid);
+			killProcessTree(tp);
 		});
 		// All processes are now dead. Empty the array to prepare for the next run.
 		runningTestProcesses.splice(0, runningTestProcesses.length);
diff --git a/src/util.ts b/src/util.ts
index ec2e6df..54737f1 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -8,7 +8,6 @@
 import os = require('os');
 import path = require('path');
 import semver = require('semver');
-import kill = require('tree-kill');
 import util = require('util');
 import vscode = require('vscode');
 import { NearestNeighborDict, Node } from './avlTree';
@@ -16,6 +15,7 @@
 import { toolExecutionEnvironment } from './goEnv';
 import { buildDiagnosticCollection, lintDiagnosticCollection, vetDiagnosticCollection } from './goMain';
 import { getCurrentPackage } from './goModules';
+import { outputChannel } from './goStatus';
 import {
 	envPath,
 	fixDriveCasingInWindows,
@@ -23,8 +23,8 @@
 	getCurrentGoRoot,
 	getInferredGopath,
 	resolveHomeDir,
-} from './goPath';
-import { outputChannel } from './goStatus';
+} from './utils/goPath';
+import { killProcessTree } from './utils/processUtils';
 
 let userNameHash: number = 0;
 
@@ -687,7 +687,7 @@
 	if (token) {
 		token.onCancellationRequested(() => {
 			if (p) {
-				killTree(p.pid);
+				killProcessTree(p);
 			}
 		});
 	}
@@ -864,28 +864,6 @@
 	}
 }
 
-export const killTree = (processId: number): void => {
-	try {
-		kill(processId, (err) => {
-			if (err) {
-				console.log(`Error killing process tree: ${err}`);
-			}
-		});
-	} catch (err) {
-		console.log(`Error killing process tree: ${err}`);
-	}
-};
-
-export function killProcess(p: cp.ChildProcess) {
-	if (p) {
-		try {
-			p.kill();
-		} catch (e) {
-			console.log('Error killing process: ' + e);
-		}
-	}
-}
-
 export function makeMemoizedByteOffsetConverter(buffer: Buffer): (byteOffset: number) => number {
 	const defaultValue = new Node<number, number>(0, 0); // 0 bytes will always be 0 characters
 	const memo = new NearestNeighborDict(defaultValue, NearestNeighborDict.NUMERIC_DISTANCE_FUNCTION);
@@ -1029,7 +1007,7 @@
 
 			if (token) {
 				token.onCancellationRequested(() => {
-					killTree(p.pid);
+					killProcessTree(p);
 				});
 			}
 		});
diff --git a/src/goPath.ts b/src/utils/goPath.ts
similarity index 100%
rename from src/goPath.ts
rename to src/utils/goPath.ts
diff --git a/src/processUtils.ts b/src/utils/processUtils.ts
similarity index 60%
rename from src/processUtils.ts
rename to src/utils/processUtils.ts
index 12aacca..5c6d343 100644
--- a/src/processUtils.ts
+++ b/src/utils/processUtils.ts
@@ -28,3 +28,22 @@
 		});
 	});
 }
+
+// Kill a process.
+//
+// READ THIS BEFORE USING THE FUNCTION:
+//
+// TODO: This function is kept for historical reasons and should be removed once
+// its user (go-outline) is replaced. Outlining uses this function and not
+// killProcessTree because of performance issues that were observed in the past.
+// See https://go-review.googlesource.com/c/vscode-go/+/242518/ for more
+// details and background.
+export function killProcess(p: ChildProcess) {
+	if (p) {
+		try {
+			p.kill();
+		} catch (e) {
+			console.log('Error killing process: ' + e);
+		}
+	}
+}
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index b8c752d..5c2e0fe 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -364,8 +364,9 @@
 
 	test('Linting - concurrent process cancelation', async () => {
 		const util = require('../../src/util');
+		const processutil = require('../../src/utils/processUtils');
 		sinon.spy(util, 'runTool');
-		sinon.spy(util, 'killTree');
+		sinon.spy(processutil, 'killProcessTree');
 
 		const config = Object.create(vscode.workspace.getConfiguration('go'), {
 			vetOnSave: { value: 'package' },
@@ -382,7 +383,7 @@
 			goLint(vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_2.go')), config)
 		]);
 		assert.equal(util.runTool.callCount, 2, 'should have launched 2 lint jobs');
-		assert.equal(util.killTree.callCount, 1, 'should have killed 1 lint job before launching the next');
+		assert.equal(processutil.killProcessTree.callCount, 1, 'should have killed 1 lint job before launching the next');
 	});
 
 	test('Error checking', async () => {
diff --git a/test/integration/statusbar.test.ts b/test/integration/statusbar.test.ts
index e4347aa..8838485 100644
--- a/test/integration/statusbar.test.ts
+++ b/test/integration/statusbar.test.ts
@@ -23,8 +23,8 @@
 	setSelectedGo,
 } from '../../src/goEnvironmentStatus';
 import { updateGoVarsFromConfig } from '../../src/goInstallTools';
-import { getCurrentGoRoot } from '../../src/goPath';
 import ourutil = require('../../src/util');
+import { getCurrentGoRoot } from '../../src/utils/goPath';
 
 describe('#initGoStatusBar()', function () {
 	this.beforeAll(async () => {