Skip to content

Commit 27b6a84

Browse files
Merge master into feature/q-dev-ux
2 parents 8c3105f + 65b0148 commit 27b6a84

20 files changed

+1023
-78
lines changed

docs/vscode_behaviors.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# VS Code Behaviors
2+
3+
Many VS Code behavoirs for certain APIs or user interactions with the IDE are not clearly documented,
4+
or documented at all. Please add any findings to this document.
5+
6+
## `deactivate()` - extension shutdown
7+
8+
This method is defined as part of the VS Code extension API, and is run on a **graceful** shutdown
9+
for each extension.
10+
11+
- Our extension and its `deactivate()` function are in the Extension Host process [1]
12+
- The Extension Host process has at most 5 seconds to shut down, after which it will exit. [1]
13+
- The vscode API will be unreliable at deactivation time. So certain VSC APIs like the filesystem may not work. [1]
14+
- The VSC Filesystem API has been confirmed to not work
15+
- In `Run & Debug` mode, closing the Debug IDE instance behaves differently depending on how it is closed
16+
- The regular close button in the Debug IDE instance results in a graceful shutdown
17+
- The red square in the root IDE instance to stop the debugging session results on a non-graceful shutdown, meaning `deactivate()` is not run.
18+
19+
Sources:
20+
21+
- [[1]](https://github.com/Microsoft/vscode/issues/47881#issuecomment-381910587)
22+
- [[2]](https://github.com/microsoft/vscode/issues/122825#issuecomment-814218149)

packages/amazonq/src/extensionNode.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as vscode from 'vscode'
77
import { activateAmazonQCommon, amazonQContextPrefix, deactivateCommon } from './extension'
88
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
99
import { activate as activateQGumby } from 'aws-core-vscode/amazonqGumby'
10-
import { ExtContext, globals } from 'aws-core-vscode/shared'
10+
import { ExtContext, globals, CrashMonitoring } from 'aws-core-vscode/shared'
1111
import { filetypes, SchemaService } from 'aws-core-vscode/sharedNode'
1212
import { updateDevMode } from 'aws-core-vscode/dev'
1313
import { CommonAuthViewProvider } from 'aws-core-vscode/login'
@@ -32,6 +32,8 @@ export async function activate(context: vscode.ExtensionContext) {
3232
* the code compatible with web and move it to {@link activateAmazonQCommon}.
3333
*/
3434
async function activateAmazonQNode(context: vscode.ExtensionContext) {
35+
await (await CrashMonitoring.instance()).start()
36+
3537
const extContext = {
3638
extensionContext: context,
3739
}
@@ -77,6 +79,7 @@ async function setupDevMode(context: vscode.ExtensionContext) {
7779
'deleteSsoConnections',
7880
'expireSsoConnections',
7981
'editAuthConnections',
82+
'forceIdeCrash',
8083
],
8184
}
8285

@@ -92,5 +95,6 @@ async function setupDevMode(context: vscode.ExtensionContext) {
9295
}
9396

9497
export async function deactivate() {
95-
await deactivateCommon()
98+
// Run concurrently to speed up execution. stop() does not throw so it is safe
99+
await Promise.all([(await CrashMonitoring.instance()).stop(), deactivateCommon()])
96100
}

packages/core/src/dev/activation.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { entries } from '../shared/utilities/tsUtils'
2424
import { getEnvironmentSpecificMemento } from '../shared/utilities/mementos'
2525
import { setContext } from '../shared'
2626
import { telemetry } from '../shared/telemetry'
27+
import { getSessionId } from '../shared/telemetry/util'
2728

2829
interface MenuOption {
2930
readonly label: string
@@ -41,6 +42,7 @@ export type DevFunction =
4142
| 'deleteSsoConnections'
4243
| 'expireSsoConnections'
4344
| 'editAuthConnections'
45+
| 'forceIdeCrash'
4446

4547
export type DevOptions = {
4648
context: vscode.ExtensionContext
@@ -105,6 +107,11 @@ const menuOptions: Record<DevFunction, MenuOption> = {
105107
detail: 'Opens editor to all Auth Connections the extension is using.',
106108
executor: editSsoConnections,
107109
},
110+
forceIdeCrash: {
111+
label: 'Crash: Force IDE ExtHost Crash',
112+
detail: `Will SIGKILL ExtHost, { pid: ${process.pid}, sessionId: '${getSessionId().slice(0, 8)}-...' }, but the IDE itself will not crash.`,
113+
executor: forceQuitIde,
114+
},
108115
}
109116

110117
/**
@@ -216,6 +223,7 @@ function isSecrets(obj: vscode.Memento | vscode.SecretStorage): obj is vscode.Se
216223

217224
class VirtualObjectFile implements FileProvider {
218225
private mTime = 0
226+
private size = 0
219227
private readonly onDidChangeEmitter = new vscode.EventEmitter<void>()
220228
public readonly onDidChange = this.onDidChangeEmitter.event
221229

@@ -227,22 +235,24 @@ class VirtualObjectFile implements FileProvider {
227235
/** Emits an event indicating this file's content has changed */
228236
public refresh() {
229237
/**
230-
* Per {@link vscode.FileSystemProvider.onDidChangeFile}, if the mTime does not change, new file content may
231-
* not be retrieved. Without this, when we emit a change the text editor did not update.
238+
* Per {@link vscode.FileSystemProvider.onDidChangeFile}, if the mTime and/or size does not change, new file content may
239+
* not be retrieved due to optimizations. Without this, when we emit a change the text editor did not update.
232240
*/
233241
this.mTime++
234242
this.onDidChangeEmitter.fire()
235243
}
236244

237245
public stat(): { ctime: number; mtime: number; size: number } {
238246
// This would need to be filled out to track conflicts
239-
return { ctime: 0, mtime: this.mTime, size: 0 }
247+
return { ctime: 0, mtime: this.mTime, size: this.size }
240248
}
241249

242250
public async read(): Promise<Uint8Array> {
243251
const encoder = new TextEncoder()
244252

245-
return encoder.encode(await this.readStore(this.key))
253+
const data = encoder.encode(await this.readStore(this.key))
254+
this.size = data.length
255+
return data
246256
}
247257

248258
public async write(content: Uint8Array): Promise<void> {
@@ -435,6 +445,15 @@ async function expireSsoConnections() {
435445
)
436446
}
437447

448+
export function forceQuitIde() {
449+
// This current process is the ExtensionHost. Killing it will cause all the extensions to crash
450+
// for the current ExtensionHost (unless using "extensions.experimental.affinity").
451+
// The IDE instance itself will remaing running, but a new ExtHost will spawn within it.
452+
// The PPID (parent process) is vscode itself, killing it crashes all vscode instances.
453+
const vsCodePid = process.pid
454+
process.kill(vsCodePid, 'SIGKILL') // SIGTERM would be the graceful shutdown
455+
}
456+
438457
async function showState(path: string) {
439458
const uri = vscode.Uri.parse(`aws-dev2://state/${path}-${targetContext.extension.id}`)
440459
const doc = await vscode.workspace.openTextDocument(uri)

packages/core/src/extensionNode.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ import { ExtensionUse } from './auth/utils'
5959
import { ExtStartUpSources } from './shared/telemetry'
6060
import { activate as activateThreatComposerEditor } from './threatComposer/activation'
6161
import { isSsoConnection, hasScopes } from './auth/connection'
62-
import { setContext } from './shared'
62+
import { CrashMonitoring, setContext } from './shared'
6363

6464
let localize: nls.LocalizeFunc
6565

@@ -78,6 +78,8 @@ export async function activate(context: vscode.ExtensionContext) {
7878
// IMPORTANT: If you are doing setup that should also work in web mode (browser), it should be done in the function below
7979
const extContext = await activateCommon(context, contextPrefix, false)
8080

81+
await (await CrashMonitoring.instance()).start()
82+
8183
initializeCredentialsProviderManager()
8284

8385
const toolkitEnvDetails = getExtEnvironmentDetails()
@@ -251,7 +253,8 @@ export async function activate(context: vscode.ExtensionContext) {
251253
}
252254

253255
export async function deactivate() {
254-
await deactivateCommon()
256+
// Run concurrently to speed up execution. stop() does not throw so it is safe
257+
await Promise.all([await (await CrashMonitoring.instance()).stop(), deactivateCommon()])
255258
await globals.resourceManager.dispose()
256259
}
257260

packages/core/src/shared/constants.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,14 @@ export const amazonQHelpUrl = 'https://aws.amazon.com/q/'
157157
// URL for Amazon Q VS Code
158158
export const amazonQVscodeMarketplace =
159159
'https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.amazon-q-vscode'
160+
161+
/**
162+
* Names of directories relevant to the crash reporting functionality.
163+
*
164+
* Moved here to resolve circular dependency issues.
165+
*/
166+
export const crashMonitoringDirNames = {
167+
root: 'crashMonitoring',
168+
running: 'running',
169+
shutdown: 'shutdown',
170+
} as const

0 commit comments

Comments
 (0)