Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add instrumentation support to the typescript code #12991

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2c3dcd4
add instrumentation support to the typescript code
fearthecowboy Nov 22, 2024
8db9949
fix linter
fearthecowboy Nov 22, 2024
ad614bf
missed file
fearthecowboy Nov 25, 2024
1b3c84e
update to a newer image?
fearthecowboy Nov 25, 2024
73abb77
Merge branch 'main' into dev/garretts/instrumentation
fearthecowboy Nov 25, 2024
47f172e
merged from main
fearthecowboy Dec 3, 2024
8c6e727
merged
fearthecowboy Dec 3, 2024
ad53e59
Merge branch 'dev/garretts/instrumentation' of https://github.com/mic…
fearthecowboy Dec 3, 2024
53dd899
work around webpack
fearthecowboy Jan 3, 2025
c5d8af4
webpack! Arg!
fearthecowboy Jan 3, 2025
35172cb
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
fearthecowboy Jan 14, 2025
fbb07e6
cleanup
fearthecowboy Jan 15, 2025
d272d97
cleanups
fearthecowboy Jan 15, 2025
9a22dc2
remove spaces
fearthecowboy Jan 15, 2025
b147b2b
must use eval to work around webpack
fearthecowboy Jan 15, 2025
86c26c1
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
fearthecowboy Jan 17, 2025
2b87424
need vscode in here
fearthecowboy Feb 6, 2025
9270034
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
fearthecowboy Feb 6, 2025
30044a4
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
fearthecowboy Feb 10, 2025
b016317
fix line endings?
fearthecowboy Feb 10, 2025
4c206b2
fix line endings?
fearthecowboy Feb 10, 2025
d71e4fb
Add comment
fearthecowboy Feb 10, 2025
9d09405
Merge branch 'main' of https://github.com/microsoft/vscode-cpptools i…
fearthecowboy Feb 12, 2025
0a2c368
Merge branch 'dev/garretts/instrumentation' of https://github.com/mic…
fearthecowboy Feb 12, 2025
fe6a369
Added instrumentation to copilothoverprovider
fearthecowboy Feb 14, 2025
249f93e
Merge branch 'main' into dev/garretts/instrumentation
fearthecowboy Feb 14, 2025
0823147
inject instrumentation code via perfecto instead
fearthecowboy Feb 15, 2025
20749be
Merge branch 'dev/garretts/instrumentation' of https://github.com/mic…
fearthecowboy Feb 15, 2025
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
68 changes: 33 additions & 35 deletions Extension/src/LanguageServer/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { logAndReturn } from '../Utility/Async/returns';
import { is } from '../Utility/System/guards';
import * as util from '../common';
import { isWindows } from '../constants';
import { instrument, isInstrumentationEnabled } from '../instrumentation';
import { DebugProtocolParams, Logger, ShowWarningParams, getDiagnosticsChannel, getOutputChannelLogger, logDebugProtocol, logLocalized, showWarning } from '../logger';
import { localizedStringCount, lookupString } from '../nativeStrings';
import { SessionState } from '../sessionState';
Expand Down Expand Up @@ -855,6 +856,16 @@ export interface Client {
}

export function createClient(workspaceFolder?: vscode.WorkspaceFolder): Client {
if (isInstrumentationEnabled()) {
instrument(vscode.languages, { name: "languages" });
instrument(vscode.window, { name: "window" });
instrument(vscode.workspace, { name: "workspace" });
instrument(vscode.commands, { name: "commands" });
instrument(vscode.debug, { name: "debug" });
instrument(vscode.env, { name: "env" });
instrument(vscode.extensions, { name: "extensions" });
return instrument(new DefaultClient(workspaceFolder), { ignore: ["enqueue", "onInterval", "logTelemetry"] });
}
return new DefaultClient(workspaceFolder);
}

