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:
Родитель
3f19d0026e
Коммит
2fd2b79611
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче