ADS changes for opening XEL files (#23666)

* Initial set of changes for opening XEL files in ADS

* Code cleanup and update STS version

* Fix runtime errors

* Address comments

* Address comments and update Start button to be disabled for file session

* Code cleanup
This commit is contained in:
Sakshi Sharma 2023-07-09 10:22:12 -07:00 коммит произвёл GitHub
Родитель 3f19d0026e
Коммит 2fd2b79611
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 136 добавлений и 27 удалений

Просмотреть файл

@ -1298,9 +1298,14 @@ export interface StartProfilingParams {
ownerUri: string;
/**
* Session name
* Session name or full path of XEL file to open
*/
sessionName: string;
/**
* Identifies which type of target session name identifies
*/
sessionType: azdata.ProfilingSessionType;
}
export interface StartProfilingResponse { }

Просмотреть файл

@ -1001,10 +1001,11 @@ export class ProfilerFeature extends SqlOpsFeature<undefined> {
);
};
let startSession = (ownerUri: string, sessionName: string): Thenable<boolean> => {
let startSession = (ownerUri: string, sessionName: string, sessionType: azdata.ProfilingSessionType = azdata.ProfilingSessionType.RemoteSession): Thenable<boolean> => {
let params: contracts.StartProfilingParams = {
ownerUri,
sessionName
sessionName,
sessionType
};
return client.sendRequest(contracts.StartProfilingRequest.type, params).then(

Просмотреть файл

@ -51,6 +51,11 @@
"command": "profiler.openCreateSessionDialog",
"title": "profiler.contributes.title.openCreateSessionDialog",
"category": "%profiler.category%"
},
{
"command": "profiler.openFile",
"title": "%profiler.contributes.title.openXELFile%",
"category": "%profiler.category%"
}
],
"menus": {

Просмотреть файл

@ -5,5 +5,6 @@
"profiler.contributes.title.start": "Start",
"profiler.contributes.title.stop": "Stop",
"profiler.contributes.title.openCreateSessionDialog": "Create Profiler Season",
"profiler.category": "Profiler"
"profiler.category": "Profiler",
"profiler.contributes.title.openXELFile": "Open XEL File"
}

9
src/sql/azdata.proposed.d.ts поставляемый
Просмотреть файл

@ -2028,4 +2028,13 @@ declare module 'azdata' {
*/
setActiveCell(row: number, column: number): void;
}
export interface ProfilerProvider {
startSession(sessionId: string, sessionName: string, sessionType?: ProfilingSessionType): Thenable<boolean>;
}
export enum ProfilingSessionType {
RemoteSession = 0,
LocalFile = 1
}
}

Просмотреть файл

@ -359,8 +359,8 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
createSession(sessionId: string, createStatement: string, template: azdata.ProfilerSessionTemplate): Thenable<boolean> {
return self._proxy.$createSession(handle, sessionId, createStatement, template);
},
startSession(sessionId: string, sessionName: string): Thenable<boolean> {
return self._proxy.$startSession(handle, sessionId, sessionName);
startSession(sessionId: string, sessionName: string, sessionType?: azdata.ProfilingSessionType): Thenable<boolean> {
return self._proxy.$startSession(handle, sessionId, sessionName, sessionType);
},
stopSession(sessionId: string): Thenable<boolean> {
return self._proxy.$stopSession(handle, sessionId);

Просмотреть файл

@ -686,8 +686,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
/**
* Start a profiler session
*/
public override $startSession(handle: number, sessionId: string, sessionName: string): Thenable<boolean> {
return this._resolveProvider<azdata.ProfilerProvider>(handle).startSession(sessionId, sessionName);
public override $startSession(handle: number, sessionId: string, sessionName: string, sessionType?: azdata.ProfilingSessionType): Thenable<boolean> {
return this._resolveProvider<azdata.ProfilerProvider>(handle).startSession(sessionId, sessionName, sessionType);
}
/**
@ -739,7 +739,6 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
this._proxy.$onProfilerSessionCreated(handle, response);
}
/**
* Agent Job Provider methods
*/

Просмотреть файл

@ -687,7 +687,8 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
designers: designers,
executionPlan: executionPlan,
diagnostics: diagnostics,
env
env,
ProfilingSessionType: sqlExtHostTypes.ProfilingSessionType
};
}
};

Просмотреть файл