Expand Down Expand Up @@ -1338,31 +1349,33 @@ export class DefaultClient implements Client {
this.currentCopilotHoverEnabled = new PersistentWorkspaceState<string>("cpp.copilotHover", settings.copilotHover);
if (settings.copilotHover !== "disabled") {
this.copilotHoverProvider = new CopilotHoverProvider(this);
this.disposables.push(vscode.languages.registerHoverProvider(util.documentSelector, this.copilotHoverProvider));
this.disposables.push(vscode.languages.registerHoverProvider(util.documentSelector, instrument(this.copilotHoverProvider)));
}

if (settings.copilotHover !== this.currentCopilotHoverEnabled.Value) {
this.currentCopilotHoverEnabled.Value = settings.copilotHover;
}
this.disposables.push(vscode.languages.registerHoverProvider(util.documentSelector, this.hoverProvider));
this.disposables.push(vscode.languages.registerInlayHintsProvider(util.documentSelector, this.inlayHintsProvider));
this.disposables.push(vscode.languages.registerRenameProvider(util.documentSelector, new RenameProvider(this)));
this.disposables.push(vscode.languages.registerReferenceProvider(util.documentSelector, new FindAllReferencesProvider(this)));
this.disposables.push(vscode.languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this)));
this.disposables.push(vscode.languages.registerDocumentSymbolProvider(util.documentSelector, new DocumentSymbolProvider(), undefined));
this.disposables.push(vscode.languages.registerCodeActionsProvider(util.documentSelector, new CodeActionProvider(this), undefined));
this.disposables.push(vscode.languages.registerCallHierarchyProvider(util.documentSelector, new CallHierarchyProvider(this)));
this.disposables.push(vscode.languages.registerHoverProvider(util.documentSelector, instrument(this.hoverProvider)));
this.disposables.push(vscode.languages.registerInlayHintsProvider(util.documentSelector, instrument(this.inlayHintsProvider)));
this.disposables.push(vscode.languages.registerRenameProvider(util.documentSelector, instrument(new RenameProvider(this))));
this.disposables.push(vscode.languages.registerReferenceProvider(util.documentSelector, instrument(new FindAllReferencesProvider(this))));
this.disposables.push(vscode.languages.registerWorkspaceSymbolProvider(instrument(new WorkspaceSymbolProvider(this))));
this.disposables.push(vscode.languages.registerDocumentSymbolProvider(util.documentSelector, instrument(new DocumentSymbolProvider()), undefined));
this.disposables.push(vscode.languages.registerCodeActionsProvider(util.documentSelector, instrument(new CodeActionProvider(this)), undefined));
this.disposables.push(vscode.languages.registerCallHierarchyProvider(util.documentSelector, instrument(new CallHierarchyProvider(this))));

// Because formatting and codeFolding can vary per folder, we need to register these providers once
// and leave them registered. The decision of whether to provide results needs to be made on a per folder basis,
// within the providers themselves.
this.documentFormattingProviderDisposable = vscode.languages.registerDocumentFormattingEditProvider(util.documentSelector, new DocumentFormattingEditProvider(this));
this.formattingRangeProviderDisposable = vscode.languages.registerDocumentRangeFormattingEditProvider(util.documentSelector, new DocumentRangeFormattingEditProvider(this));
this.onTypeFormattingProviderDisposable = vscode.languages.registerOnTypeFormattingEditProvider(util.documentSelector, new OnTypeFormattingEditProvider(this), ";", "}", "\n");
this.documentFormattingProviderDisposable = vscode.languages.registerDocumentFormattingEditProvider(util.documentSelector, instrument(new DocumentFormattingEditProvider(this)));
this.formattingRangeProviderDisposable = vscode.languages.registerDocumentRangeFormattingEditProvider(util.documentSelector, instrument(new DocumentRangeFormattingEditProvider(this)));
this.onTypeFormattingProviderDisposable = vscode.languages.registerOnTypeFormattingEditProvider(util.documentSelector, instrument(new OnTypeFormattingEditProvider(this)), ";", "}", "\n");

