Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/vscode_behaviors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# VS Code Behaviors

Many VS Code behavoirs for certain APIs or user interactions with the IDE are not clearly documented,
or documented at all. Please add any findings to this document.

## `deactivate()` - extension shutdown

This method is defined as part of the VS Code extension API, and is run on a **graceful** shutdown
for each extension.

- Our extension and its `deactivate()` function are in the Extension Host process [1]
- The Extension Host process has at most 5 seconds to shut down, after which it will exit. [1]
- The vscode API will be unreliable at deactivation time. So certain VSC APIs like the filesystem may not work. [1]
- The VSC Filesystem API has been confirmed to not work
- In `Run & Debug` mode, closing the Debug IDE instance behaves differently depending on how it is closed
- The regular close button in the Debug IDE instance results in a graceful shutdown
- The red square in the root IDE instance to stop the debugging session results on a non-graceful shutdown, meaning `deactivate()` is not run.

Sources:

- [[1]](https://github.com/Microsoft/vscode/issues/47881#issuecomment-381910587)
- [[2]](https://github.com/microsoft/vscode/issues/122825#issuecomment-814218149)
8 changes: 6 additions & 2 deletions packages/amazonq/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as vscode from 'vscode'
import { activateAmazonQCommon, amazonQContextPrefix, deactivateCommon } from './extension'
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
import { activate as activateQGumby } from 'aws-core-vscode/amazonqGumby'
import { ExtContext, globals } from 'aws-core-vscode/shared'
import { ExtContext, globals, CrashMonitoring } from 'aws-core-vscode/shared'
import { filetypes, SchemaService } from 'aws-core-vscode/sharedNode'
import { updateDevMode } from 'aws-core-vscode/dev'
import { CommonAuthViewProvider } from 'aws-core-vscode/login'
Expand All @@ -32,6 +32,8 @@ export async function activate(context: vscode.ExtensionContext) {
* the code compatible with web and move it to {@link activateAmazonQCommon}.
*/
async function activateAmazonQNode(context: vscode.ExtensionContext) {
await (await CrashMonitoring.instance()).start()

const extContext = {
extensionContext: context,
}
Expand Down Expand Up @@ -77,6 +79,7 @@ async function setupDevMode(context: vscode.ExtensionContext) {
'deleteSsoConnections',
'expireSsoConnections',
'editAuthConnections',
'forceIdeCrash',
],
}

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

export async function deactivate() {
await deactivateCommon()
// Run concurrently to speed up execution. stop() does not throw so it is safe
await Promise.all([(await CrashMonitoring.instance()).stop(), deactivateCommon()])
}
27 changes: 23 additions & 4 deletions packages/core/src/dev/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { entries } from '../shared/utilities/tsUtils'
import { getEnvironmentSpecificMemento } from '../shared/utilities/mementos'
import { setContext } from '../shared'
import { telemetry } from '../shared/telemetry'
import { getSessionId } from '../shared/telemetry/util'

interface MenuOption {
readonly label: string
Expand All @@ -41,6 +42,7 @@ export type DevFunction =
| 'deleteSsoConnections'
| 'expireSsoConnections'
| 'editAuthConnections'
| 'forceIdeCrash'

export type DevOptions = {
context: vscode.ExtensionContext
Expand Down Expand Up @@ -105,6 +107,11 @@ const menuOptions: Record<DevFunction, MenuOption> = {
detail: 'Opens editor to all Auth Connections the extension is using.',
executor: editSsoConnections,
},
forceIdeCrash: {
label: 'Crash: Force IDE ExtHost Crash',
detail: `Will SIGKILL ExtHost, { pid: ${process.pid}, sessionId: '${getSessionId().slice(0, 8)}-...' }, but the IDE itself will not crash.`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this different than vscode's Restart Extension Host command?
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restart Extension Host still results in a graceful shutdown of the extension host (deactivate() runs), while this one is non-graceful

executor: forceQuitIde,
},
}

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

class VirtualObjectFile implements FileProvider {
private mTime = 0
private size = 0
private readonly onDidChangeEmitter = new vscode.EventEmitter<void>()
public readonly onDidChange = this.onDidChangeEmitter.event

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

public stat(): { ctime: number; mtime: number; size: number } {
// This would need to be filled out to track conflicts
return { ctime: 0, mtime: this.mTime, size: 0 }
return { ctime: 0, mtime: this.mTime, size: this.size }
}

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

return encoder.encode(await this.readStore(this.key))
const data = encoder.encode(await this.readStore(this.key))
this.size = data.length
return data
}

public async write(content: Uint8Array): Promise<void> {
Expand Down Expand Up @@ -435,6 +445,15 @@ async function expireSsoConnections() {
)
}

export function forceQuitIde() {
// This current process is the ExtensionHost. Killing it will cause all the extensions to crash
// for the current ExtensionHost (unless using "extensions.experimental.affinity").
// The IDE instance itself will remaing running, but a new ExtHost will spawn within it.
// The PPID (parent process) is vscode itself, killing it crashes all vscode instances.
const vsCodePid = process.pid
process.kill(vsCodePid, 'SIGKILL') // SIGTERM would be the graceful shutdown
}

async function showState(path: string) {
const uri = vscode.Uri.parse(`aws-dev2://state/${path}-${targetContext.extension.id}`)
const doc = await vscode.workspace.openTextDocument(uri)
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import { ExtensionUse } from './auth/utils'
import { ExtStartUpSources } from './shared/telemetry'
import { activate as activateThreatComposerEditor } from './threatComposer/activation'
import { isSsoConnection, hasScopes } from './auth/connection'
import { setContext } from './shared'
import { CrashMonitoring, setContext } from './shared'

let localize: nls.LocalizeFunc

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

await (await CrashMonitoring.instance()).start()

initializeCredentialsProviderManager()

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

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

Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,14 @@ export const amazonQHelpUrl = 'https://aws.amazon.com/q/'
// URL for Amazon Q VS Code
export const amazonQVscodeMarketplace =
'https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.amazon-q-vscode'

/**
* Names of directories relevant to the crash reporting functionality.
*
* Moved here to resolve circular dependency issues.
*/
export const crashMonitoringDirNames = {
root: 'crashMonitoring',
running: 'running',
shutdown: 'shutdown',
} as const
Loading
Loading