@ -385,7 +385,7 @@ export abstract class ExtHostDataProtocolShape {
/**
* Start a profiler session
*/
$startSession(handle: number, sessionId: string, sessionName: string): Thenable<boolean> { throw ni(); }
$startSession(handle: number, sessionId: string, sessionName: string, sessionType?: azdata.ProfilingSessionType): Thenable<boolean> { throw ni(); }
/**
* Stop a profiler session

Просмотреть файл

@ -219,6 +219,11 @@ export enum StepCompletionAction {
GoToStep = 4
}
export enum ProfilingSessionType {
RemoteSession = 0,
LocalFile = 1
}
export interface CheckBoxInfo {
row: number;
columnName: string;

Просмотреть файл

@ -44,7 +44,8 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
private _filter: ProfilerFilter = { clauses: [] };
constructor(
public connection: IConnectionProfile,
public connection: IConnectionProfile | undefined,
public fileURI: URI | undefined,
@IProfilerService private _profilerService: IProfilerService,
@INotificationService private _notificationService: INotificationService
) {
@ -63,6 +64,7 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
this._id = id;
this.state.change({ isConnected: true });
});
let searchFn = (val: { [x: string]: string }, exp: string): Array<number> => {
let ret = new Array<number>();
for (let i = 0; i < this._columns.length; i++) {
@ -156,6 +158,16 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
}
}
public get isFileSession(): boolean {
return !!this.fileURI;
}
public setConnectionState(isConnected: boolean): void {
this.state.change({
isConnected: isConnected
});
}
public setColumns(columns: Array<string>) {
this._columns = columns;
this._onColumnsChanged.fire(this.columns);
@ -193,7 +205,9 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
}
public onSessionStopped(notification: azdata.ProfilerSessionStoppedParams) {
this._notificationService.error(nls.localize("profiler.sessionStopped", "XEvent Profiler Session stopped unexpectedly on the server {0}.", this.connection.serverName));
if (!this.isFileSession) { // File session do not have serverName, so ignore notification error based off of server
this._notificationService.error(nls.localize("profiler.sessionStopped", "XEvent Profiler Session stopped unexpectedly on the server {0}.", this.connection.serverName));
}
this.state.change({
isStopped: true,

Просмотреть файл

@ -9,7 +9,7 @@ import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/commo
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ProfilerInput } from 'sql/workbench/browser/editor/profiler/profilerInput';
import * as TaskUtilities from 'sql/workbench/browser/taskUtilities';
import { IProfilerService } from 'sql/workbench/services/profiler/browser/interfaces';
import { IProfilerService, ProfilingSessionType } from 'sql/workbench/services/profiler/browser/interfaces';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ProfilerEditor } from 'sql/workbench/contrib/profiler/browser/profilerEditor';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
@ -18,6 +18,7 @@ import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
CommandsRegistry.registerCommand({
id: 'profiler.newProfiler',
@ -59,7 +60,7 @@ CommandsRegistry.registerCommand({
}
if (connectionProfile && connectionProfile.providerName === mssqlProviderName) {
let profilerInput = instantiationService.createInstance(ProfilerInput, connectionProfile);
let profilerInput = instantiationService.createInstance(ProfilerInput, connectionProfile, undefined);
editorService.openEditor(profilerInput, { pinned: true }, ACTIVE_GROUP).then(() => Promise.resolve(true));
}
});
@ -93,9 +94,23 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
} else {
// clear data when profiler is started
profilerInput.data.clear();
return profilerService.startSession(profilerInput.id, profilerInput.sessionName);
return profilerService.startSession(profilerInput.id, profilerInput.sessionName, ProfilingSessionType.RemoteSession);
}
}
return Promise.resolve(false);
}
});
CommandsRegistry.registerCommand({
id: 'profiler.openFile',
handler: async (accessor: ServicesAccessor, ...args: any[]) => {
const editorService: IEditorService = accessor.get(IEditorService);
const fileDialogService: IFileDialogService = accessor.get(IFileDialogService);
const profilerService: IProfilerService = accessor.get(IProfilerService);
const instantiationService: IInstantiationService = accessor.get(IInstantiationService)
const result = await profilerService.openFile(fileDialogService, editorService, instantiationService);
return result;
}
});

Просмотреть файл

@ -275,7 +275,7 @@ export class NewProfilerAction extends Task {
}
public async runTask(accessor: ServicesAccessor, profile: IConnectionProfile): Promise<void> {
let profilerInput = accessor.get<IInstantiationService>(IInstantiationService).createInstance(ProfilerInput, profile);
let profilerInput = accessor.get<IInstantiationService>(IInstantiationService).createInstance(ProfilerInput, profile, undefined);
await accessor.get<IEditorService>(IEditorService).openEditor(profilerInput, { pinned: true }, ACTIVE_GROUP);
let options: IConnectionCompletionOptions = {
saveTheConnection: false,

Просмотреть файл

@ -549,11 +549,13 @@ export class ProfilerEditor extends EditorPane {
// Launch the create session dialog if openning a new window.
let uiState = this._profilerService.getSessionViewState(this.input.id);
let previousSessionName = uiState && uiState.previousSessionName;
if (!this.input.sessionName && !previousSessionName) {
if (!this.input.sessionName && !previousSessionName && !this.input.isFileSession) {
this._profilerService.launchCreateSessionDialog(this.input);
}
this._updateSessionSelector(previousSessionName);
if (previousSessionName) { // skip updating session selector if there is no previous session name
this._updateSessionSelector(previousSessionName);
}
} else {
this._startAction.enabled = false;
this._stopAction.enabled = false;
@ -579,7 +581,9 @@ export class ProfilerEditor extends EditorPane {
}
if (this.input.state.isStopped) {
this._updateToolbar();
this._updateSessionSelector();
if (!this.input.isFileSession) { // skip updating session selector for File sessions
this._updateSessionSelector();
}
}
}
}

Просмотреть файл

@ -6,9 +6,11 @@
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ProfilerInput } from 'sql/workbench/browser/editor/profiler/profilerInput';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import * as azdata from 'azdata';
import { INewProfilerState } from 'sql/workbench/common/editor/profiler/profilerState';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
const PROFILER_SERVICE_ID = 'profilerService';
export const IProfilerService = createDecorator<IProfilerService>(PROFILER_SERVICE_ID);
@ -69,9 +71,9 @@ export interface IProfilerService {
*/
createSession(id: string, createStatement: string, template: azdata.ProfilerSessionTemplate): Thenable<boolean>;
/**
* Starts the session specified by the id
* Starts the session specified by the id or a session for opening file
*/
startSession(sessionId: ProfilerSessionID, sessionName: string): Thenable<boolean>;
startSession(sessionId: ProfilerSessionID, sessionName: string, sessionType?: ProfilingSessionType): Thenable<boolean>;
/**
* Pauses the session specified by the id
*/
@ -140,6 +142,18 @@ export interface IProfilerService {
* @param filter filter object
*/
saveFilter(filter: ProfilerFilter): Promise<void>;
/**
* Launches the dialog for picking a file to open in Profiler extension
* @param fileDialogService service to open file dialog
* @param editorService service to open profiler editor
* @param instantiationService service to create profiler instance
*/
openFile(fileDialogService: IFileDialogService, editorService: IEditorService, instantiationService: IInstantiationService): Promise<boolean>;
}
export enum ProfilingSessionType {
RemoteSession = 0,
LocalFile = 1
}
export interface IProfilerSettings {

Просмотреть файл

@ -4,12 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement';
import { ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate, PROFILER_SETTINGS, IProfilerSettings, EngineType, ProfilerFilter, PROFILER_FILTER_SETTINGS } from './interfaces';
import { ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate, PROFILER_SETTINGS, IProfilerSettings, EngineType, ProfilerFilter, PROFILER_FILTER_SETTINGS, ProfilingSessionType } from './interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ProfilerInput } from 'sql/workbench/browser/editor/profiler/profilerInput';
import { ProfilerColumnEditorDialog } from 'sql/workbench/services/profiler/browser/profilerColumnEditorDialog';
import * as azdata from 'azdata';
import * as nls from 'vs/nls';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -19,6 +20,8 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { Memento } from 'vs/workbench/common/memento';
import { ProfilerFilterDialog } from 'sql/workbench/services/profiler/browser/profilerFilterDialog';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
class TwoWayMap<T, K> {
private forwardMap: Map<T, K>;
@ -145,12 +148,21 @@ export class ProfilerService implements IProfilerService {
return false;
}
public async startSession(id: ProfilerSessionID, sessionName: string): Promise<boolean> {
/**
* Starts the session specified by the id or a session for opening file
* @param id session ID
* @param sessionName session name or file path to start session with
* @param sessionType distinguisher between remote session and local file
* @returns state of the run as success or failure
*/
public async startSession(id: ProfilerSessionID, sessionName: string, sessionType: ProfilingSessionType): Promise<boolean> {
if (this._idMap.has(id)) {
this.updateMemento(id, { previousSessionName: sessionName });
try {
await this._runAction(id, provider => provider.startSession(this._idMap.get(id)!, sessionName));
this._sessionMap.get(this._idMap.reverseGet(id)!)!.onSessionStateChanged({ isRunning: true, isStopped: false, isPaused: false });
await this._runAction(id, provider => provider.startSession(this._idMap.get(id)!, sessionName, sessionType));
let isRunning = sessionType === ProfilingSessionType.RemoteSession ? true : false; // Reading session stops when the file reading completes
this._sessionMap.get(this._idMap.reverseGet(id)!)!.onSessionStateChanged({ isRunning: isRunning, isStopped: false, isPaused: false });
return true;
} catch (reason) {
this._notificationService.error(reason.message);
@ -290,4 +302,28 @@ export class ProfilerService implements IProfilerService {
const config = [filter];
await this._configurationService.updateValue(PROFILER_FILTER_SETTINGS, config, ConfigurationTarget.USER);
}
public async openFile(fileDialogService: IFileDialogService, editorService: IEditorService, instantiationService: IInstantiationService): Promise<boolean> {
const fileURIs = await fileDialogService.showOpenDialog({
filters: [
{
extensions: ['xel'],
name: nls.localize('FileFilterDescription', "XEL Files")
}
],
canSelectMany: false
});
if (fileURIs?.length === 1) {
const fileURI = fileURIs[0];
let profilerInput: ProfilerInput = instantiationService.createInstance(ProfilerInput, undefined, fileURI);
await editorService.openEditor(profilerInput, { pinned: true }, ACTIVE_GROUP);
profilerInput.setConnectionState(false); // Reset connection to be not connected for File session, so that "Start" is not enabled.
const result = await this.startSession(profilerInput.id, profilerInput.fileURI.fsPath, ProfilingSessionType.LocalFile);
return result;
}
return true;
}
}