this.codeFoldingProvider = new FoldingRangeProvider(this);
this.codeFoldingProviderDisposable = vscode.languages.registerFoldingRangeProvider(util.documentSelector, this.codeFoldingProvider);
this.codeFoldingProviderDisposable = vscode.languages.registerFoldingRangeProvider(util.documentSelector, instrument(this.codeFoldingProvider));

if (settings.isEnhancedColorizationEnabled && semanticTokensLegend) {
this.semanticTokensProvider = new SemanticTokensProvider();
this.semanticTokensProvider = instrument(new SemanticTokensProvider());
this.semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(util.documentSelector, this.semanticTokensProvider, semanticTokensLegend);
}

Expand Down Expand Up @@ -1745,8 +1758,7 @@ export class DefaultClient implements Client {
const oldLoggingLevelLogged: boolean = this.loggingLevel > 1;
this.loggingLevel = util.getNumericLoggingLevel(changedSettings.loggingLevel);
if (oldLoggingLevelLogged || this.loggingLevel > 1) {
const out: Logger = getOutputChannelLogger();
out.appendLine(localize({ key: "loggingLevel.changed", comment: ["{0} is the setting name 'loggingLevel', {1} is a string value such as 'Debug'"] }, "{0} has changed to: {1}", "loggingLevel", changedSettings.loggingLevel));
getOutputChannelLogger().appendLine(localize({ key: "loggingLevel.changed", comment: ["{0} is the setting name 'loggingLevel', {1} is a string value such as 'Debug'"] }, "{0} has changed to: {1}", "loggingLevel", changedSettings.loggingLevel));
}
}
const settings: CppSettings = new CppSettings();
Expand Down Expand Up @@ -2763,12 +2775,7 @@ export class DefaultClient implements Client {
const status: IntelliSenseStatus = { status: Status.IntelliSenseCompiling };
testHook.updateStatus(status);
} else if (message.endsWith("IntelliSense done")) {
const settings: CppSettings = new CppSettings();
if (util.getNumericLoggingLevel(settings.loggingLevel) >= 6) {
const out: Logger = getOutputChannelLogger();
const duration: number = Date.now() - timeStamp;
out.appendLine(localize("update.intellisense.time", "Update IntelliSense time (sec): {0}", duration / 1000));
}
getOutputChannelLogger().appendLineAtLevel(6, localize("update.intellisense.time", "Update IntelliSense time (sec): {0}", (Date.now() - timeStamp) / 1000));
this.model.isUpdatingIntelliSense.Value = false;
const status: IntelliSenseStatus = { status: Status.IntelliSenseReady };
testHook.updateStatus(status);
Expand Down Expand Up @@ -3200,11 +3207,8 @@ export class DefaultClient implements Client {
return;
}

const settings: CppSettings = new CppSettings();
const out: Logger = getOutputChannelLogger();
if (util.getNumericLoggingLevel(settings.loggingLevel) >= 6) {
out.appendLine(localize("configurations.received", "Custom configurations received:"));
}
out.appendLineAtLevel(6, localize("configurations.received", "Custom configurations received:"));
const sanitized: SourceFileConfigurationItemAdapter[] = [];
configs.forEach(item => {
if (this.isSourceFileConfigurationItem(item, providerVersion)) {
Expand All @@ -3216,10 +3220,8 @@ export class DefaultClient implements Client {
uri = item.uri.toString();
}
this.configurationLogging.set(uri, JSON.stringify(item.configuration, null, 4));
if (util.getNumericLoggingLevel(settings.loggingLevel) >= 6) {
out.appendLine(` uri: ${uri}`);
out.appendLine(` config: ${JSON.stringify(item.configuration, null, 2)}`);
}
out.appendLineAtLevel(6, ` uri: ${uri}`);
out.appendLineAtLevel(6, ` config: ${JSON.stringify(item.configuration, null, 2)}`);
if (item.configuration.includePath.some(path => path.endsWith('**'))) {
console.warn("custom include paths should not use recursive includes ('**')");
}
Expand Down Expand Up @@ -3323,11 +3325,7 @@ export class DefaultClient implements Client {
return;
}

const settings: CppSettings = new CppSettings();
if (util.getNumericLoggingLevel(settings.loggingLevel) >= 6) {
const out: Logger = getOutputChannelLogger();
out.appendLine(localize("browse.configuration.received", "Custom browse configuration received: {0}", JSON.stringify(sanitized, null, 2)));
}
getOutputChannelLogger().appendLineAtLevel(6, localize("browse.configuration.received", "Custom browse configuration received: {0}", JSON.stringify(sanitized, null, 2)));

// Separate compiler path and args before sending to language client
if (util.isString(sanitized.compilerPath)) {
Expand Down
6 changes: 2 additions & 4 deletions Extension/src/LanguageServer/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1280,10 +1280,8 @@ async function handleCrashFileRead(crashDirectory: string, crashFile: string, cr
if (crashCallStack !== prevCppCrashCallStackData) {
prevCppCrashCallStackData = crashCallStack;

const settings: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", null);
if (lines.length >= 6 && util.getNumericLoggingLevel(settings.get<string>("loggingLevel")) >= 1) {
const out: vscode.OutputChannel = getCrashCallStacksChannel();
out.appendLine(`\n${isCppToolsSrv ? "cpptools-srv" : "cpptools"}\n${crashDate.toLocaleString()}\n${signalType}${crashCallStack}`);
if (lines.length >= 6 && util.getLoggingLevel() >= 1) {
getCrashCallStacksChannel().appendLine(`\n${isCppToolsSrv ? "cpptools-srv" : "cpptools"}\n${crashDate.toLocaleString()}\n${signalType}${crashCallStack}`);
}
}

Expand Down
17 changes: 7 additions & 10 deletions Extension/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -759,10 +759,7 @@ export interface ProcessReturnType {
export async function spawnChildProcess(program: string, args: string[] = [], continueOn?: string, skipLogging?: boolean, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
// Do not use CppSettings to avoid circular require()
if (skipLogging === undefined || !skipLogging) {
const settings: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", null);
if (getNumericLoggingLevel(settings.get<string>("loggingLevel")) >= 5) {
getOutputChannelLogger().appendLine(`$ ${program} ${args.join(' ')}`);
}
getOutputChannelLogger().appendLineAtLevel(5, `$ ${program} ${args.join(' ')}`);
}
const programOutput: ProcessOutput = await spawnChildProcessImpl(program, args, continueOn, skipLogging, cancellationToken);
const exitCode: number | NodeJS.Signals | undefined = programOutput.exitCode;
Expand All @@ -789,10 +786,6 @@ interface ProcessOutput {
async function spawnChildProcessImpl(program: string, args: string[], continueOn?: string, skipLogging?: boolean, cancellationToken?: vscode.CancellationToken): Promise<ProcessOutput> {
const result = new ManualPromise<ProcessOutput>();

// Do not use CppSettings to avoid circular require()
const settings: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", null);
const loggingLevel: number = (skipLogging === undefined || !skipLogging) ? getNumericLoggingLevel(settings.get<string>("loggingLevel")) : 0;

let proc: child_process.ChildProcess;
if (await isExecutable(program)) {
proc = child_process.spawn(`.${isWindows ? '\\' : '/'}${path.basename(program)}`, args, { shell: true, cwd: path.dirname(program) });
Expand All @@ -817,8 +810,8 @@ async function spawnChildProcessImpl(program: string, args: string[], continueOn
if (proc.stdout) {
proc.stdout.on('data', data => {
const str: string = data.toString();
if (loggingLevel > 0) {
getOutputChannelLogger().append(str);
if (skipLogging === undefined || !skipLogging) {
getOutputChannelLogger().appendAtLevel(1, str);
}
stdout += str;
if (continueOn) {
Expand Down Expand Up @@ -1576,6 +1569,10 @@ function isIntegral(str: string): boolean {
return regex.test(str);
}

export function getLoggingLevel() {
return getNumericLoggingLevel(vscode.workspace.getConfiguration("C_Cpp", null).get<string>("loggingLevel"));
}

export function getNumericLoggingLevel(loggingLevel: string | undefined): number {
if (!loggingLevel) {
return 1;
Expand Down
9 changes: 2 additions & 7 deletions Extension/src/cppTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import { CppToolsTestApi, CppToolsTestHook } from 'vscode-cpptools/out/testApi';
import * as nls from 'vscode-nls';
import { CustomConfigurationProvider1, CustomConfigurationProviderCollection, getCustomConfigProviders } from './LanguageServer/customProviders';
import * as LanguageServer from './LanguageServer/extension';
import { CppSettings } from './LanguageServer/settings';
import { getNumericLoggingLevel } from './common';
import { getOutputChannel } from './logger';
import { getOutputChannelLogger } from './logger';
import * as test from './testHook';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
Expand Down Expand Up @@ -61,10 +59,7 @@ export class CppTools implements CppToolsTestApi {
if (providers.add(provider, this.version)) {
const added: CustomConfigurationProvider1 | undefined = providers.get(provider);
if (added) {
const settings: CppSettings = new CppSettings();
if (getNumericLoggingLevel(settings.loggingLevel) >= 5) {
getOutputChannel().appendLine(localize("provider.registered", "Custom configuration provider '{0}' registered", added.name));
}
getOutputChannelLogger().appendLineAtLevel(5, localize("provider.registered", "Custom configuration provider '{0}' registered", added.name));
this.providers.push(added);
LanguageServer.getClients().forEach(client => void client.onRegisterCustomConfigurationProvider(added));
this.addNotifyReadyTimer(added);
Expand Down
61 changes: 61 additions & 0 deletions Extension/src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
'use strict';

/* eslint @typescript-eslint/no-var-requires: "off" */

export interface PerfMessage<TInput = Record<string, any> | undefined> {
/** this is the 'name' of the event */
name: string;

/** this indicates the context or origin of the message */
context: Record<string, string | Set<string>>;

/** if the message contains complex data, it should be in here */
data?: TInput;

/** if the message is just a text message, this is the contents of the message */
text?: string;

/** the message can have a numeric value that indicates the 'level' or 'severity' etc */
level?: number;
}

const services = {
instrument: <T extends Record<string, any>>(instance: T, _options?: { ignore?: string[]; name?: string }): T => instance,
message: (_message: PerfMessage) => { },
init: (_vscode: any) => { },
launchSettings: undefined as Record<string, any> | undefined
};

/** Adds instrumentation to all the members of an object when instrumentation is enabled */
export function instrument<T extends Record<string, any>>(instance: T, options?: { ignore?: string[]; name?: string }): T {
return services.instrument(instance, options);
}

/** sends a perf message to the monitor */
export function sendInstrumentation(message: PerfMessage): void {
services.message(message);
}

/** verifies that the instrumentation is loaded into the global namespace */
export function isInstrumentationEnabled(): boolean {
return !!(global as any).instrumentation;
fearthecowboy marked this conversation as resolved.
Show resolved Hide resolved
}

// If the instrumentation object was *actually* loaded then we can set the services from the global object.
// Having this separate ensures that this module is wired up to the global object.
// It's not included in the previous block because if something else loads the instrumentation code first
// this is still needed so that *this* module is properly connected to the global object.
if (isInstrumentationEnabled()) {
// instrumentation services provided by the tool
services.instrument = (global as any).instrumentation.instrument;
services.message = (global as any).instrumentation.message;
services.init = (global as any).instrumentation.init;

services.init(require('vscode'));
}

(globalThis as any)["_vscode_"] = require('vscode');
Loading
Loading