diff --git a/extensions/mssql/package.json b/extensions/mssql/package.json index d8a06b2a0e..04c60dd902 100644 --- a/extensions/mssql/package.json +++ b/extensions/mssql/package.json @@ -658,7 +658,7 @@ } }, "dependencies": { - "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.3", + "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.4", "opener": "^1.4.3", "service-downloader": "github:anthonydresser/service-downloader#0.1.4", "vscode-extension-telemetry": "^0.0.15" diff --git a/extensions/mssql/src/config.json b/extensions/mssql/src/config.json index 1013f3cb11..fb306cdd83 100644 --- a/extensions/mssql/src/config.json +++ b/extensions/mssql/src/config.json @@ -1,6 +1,6 @@ { "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", - "version": "1.5.0-alpha.20", + "version": "1.5.0-alpha.22", "downloadFileNames": { "Windows_86": "win-x86-netcoreapp2.1.zip", "Windows_64": "win-x64-netcoreapp2.1.zip", diff --git a/extensions/profiler/client/src/data/createSessionData.ts b/extensions/profiler/client/src/data/createSessionData.ts new file mode 100644 index 0000000000..2a5b78b43f --- /dev/null +++ b/extensions/profiler/client/src/data/createSessionData.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as sqlops from 'sqlops'; + +export class CreateSessionData { + public ownerUri: string; + public sessionName: string; + public templates: Array = new Array(); + + constructor(ownerUri: string, templates: Array) { + this.ownerUri = ownerUri; + this.templates = templates; + } + + public getTemplateNames(): string[] { + return this.templates.map(e => e.name); + } + + public selectTemplate(name: string): sqlops.ProfilerSessionTemplate { + return this.templates.find((t) => { return t.name === name; }); + } +} \ No newline at end of file diff --git a/extensions/profiler/client/src/dialogs/profilerCreateSessionDialog.ts b/extensions/profiler/client/src/dialogs/profilerCreateSessionDialog.ts new file mode 100644 index 0000000000..4906a48b21 --- /dev/null +++ b/extensions/profiler/client/src/dialogs/profilerCreateSessionDialog.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; +import * as nls from 'vscode-nls'; +import * as sqlops from 'sqlops'; +import * as vscode from 'vscode'; +import { CreateSessionData } from '../data/createSessionData'; + +const localize = nls.loadMessageBundle(); + +export class CreateSessionDialog { + + // Top level + private readonly DialogTitle: string = localize('createSessionDialog.newSession', 'New Session'); + private readonly CancelButtonText: string = localize('createSessionDialog.cancel', 'Cancel'); + private readonly CreateButtonText: string = localize('createSessionDialog.create', 'Create'); + private readonly DialogTitleText: string = localize('createSessionDialog.title', 'Create New Profiler Session'); + + // UI Components + private dialog: sqlops.window.modelviewdialog.Dialog; + private templatesBox: sqlops.DropDownComponent; + private sessionNameBox: sqlops.InputBoxComponent; + + private model: CreateSessionData; + + private _onSuccess: vscode.EventEmitter = new vscode.EventEmitter(); + public readonly onSuccess: vscode.Event = this._onSuccess.event; + + + constructor(ownerUri: string, templates: Array) { + if (typeof (templates) === 'undefined' || templates === null) { + throw new Error(localize('createSessionDialog.templatesInvalid', "Invalid templates list, cannot open dialog")); + } + if (typeof (ownerUri) === 'undefined' || ownerUri === null) { + throw new Error(localize('createSessionDialog.dialogOwnerInvalid', "Invalid dialog owner, cannot open dialog")); + } + this.model = new CreateSessionData(ownerUri, templates); + } + + public async showDialog(): Promise { + this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle); + this.initializeContent(); + this.dialog.okButton.onClick(() => this.execute()); + this.dialog.cancelButton.onClick(() => { }); + this.dialog.okButton.label = this.CreateButtonText; + this.dialog.cancelButton.label = this.CancelButtonText; + + sqlops.window.modelviewdialog.openDialog(this.dialog); + } + + private initializeContent(): void { + this.dialog.registerContent(async view => { + this.templatesBox = view.modelBuilder.dropDown() + .withProperties({ + values: [] + }).component(); + + this.sessionNameBox = view.modelBuilder.inputBox() + .withProperties({ + required: true, + multiline: false, + value: '' + }).component(); + + let formModel = view.modelBuilder.formContainer() + .withFormItems([{ + components: [{ + component: this.templatesBox, + title: localize('createSessionDialog.selectTemplates', "Select session template:") + }, + { + component: this.sessionNameBox, + + title: localize('createSessionDialog.enterSessionName', "Enter session name:") + }], + title: this.DialogTitleText + }]).withLayout({ width: '100%' }).component(); + + await view.initializeModel(formModel); + + if (this.model.templates) { + this.templatesBox.values = this.model.getTemplateNames(); + } + + this.sessionNameBox.onTextChanged(() => { + if (this.sessionNameBox.value.length > 0) { + this.model.sessionName = this.sessionNameBox.value; + this.dialog.okButton.enabled = true; + } else { + this.dialog.okButton.enabled = false; + } + }); + }); + } + + private async execute(): Promise { + let currentConnection = await sqlops.connection.getCurrentConnection(); + let profilerService = sqlops.dataprotocol.getProvider(currentConnection.providerName, sqlops.DataProviderType.ProfilerProvider); + + let name = this.sessionNameBox.value; + let selected = this.templatesBox.value.toString(); + let temp = this.model.selectTemplate(selected); + profilerService.createSession(this.model.ownerUri, this.sessionNameBox.value, temp); + } +} \ No newline at end of file diff --git a/extensions/profiler/client/src/mainController.ts b/extensions/profiler/client/src/mainController.ts index 152bfde048..8b95404b9a 100644 --- a/extensions/profiler/client/src/mainController.ts +++ b/extensions/profiler/client/src/mainController.ts @@ -5,28 +5,33 @@ 'use strict'; import * as vscode from 'vscode'; -import * as data from 'sqlops'; +import * as sqlops from 'sqlops'; import { ApiWrapper } from './apiWrapper'; +import { CreateSessionDialog } from './dialogs/profilerCreateSessionDialog'; /** * The main controller class that initializes the extension */ -export class MainController { - protected _apiWrapper: ApiWrapper; - protected _context: vscode.ExtensionContext; +export class MainController { + protected _apiWrapper: ApiWrapper; + protected _context: vscode.ExtensionContext; - // PUBLIC METHODS ////////////////////////////////////////////////////// - public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) { - this._apiWrapper = apiWrapper || new ApiWrapper(); - this._context = context; - } + // PUBLIC METHODS + public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) { + this._apiWrapper = apiWrapper || new ApiWrapper(); + this._context = context; + } /** * Deactivates the extension */ - public deactivate(): void { - } + public deactivate(): void { + } - public activate(): void { - } + public activate(): void { + vscode.commands.registerCommand('profiler.openCreateSessionDialog', (ownerUri: string, templates: Array) => { + let dialog = new CreateSessionDialog(ownerUri, templates); + dialog.showDialog(); + }); + } } diff --git a/extensions/profiler/client/src/typings/ref.d.ts b/extensions/profiler/client/src/typings/ref.d.ts index 642b4842ad..83bbdd3e21 100644 --- a/extensions/profiler/client/src/typings/ref.d.ts +++ b/extensions/profiler/client/src/typings/ref.d.ts @@ -5,4 +5,5 @@ /// /// +/// /// \ No newline at end of file diff --git a/extensions/profiler/package.json b/extensions/profiler/package.json index c579b2e421..99165e40f8 100644 --- a/extensions/profiler/package.json +++ b/extensions/profiler/package.json @@ -27,7 +27,7 @@ ], "contributes": { - "commands": [ + "commands": [ { "command": "profiler.newProfiler", "title": "New Profiler", @@ -42,12 +42,19 @@ "command": "profiler.stop", "title": "Stop", "category": "Profiler" + }, + { + "command": "profiler.openCreateSessionDialog", + "category": "Profiler" } ], "outputChannels": [ "sqlprofiler" ] }, + "dependencies": { + "vscode-nls": "^3.2.1" + }, "devDependencies": { "vscode": "1.0.1" } diff --git a/src/sql/base/browser/ui/taskbar/media/add.svg b/src/sql/base/browser/ui/taskbar/media/add.svg new file mode 100644 index 0000000000..2b679663e8 --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/media/add.svg @@ -0,0 +1 @@ +add \ No newline at end of file diff --git a/src/sql/base/browser/ui/taskbar/media/icons.css b/src/sql/base/browser/ui/taskbar/media/icons.css index 8c902a30df..9f030eeb93 100644 --- a/src/sql/base/browser/ui/taskbar/media/icons.css +++ b/src/sql/base/browser/ui/taskbar/media/icons.css @@ -18,6 +18,12 @@ background-image: url('start.svg'); } +.vs .icon.add, +.vs-dark .icon.add, +.hc-black .icon.add { + background-image: url('add.svg'); +} + .vs .icon.stop, .vs-dark .icon.stop, .hc-black .icon.stop { diff --git a/src/sql/parts/profiler/contrib/profiler.contribution.ts b/src/sql/parts/profiler/contrib/profiler.contribution.ts index 316585da86..bc4170de8e 100644 --- a/src/sql/parts/profiler/contrib/profiler.contribution.ts +++ b/src/sql/parts/profiler/contrib/profiler.contribution.ts @@ -13,7 +13,7 @@ import * as nls from 'vs/nls'; import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; import { ProfilerEditor } from 'sql/parts/profiler/editor/profilerEditor'; -import { PROFILER_VIEW_TEMPLATE_SETTINGS, IProfilerViewTemplate } from 'sql/parts/profiler/service/interfaces'; +import { PROFILER_VIEW_TEMPLATE_SETTINGS, PROFILER_SESSION_TEMPLATE_SETTINGS, IProfilerViewTemplate, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces'; const profilerDescriptor = new EditorDescriptor( ProfilerEditor, @@ -25,212 +25,300 @@ Registry.as(EditorExtensions.Editors) .registerEditor(profilerDescriptor, [new SyncDescriptor(ProfilerInput)]); const profilerViewTemplateSchema: IJSONSchema = { - description: nls.localize('profiler.settings.viewTemplates', "Specifies view templates"), - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string' + description: nls.localize('profiler.settings.viewTemplates', "Specifies view templates"), + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string' + } + } + }, + default: >[ + { + name: 'Standard View', + columns: [ + { + name: 'EventClass', + eventsMapped: ['name'] + }, + { + name: 'TextData', + eventsMapped: ['options_text', 'batch_text'] + }, + { + name: 'ApplicationName', + width: '1', + eventsMapped: ['client_app_name'] + }, + { + name: 'NTUserName', + eventsMapped: ['nt_username'] + }, + { + name: 'LoginName', + eventsMapped: ['server_principal_name'] + }, + { + name: 'ClientProcessID', + eventsMapped: ['client_pid'] + }, + { + name: 'SPID', + eventsMapped: ['session_id'] + }, + { + name: 'StartTime', + eventsMapped: ['timestamp'] + }, + { + name: 'CPU', + eventsMapped: ['cpu_time'] + }, + { + name: 'Reads', + eventsMapped: ['logical_reads'] + }, + { + name: 'Writes', + eventsMapped: ['writes'] + }, + { + name: 'Duration', + eventsMapped: ['duration'] } - } + ] }, - default: >[ - { - name: 'Standard View', - columns: [ - { - name: 'EventClass', - eventsMapped: ['name'] - }, - { - name: 'TextData', - eventsMapped: ['options_text', 'batch_text'] - }, - { - name: 'ApplicationName', - width: '1', - eventsMapped: ['client_app_name'] - }, - { - name: 'NTUserName', - eventsMapped: ['nt_username'] - }, - { - name: 'LoginName', - eventsMapped: ['server_principal_name'] - }, - { - name: 'ClientProcessID', - eventsMapped: ['client_pid'] - }, - { - name: 'SPID', - eventsMapped: ['session_id'] - }, - { - name: 'StartTime', - eventsMapped: ['timestamp'] - }, - { - name: 'CPU', - eventsMapped: ['cpu_time'] - }, - { - name: 'Reads', - eventsMapped: ['logical_reads'] - }, - { - name: 'Writes', - eventsMapped: ['writes'] - }, - { - name: 'Duration', - eventsMapped: ['duration'] - } - ] - }, - { - name: 'TSQL View', - columns: [ - { - name: 'EventClass', - eventsMapped: ['name'] - }, - { - name: 'TextData', - eventsMapped: ['options_text', 'batch_text'] - }, - { - name: 'SPID', - eventsMapped: ['session_id'] - }, - { - name: 'StartTime', - eventsMapped: ['timestamp'] - } - ] - }, - { - name: 'Tuning View', - columns: [ - { - name: 'EventClass', - eventsMapped: ['name'] - }, - { - name: 'TextData', - eventsMapped: ['options_text', 'batch_text'] - }, - { - name: 'Duration', - eventsMapped: ['duration'] - }, - { - name: 'SPID', - eventsMapped: ['session_id'] - }, - { - name: 'DatabaseID', - eventsMapped: ['database_id'] - }, - { - name: 'DatabaseName', - eventsMapped: ['database_name'] - }, - { - name: 'ObjectType', - eventsMapped: ['object_type'] - }, - { - name: 'LoginName', - eventsMapped: ['server_principal_name'] - } - ] - }, - { - name: 'TSQL_Locks View', - columns: [ - { - name: 'EventClass', - eventsMapped: ['name'] - }, - { - name: 'TextData', - eventsMapped: ['options_text', 'batch_text'] - }, - { - name: 'ApplicationName', - eventsMapped: ['client_app_name'] - }, - { - name: 'NTUserName', - eventsMapped: ['nt_username'] - }, - { - name: 'LoginName', - eventsMapped: ['server_principal_name'] - }, - { - name: 'ClientProcessID', - eventsMapped: ['client_pid'] - }, - { - name: 'SPID', - eventsMapped: ['session_id'] - }, - { - name: 'StartTime', - eventsMapped: ['timestamp'] - }, - { - name: 'CPU', - eventsMapped: ['cpu_time'] - }, - { - name: 'Reads', - eventsMapped: ['logical_reads'] - }, - { - name: 'Writes', - eventsMapped: ['writes'] - }, - { - name: 'Duration', - eventsMapped: ['duration'] - } - ] - }, - { - name: 'TSQL_Duration View', - columns: [ - { - name: 'EventClass', - eventsMapped: ['name'] - }, - { - name: 'Duration', - eventsMapped: ['duration'] - }, - { - name: 'TextData', - eventsMapped: ['options_text', 'batch_text'] - }, - { - name: 'SPID', - eventsMapped: ['session_id'] - } - ] + { + name: 'TSQL View', + columns: [ + { + name: 'EventClass', + eventsMapped: ['name'] + }, + { + name: 'TextData', + eventsMapped: ['options_text', 'batch_text'] + }, + { + name: 'SPID', + eventsMapped: ['session_id'] + }, + { + name: 'StartTime', + eventsMapped: ['timestamp'] + } + ] + }, + { + name: 'Tuning View', + columns: [ + { + name: 'EventClass', + eventsMapped: ['name'] + }, + { + name: 'TextData', + eventsMapped: ['options_text', 'batch_text'] + }, + { + name: 'Duration', + eventsMapped: ['duration'] + }, + { + name: 'SPID', + eventsMapped: ['session_id'] + }, + { + name: 'DatabaseID', + eventsMapped: ['database_id'] + }, + { + name: 'DatabaseName', + eventsMapped: ['database_name'] + }, + { + name: 'ObjectType', + eventsMapped: ['object_type'] + }, + { + name: 'LoginName', + eventsMapped: ['server_principal_name'] + } + ] + }, + { + name: 'TSQL_Locks View', + columns: [ + { + name: 'EventClass', + eventsMapped: ['name'] + }, + { + name: 'TextData', + eventsMapped: ['options_text', 'batch_text'] + }, + { + name: 'ApplicationName', + eventsMapped: ['client_app_name'] + }, + { + name: 'NTUserName', + eventsMapped: ['nt_username'] + }, + { + name: 'LoginName', + eventsMapped: ['server_principal_name'] + }, + { + name: 'ClientProcessID', + eventsMapped: ['client_pid'] + }, + { + name: 'SPID', + eventsMapped: ['session_id'] + }, + { + name: 'StartTime', + eventsMapped: ['timestamp'] + }, + { + name: 'CPU', + eventsMapped: ['cpu_time'] + }, + { + name: 'Reads', + eventsMapped: ['logical_reads'] + }, + { + name: 'Writes', + eventsMapped: ['writes'] + }, + { + name: 'Duration', + eventsMapped: ['duration'] + } + ] + }, + { + name: 'TSQL_Duration View', + columns: [ + { + name: 'EventClass', + eventsMapped: ['name'] + }, + { + name: 'Duration', + eventsMapped: ['duration'] + }, + { + name: 'TextData', + eventsMapped: ['options_text', 'batch_text'] + }, + { + name: 'SPID', + eventsMapped: ['session_id'] + } + ] + } + ] +}; + +const profilerSessionTemplateSchema: IJSONSchema = { + description: nls.localize('profiler.settings.sessionTemplates', "Specifies session templates"), + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string' } - ] - }; + } + }, + default: >[ + { + name: 'Standard_OnPrem', + defaultView: 'Standard View', + createStatement: + `CREATE EVENT SESSION [{sessionName}] ON SERVER + ADD EVENT sqlserver.attention( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))), + ADD EVENT sqlserver.existing_connection(SET collect_options_text=(1) + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.nt_username,sqlserver.server_principal_name,sqlserver.session_id)), + ADD EVENT sqlserver.login(SET collect_options_text=(1) + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.nt_username,sqlserver.server_principal_name,sqlserver.session_id)), + ADD EVENT sqlserver.logout( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.nt_username,sqlserver.server_principal_name,sqlserver.session_id)), + ADD EVENT sqlserver.rpc_completed( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))), + ADD EVENT sqlserver.sql_batch_completed( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))), + ADD EVENT sqlserver.sql_batch_starting( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))) + ADD TARGET package0.ring_buffer(SET max_events_limit=(1000),max_memory=(51200)) + WITH (MAX_MEMORY=8192 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_CPU,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)` + }, + { + name: 'Standard_Azure', + defaultView: 'Standard View', + createStatement: + `CREATE EVENT SESSION [{sessionName}] ON DATABASE + ADD EVENT sqlserver.attention( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))), + ADD EVENT sqlserver.existing_connection(SET collect_options_text=(1) + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.username,sqlserver.session_id)), + ADD EVENT sqlserver.login(SET collect_options_text=(1) + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.username,sqlserver.session_id)), + ADD EVENT sqlserver.logout( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.username,sqlserver.session_id)), + ADD EVENT sqlserver.rpc_completed( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))), + ADD EVENT sqlserver.sql_batch_completed( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))), + ADD EVENT sqlserver.sql_batch_starting( + ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))) + ADD TARGET package0.ring_buffer(SET max_events_limit=(1000),max_memory=(51200)) + WITH (MAX_MEMORY=8192 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_CPU,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)` + }, + { + name: 'TSQL_OnPrem', + defaultView: 'TSQL View', + createStatement: + `CREATE EVENT SESSION [{sessionName}] ON SERVER + ADD EVENT sqlserver.existing_connection( + ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.client_hostname)), + ADD EVENT sqlserver.login(SET collect_options_text=(1) + ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.client_hostname)), + ADD EVENT sqlserver.logout( + ACTION(package0.event_sequence,sqlserver.session_id)), + ADD EVENT sqlserver.rpc_starting( + ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.database_name) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))), + ADD EVENT sqlserver.sql_batch_starting( + ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.database_name) + WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))) + ADD TARGET package0.ring_buffer(SET max_events_limit=(1000),max_memory=(51200)) + WITH (MAX_MEMORY=8192 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_CPU,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)` + } + ] +}; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const dashboardConfig: IConfigurationNode = { id: 'Profiler', type: 'object', properties: { - [PROFILER_VIEW_TEMPLATE_SETTINGS]: profilerViewTemplateSchema + [PROFILER_VIEW_TEMPLATE_SETTINGS]: profilerViewTemplateSchema, + [PROFILER_SESSION_TEMPLATE_SETTINGS]: profilerSessionTemplateSchema } }; diff --git a/src/sql/parts/profiler/contrib/profilerActions.contribution.ts b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts index 6c7070f0d3..94e80cf6aa 100644 --- a/src/sql/parts/profiler/contrib/profilerActions.contribution.ts +++ b/src/sql/parts/profiler/contrib/profilerActions.contribution.ts @@ -23,7 +23,7 @@ import { IObjectExplorerService } from '../../objectExplorer/common/objectExplor import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput'; import { TPromise } from 'vs/base/common/winjs.base'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; -import { IProfilerService} from '../service/interfaces'; +import { IProfilerService } from '../service/interfaces'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/editor/editor.api'; import { ProfilerEditor } from '../editor/profilerEditor'; @@ -73,12 +73,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ let activeEditor = editorService.getActiveEditor(); if (activeEditor instanceof ProfilerEditor) { let profilerInput = activeEditor.input; - if (profilerInput.state.isRunning){ + if (profilerInput.state.isRunning) { return profilerService.stopSession(profilerInput.id); } else { // clear data when profiler is started profilerInput.data.clear(); - return profilerService.startSession(profilerInput.id); + return profilerService.startSession(profilerInput.id, profilerInput.sessionName); } } return TPromise.as(false); diff --git a/src/sql/parts/profiler/contrib/profilerActions.ts b/src/sql/parts/profiler/contrib/profilerActions.ts index 342e67d658..c18e0a8d12 100644 --- a/src/sql/parts/profiler/contrib/profilerActions.ts +++ b/src/sql/parts/profiler/contrib/profilerActions.ts @@ -20,7 +20,9 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IEditorAction } from 'vs/editor/common/editorCommon'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ICommandService } from 'vs/platform/commands/common/commands' import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export class ProfilerConnect extends Action { public static ID = 'profiler.connect'; @@ -78,7 +80,27 @@ export class ProfilerStart extends Action { public run(input: ProfilerInput): TPromise { input.data.clear(); - return TPromise.wrap(this._profilerService.startSession(input.id)); + return TPromise.wrap(this._profilerService.startSession(input.id, input.sessionName)); + } +} + +export class ProfilerCreate extends Action { + public static ID = 'profiler.create'; + public static LABEL = nls.localize('create', "Create"); + + constructor( + id: string, label: string, + @ICommandService private _commandService: ICommandService, + @IProfilerService private _profilerService: IProfilerService, + @INotificationService private _notificationService: INotificationService + ) { + super(id, label, 'add'); + } + + public run(input: ProfilerInput): TPromise { + return TPromise.wrap(this._profilerService.launchCreateSessionDialog(input).then(() => { + return true; + })); } } diff --git a/src/sql/parts/profiler/editor/profilerEditor.ts b/src/sql/parts/profiler/editor/profilerEditor.ts index 43fd5d6f18..8238f2c5eb 100644 --- a/src/sql/parts/profiler/editor/profilerEditor.ts +++ b/src/sql/parts/profiler/editor/profilerEditor.ts @@ -118,6 +118,8 @@ export class ProfilerEditor extends BaseEditor { private _viewTemplateSelector: SelectBox; private _viewTemplates: Array; + private _sessionSelector: SelectBox; + private _sessionsList: Array; private _connectionInfoText: HTMLElement; // Actions @@ -126,6 +128,7 @@ export class ProfilerEditor extends BaseEditor { private _pauseAction: Actions.ProfilerPause; private _stopAction: Actions.ProfilerStop; private _autoscrollAction: Actions.ProfilerAutoScroll; + private _createAction: Actions.ProfilerCreate; private _collapsedPanelAction: Actions.ProfilerCollapsablePanelAction; @@ -186,6 +189,8 @@ export class ProfilerEditor extends BaseEditor { this._actionBar = new Taskbar(this._header, this._contextMenuService); this._startAction = this._instantiationService.createInstance(Actions.ProfilerStart, Actions.ProfilerStart.ID, Actions.ProfilerStart.LABEL); this._startAction.enabled = false; + this._createAction = this._instantiationService.createInstance(Actions.ProfilerCreate, Actions.ProfilerCreate.ID, Actions.ProfilerCreate.LABEL); + this._createAction.enabled = true; this._stopAction = this._instantiationService.createInstance(Actions.ProfilerStop, Actions.ProfilerStop.ID, Actions.ProfilerStop.LABEL); this._stopAction.enabled = false; this._pauseAction = this._instantiationService.createInstance(Actions.ProfilerPause, Actions.ProfilerPause.ID, Actions.ProfilerPause.LABEL); @@ -200,10 +205,22 @@ export class ProfilerEditor extends BaseEditor { this.input.viewTemplate = this._viewTemplates.find(i => i.name === e.selected); } })); - let dropdownContainer = document.createElement('div'); - dropdownContainer.style.width = '150px'; - dropdownContainer.style.paddingRight = '5px'; - this._viewTemplateSelector.render(dropdownContainer); + let viewTemplateContainer = document.createElement('div'); + viewTemplateContainer.style.width = '150px'; + viewTemplateContainer.style.paddingRight = '5px'; + this._viewTemplateSelector.render(viewTemplateContainer); + + this._sessionsList = ['']; + this._sessionSelector = new SelectBox(this._sessionsList, '', this._contextViewService); + this._register(this._sessionSelector.onDidSelect(e => { + if (this.input) { + this.input.sessionName = e.selected; + } + })); + let sessionsContainer = document.createElement('div'); + sessionsContainer.style.width = '150px'; + sessionsContainer.style.paddingRight = '5px'; + this._sessionSelector.render(sessionsContainer); this._connectionInfoText = document.createElement('div'); this._connectionInfoText.style.paddingRight = '5px'; @@ -213,15 +230,18 @@ export class ProfilerEditor extends BaseEditor { this._connectionInfoText.style.alignItems = 'center'; this._register(attachSelectBoxStyler(this._viewTemplateSelector, this.themeService)); + this._register(attachSelectBoxStyler(this._sessionSelector, this.themeService)); this._actionBar.setContent([ { action: this._startAction }, { action: this._stopAction }, + { element: sessionsContainer }, + { action: this._createAction }, { element: Taskbar.createTaskbarSeparator() }, { action: this._pauseAction }, { action: this._autoscrollAction }, { action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) }, - { element: dropdownContainer }, + { element: viewTemplateContainer }, { element: Taskbar.createTaskbarSeparator() }, { element: this._connectionInfoText } ]); @@ -416,26 +436,61 @@ export class ProfilerEditor extends BaseEditor { if (e.isConnected) { this._connectAction.connected = this.input.state.isConnected; - if (!this.input.state.isConnected) { - this._startAction.enabled = this.input.state.isConnected; + + if (this.input.state.isConnected) { + this._updateToolbar(); + this._sessionSelector.enable(); + this._profilerService.getXEventSessions(this.input.id).then((r) => { + this._sessionSelector.setOptions(r); + this._sessionsList = r; + if (this.input.sessionName === undefined || this.input.sessionName === '') { + this.input.sessionName = this._sessionsList[0]; + } + }); + } else { + this._startAction.enabled = false; this._stopAction.enabled = false; this._pauseAction.enabled = false; + this._sessionSelector.setOptions([]); + this._sessionSelector.disable(); return; } } - if (e.isPaused){ + if (e.isPaused) { this._pauseAction.paused = this.input.state.isPaused; - this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused); + this._updateToolbar(); } if (e.isStopped || e.isRunning) { - this._startAction.enabled = !this.input.state.isRunning && !this.input.state.isPaused; - this._stopAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused); - this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused); + if (this.input.state.isRunning) { + this._updateToolbar(); + this._sessionSelector.setOptions([this.input.sessionName]); + this._sessionSelector.selectWithOptionName(this.input.sessionName); + this._sessionSelector.disable(); + this._viewTemplateSelector.selectWithOptionName(this.input.viewTemplate.name); + } + if (this.input.state.isStopped) { + this._updateToolbar(); + this._sessionSelector.enable(); + this._profilerService.getXEventSessions(this.input.id).then((r) => { + this._sessionsList = r; + this._sessionSelector.setOptions(r); + if (this.input.sessionName === undefined || this.input.sessionName === '') { + this.input.sessionName = this._sessionsList[0]; + } + }); + } } } + private _updateToolbar(): void { + this._startAction.enabled = !this.input.state.isRunning && !this.input.state.isPaused && this.input.state.isConnected; + this._createAction.enabled = !this.input.state.isRunning && !this.input.state.isPaused && this.input.state.isConnected; + this._stopAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused) && this.input.state.isConnected; + this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused && this.input.state.isConnected); + } + public layout(dimension: DOM.Dimension): void { this._container.style.width = dimension.width + 'px'; this._container.style.height = dimension.height + 'px'; @@ -453,7 +508,6 @@ abstract class SettingsCommand extends Command { return activeEditor; } return null; - } } diff --git a/src/sql/parts/profiler/editor/profilerInput.ts b/src/sql/parts/profiler/editor/profilerInput.ts index ad08ad98ab..08f8002971 100644 --- a/src/sql/parts/profiler/editor/profilerInput.ts +++ b/src/sql/parts/profiler/editor/profilerInput.ts @@ -20,6 +20,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import { escape } from 'sql/base/common/strings'; +import * as types from 'vs/base/common/types'; import URI from 'vs/base/common/uri'; export class ProfilerInput extends EditorInput implements IProfilerSession { @@ -30,6 +31,7 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { private _id: ProfilerSessionID; private _state: ProfilerState; private _columns: string[] = []; + private _sessionName: string; private _viewTemplate: IProfilerViewTemplate; // mapping of event categories to what column they display under // used for coallescing multiple events with different names to the same column @@ -49,14 +51,17 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { this._state = new ProfilerState(); // set inital state this.state.change({ - isConnected: true, + isConnected: false, isStopped: true, isPaused: false, isRunning: false, autoscroll: true }); - this._id = this._profilerService.registerSession(generateUuid(), _connection, this); + this._profilerService.registerSession(generateUuid(), _connection, this).then((id) => { + this._id = id; + this.state.change({ isConnected: true }); + }); let searchFn = (val: { [x: string]: string }, exp: string): Array => { let ret = new Array(); for (let i = 0; i < this._columns.length; i++) { @@ -108,6 +113,16 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { return this._viewTemplate; } + public set sessionName(name: string) { + if (!this._state.isRunning || !this.state.isPaused) { + this._sessionName = name; + } + } + + public get sessionName(): string { + return this._sessionName; + } + public getTypeId(): string { return ProfilerInput.ID; } @@ -163,11 +178,11 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { } public get connectionName(): string { - if (this._connection !== null) { + if (!types.isUndefinedOrNull(this._connection)) { if (this._connection.databaseName) { - return `${ this._connection.serverName } ${ this._connection.databaseName }`; + return `${this._connection.serverName} ${this._connection.databaseName}`; } else { - return `${ this._connection.serverName }`; + return `${this._connection.serverName}`; } } else { @@ -193,6 +208,32 @@ export class ProfilerInput extends EditorInput implements IProfilerSession { }); } + public onProfilerSessionCreated(params: sqlops.ProfilerSessionCreatedParams) { + if (types.isUndefinedOrNull(params.sessionName) || types.isUndefinedOrNull(params.templateName)) { + this._notificationService.error(nls.localize("profiler.sessionCreationError", "Error while starting new session")); + } else { + this._sessionName = params.sessionName; + let sessionTemplate = this._profilerService.getSessionTemplates().find((template) => { + return template.name === params.templateName; + }); + if (!types.isUndefinedOrNull(sessionTemplate)) { + let newView = this._profilerService.getViewTemplates().find((view) => { + return view.name === sessionTemplate.defaultView; + }); + if (!types.isUndefinedOrNull(newView)) { + this.viewTemplate = newView; + } + } + + this.data.clear(); + this.state.change({ + isStopped: false, + isPaused: false, + isRunning: true + }); + } + } + public onSessionStateChanged(state: ProfilerState) { this.state.change(state); } diff --git a/src/sql/parts/profiler/service/interfaces.ts b/src/sql/parts/profiler/service/interfaces.ts index f12b76d581..bdcf8c636b 100644 --- a/src/sql/parts/profiler/service/interfaces.ts +++ b/src/sql/parts/profiler/service/interfaces.ts @@ -16,6 +16,7 @@ export const IProfilerService = createDecorator(PROFILER_SERVI export type ProfilerSessionID = string; export const PROFILER_VIEW_TEMPLATE_SETTINGS = 'profiler.viewTemplates'; +export const PROFILER_SESSION_TEMPLATE_SETTINGS = 'profiler.sessionTemplates'; export const PROFILER_SETTINGS = 'profiler'; /** @@ -30,6 +31,10 @@ export interface IProfilerSession { * Called by the service when the session is closed unexpectedly */ onSessionStopped(events: sqlops.ProfilerSessionStoppedParams); + /** + * Called by the service when a new profiler session is created by the dialog + */ + onProfilerSessionCreated(events: sqlops.ProfilerSessionCreatedParams); /** * Called by the service when the session state is changed */ @@ -49,7 +54,7 @@ export interface IProfilerService { * Registers a session with the service that acts as the UI for a profiler session * @returns An unique id that should be used to make subsequent calls to this service */ - registerSession(uri: string, connectionProfile: IConnectionProfile, session: IProfilerSession): ProfilerSessionID; + registerSession(uri: string, connectionProfile: IConnectionProfile, session: IProfilerSession): Promise; /** * Connects the session specified by the id */ @@ -58,10 +63,14 @@ export interface IProfilerService { * Disconnected the session specified by the id */ disconnectSession(sessionId: ProfilerSessionID): Thenable; + /** + * Creates a new session using the given create statement and session name + */ + createSession(id: string, createStatement: string, template: sqlops.ProfilerSessionTemplate): Thenable; /** * Starts the session specified by the id */ - startSession(sessionId: ProfilerSessionID): Thenable; + startSession(sessionId: ProfilerSessionID, sessionName: string): Thenable; /** * Pauses the session specified by the id */ @@ -70,6 +79,10 @@ export interface IProfilerService { * Stops the session specified by the id */ stopSession(sessionId: ProfilerSessionID): Thenable; + /** + * Gets a list of running XEvent sessions on the Profiler Session's target + */ + getXEventSessions(sessionId: ProfilerSessionID): Thenable; /** * The method called by the service provider for when more rows are available to render */ @@ -78,22 +91,39 @@ export interface IProfilerService { * The method called by the service provider for when more rows are available to render */ onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void; + /** + * Called by the service when a new profiler session is created by the dialog + */ + onProfilerSessionCreated(events: sqlops.ProfilerSessionCreatedParams); + /** + * Gets a list of the view templates that are specified in the settings + * @param provider An optional string to limit the view templates to a specific provider + * @returns An array of view templates that match the provider passed, if passed, and generic ones (no provider specified), + * otherwise returns all view templates + */ + getViewTemplates(providerId?: string): Array; /** * Gets a list of the session templates that are specified in the settings * @param provider An optional string to limit the session template to a specific * @returns An array of session templates that match the provider passed, if passed, and generic ones (no provider specified), * otherwise returns all session templates */ - getViewTemplates(providerId?: string): Array; + getSessionTemplates(providerId?: string): Array; /** * Launches the dialog for editing the view columns of a profiler session template for the given input * @param input input object that contains the necessary information which will be modified based on used input */ launchColumnEditor(input: ProfilerInput): Thenable; + /** + * Launches the dialog for creating a new XEvent session from a template + * @param input input object that contains the necessary information which will be modified based on used input + */ + launchCreateSessionDialog(input: ProfilerInput): Thenable; } export interface IProfilerSettings { viewTemplates: Array; + sessionTemplates: Array; } export interface IColumnViewTemplate { @@ -105,3 +135,9 @@ export interface IProfilerViewTemplate { name: string; columns: Array; } + +export interface IProfilerSessionTemplate { + name: string; + defaultView: string; + createStatement: string; +} \ No newline at end of file diff --git a/src/sql/parts/profiler/service/profilerService.ts b/src/sql/parts/profiler/service/profilerService.ts index 873d5d5e0e..98899ee5a8 100644 --- a/src/sql/parts/profiler/service/profilerService.ts +++ b/src/sql/parts/profiler/service/profilerService.ts @@ -5,7 +5,7 @@ import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement'; import { - ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, + ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate, PROFILER_SETTINGS, IProfilerSettings } from './interfaces'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; @@ -18,6 +18,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ICommandService } from 'vs/platform/commands/common/commands'; class TwoWayMap { private forwardMap: Map; @@ -48,20 +49,21 @@ export class ProfilerService implements IProfilerService { private _providers = new Map(); private _idMap = new TwoWayMap(); private _sessionMap = new Map(); - private _dialog: ProfilerColumnEditorDialog; + private _editColumnDialog: ProfilerColumnEditorDialog; constructor( @IConnectionManagementService private _connectionService: IConnectionManagementService, @IConfigurationService public _configurationService: IConfigurationService, @IInstantiationService private _instantiationService: IInstantiationService, - @INotificationService private _notificationService: INotificationService + @INotificationService private _notificationService: INotificationService, + @ICommandService private _commandService: ICommandService ) { } public registerProvider(providerId: string, provider: sqlops.ProfilerProvider): void { this._providers.set(providerId, provider); } - public registerSession(uri: string, connectionProfile: IConnectionProfile, session: IProfilerSession): ProfilerSessionID { + public async registerSession(uri: string, connectionProfile: IConnectionProfile, session: IProfilerSession): Promise { let options: IConnectionCompletionOptions = { params: { connectionType: ConnectionType.default, runQueryOnCompletion: RunQueryOnConnectionMode.none, input: undefined }, saveTheConnection: false, @@ -69,14 +71,14 @@ export class ProfilerService implements IProfilerService { showConnectionDialogOnError: false, showFirewallRuleOnError: true }; - this._connectionService.connect(connectionProfile, uri, options).then(() => { + try { + await this._connectionService.connect(connectionProfile, uri, options); + } catch (connectionError) { - }).catch(connectionError => { - - }); + } this._sessionMap.set(uri, session); this._idMap.set(uri, uri); - return uri; + return TPromise.wrap(uri); } public onMoreRows(params: sqlops.ProfilerSessionEvents): void { @@ -89,6 +91,11 @@ export class ProfilerService implements IProfilerService { this._sessionMap.get(this._idMap.reverseGet(params.ownerUri)).onSessionStopped(params); } + public onProfilerSessionCreated(params: sqlops.ProfilerSessionCreatedParams): void { + + this._sessionMap.get(this._idMap.reverseGet(params.ownerUri)).onProfilerSessionCreated(params); + } + public connectSession(id: ProfilerSessionID): Thenable { return this._runAction(id, provider => provider.connectSession(this._idMap.get(id))); } @@ -97,8 +104,17 @@ export class ProfilerService implements IProfilerService { return this._runAction(id, provider => provider.disconnectSession(this._idMap.get(id))); } - public startSession(id: ProfilerSessionID): Thenable { - return this._runAction(id, provider => provider.startSession(this._idMap.get(id))).then(() => { + public createSession(id: string, createStatement: string, template: sqlops.ProfilerSessionTemplate): Thenable { + return this._runAction(id, provider => provider.createSession(this._idMap.get(id), createStatement, template)).then(() => { + this._sessionMap.get(this._idMap.reverseGet(id)).onSessionStateChanged({ isRunning: true, isStopped: false, isPaused: false }); + return true; + }, (reason) => { + this._notificationService.error(reason.message); + }); + } + + public startSession(id: ProfilerSessionID, sessionName: string): Thenable { + return this._runAction(id, provider => provider.startSession(this._idMap.get(id), sessionName)).then(() => { this._sessionMap.get(this._idMap.reverseGet(id)).onSessionStateChanged({ isRunning: true, isStopped: false, isPaused: false }); return true; }, (reason) => { @@ -119,6 +135,14 @@ export class ProfilerService implements IProfilerService { }); } + public getXEventSessions(id: ProfilerSessionID): Thenable { + return this._runAction(id, provider => provider.getXEventSessions(this._idMap.get(id))).then((r) => { + return r; + }, (reason) => { + this._notificationService.error(reason.message); + }); + } + private _runAction(id: ProfilerSessionID, action: (handler: sqlops.ProfilerProvider) => Thenable): Thenable { // let providerId = this._connectionService.getProviderIdFromUri(this._idMap.get(id)); let providerId = 'MSSQL'; @@ -144,13 +168,27 @@ export class ProfilerService implements IProfilerService { } } + public getSessionTemplates(provider?: string): Array { + let config = this._configurationService.getValue(PROFILER_SETTINGS); + + if (provider) { + return config.sessionTemplates; + } else { + return config.sessionTemplates; + } + } + public launchColumnEditor(input?: ProfilerInput): Thenable { - if (!this._dialog) { - this._dialog = this._instantiationService.createInstance(ProfilerColumnEditorDialog); - this._dialog.render(); + if (!this._editColumnDialog) { + this._editColumnDialog = this._instantiationService.createInstance(ProfilerColumnEditorDialog); + this._editColumnDialog.render(); } - this._dialog.open(input); + this._editColumnDialog.open(input); return TPromise.as(null); } + + public launchCreateSessionDialog(input?: ProfilerInput): Thenable { + return this._commandService.executeCommand('profiler.openCreateSessionDialog', input.id, this.getSessionTemplates()); + } } diff --git a/src/sql/parts/profiler/service/profilerTestBackend.ts b/src/sql/parts/profiler/service/profilerTestBackend.ts index 6a28c9ea8c..51836e8e45 100644 --- a/src/sql/parts/profiler/service/profilerTestBackend.ts +++ b/src/sql/parts/profiler/service/profilerTestBackend.ts @@ -39,7 +39,11 @@ export class ProfilerTestBackend implements sqlops.ProfilerProvider { constructor( @IProfilerService private _profilerService: IProfilerService) { } - startSession(guid: string): Thenable { + createSession(guid: string, createStatement: string, template: sqlops.ProfilerSessionTemplate): Thenable { + this.timeOutMap.set(guid, this.intervalFn(guid)); + return TPromise.as(true); + } + startSession(guid: string, sessionName: string): Thenable { this.timeOutMap.set(guid, this.intervalFn(guid)); return TPromise.as(true); } @@ -52,6 +56,10 @@ export class ProfilerTestBackend implements sqlops.ProfilerProvider { return; } + registerOnProfilerSessionCreated(handler: (response: sqlops.ProfilerSessionCreatedParams) => any) { + return; + } + private intervalFn(guid: string): number { return setTimeout(() => { let data = this.testData[this.index++]; @@ -84,6 +92,11 @@ export class ProfilerTestBackend implements sqlops.ProfilerProvider { return TPromise.as(true); } + getXEventSessions(guid: string): Thenable { + let retVal = ['']; + return TPromise.as(retVal); + } + connectSession(): Thenable { if (this.testData.length === 0) { return new TPromise((resolve, reject) => { diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index 8d36b68f9c..ce9298dbde 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -1680,14 +1680,17 @@ declare module 'sqlops' { } export interface ProfilerProvider extends DataProvider { - startSession(sessionId: string): Thenable; + createSession(sessionId: string, sessionName: string, template: ProfilerSessionTemplate): Thenable; + startSession(sessionId: string, sessionName: string): Thenable; stopSession(sessionId: string): Thenable; pauseSession(sessionId: string): Thenable; + getXEventSessions(sessionId: string): Thenable; connectSession(sessionId: string): Thenable; disconnectSession(sessionId: string): Thenable; registerOnSessionEventsAvailable(handler: (response: ProfilerSessionEvents) => any): void; registerOnSessionStopped(handler: (response: ProfilerSessionStoppedParams) => any): void; + registerOnProfilerSessionCreated(handler: (response: ProfilerSessionCreatedParams) => any): void; } export interface IProfilerTableRow { @@ -1724,6 +1727,26 @@ declare module 'sqlops' { values: {}; } + /** + * Profiler Session Template + */ + export interface ProfilerSessionTemplate { + /** + * Template name + */ + name: string; + + /** + * Default view for template + */ + defaultView: string; + + /** + * TSQL for creating a session + */ + createStatement: string; + } + export interface ProfilerSessionEvents { sessionId: string; @@ -1739,6 +1762,12 @@ declare module 'sqlops' { sessionId: number; } + export interface ProfilerSessionCreatedParams { + ownerUri: string; + sessionName: string; + templateName: string; + } + // File browser interfaces ----------------------------------------------------------------------- export interface FileBrowserProvider extends DataProvider { diff --git a/src/sql/workbench/api/node/extHostDataProtocol.ts b/src/sql/workbench/api/node/extHostDataProtocol.ts index d98d143a47..f915d6e7bc 100644 --- a/src/sql/workbench/api/node/extHostDataProtocol.ts +++ b/src/sql/workbench/api/node/extHostDataProtocol.ts @@ -497,11 +497,18 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { * Profiler Provider methods */ + /** + * Create a new profiler session + */ + public $createSession(handle: number, sessionId: string, createStatement: string, template: sqlops.ProfilerSessionTemplate): Thenable { + return this._resolveProvider(handle).createSession(sessionId, createStatement, template); + } + /** * Start a profiler session */ - public $startSession(handle: number, sessionId: string): Thenable { - return this._resolveProvider(handle).startSession(sessionId); + public $startSession(handle: number, sessionId: string, sessionName: string): Thenable { + return this._resolveProvider(handle).startSession(sessionId, sessionName); } /** @@ -518,6 +525,12 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { return this._resolveProvider(handle).pauseSession(sessionId); } + /** + * Get list of running XEvent sessions on the session's target server + */ + public $getXEventSessions(handle: number, sessionId: string): Thenable { + return this._resolveProvider(handle).getXEventSessions(sessionId); + } /** * Profiler session events available notification @@ -533,6 +546,13 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { this._proxy.$onSessionStopped(handle, response); } + /** + * Profiler session created notification + */ + public $onProfilerSessionCreated(handle: number, response: sqlops.ProfilerSessionCreatedParams): void { + this._proxy.$onProfilerSessionCreated(handle, response); + } + /** * Agent Job Provider methods diff --git a/src/sql/workbench/api/node/mainThreadDataProtocol.ts b/src/sql/workbench/api/node/mainThreadDataProtocol.ts index f1e741b657..485cf62be6 100644 --- a/src/sql/workbench/api/node/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/node/mainThreadDataProtocol.ts @@ -295,8 +295,11 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { public $registerProfilerProvider(providerId: string, handle: number): TPromise { const self = this; this._profilerService.registerProvider(providerId, { - startSession(sessionId: string): Thenable { - return self._proxy.$startSession(handle, sessionId); + createSession(sessionId: string, createStatement: string, template: sqlops.ProfilerSessionTemplate): Thenable { + return self._proxy.$createSession(handle, sessionId, createStatement, template); + }, + startSession(sessionId: string, sessionName: string): Thenable { + return self._proxy.$startSession(handle, sessionId, sessionName); }, stopSession(sessionId: string): Thenable { return self._proxy.$stopSession(handle, sessionId); @@ -304,6 +307,9 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { pauseSession(sessionId: string): Thenable { return self._proxy.$pauseSession(handle, sessionId); }, + getXEventSessions(sessionId: string): Thenable { + return self._proxy.$getXEventSessions(handle, sessionId); + }, connectSession(sessionId: string): Thenable { return TPromise.as(true); }, @@ -466,6 +472,10 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { this._profilerService.onSessionStopped(response); } + public $onProfilerSessionCreated(handle: number, response: sqlops.ProfilerSessionCreatedParams): void { + this._profilerService.onProfilerSessionCreated(response); + } + // SQL Server Agent handlers public $onJobDataUpdated(handle: Number): void { this._jobManagementService.fireOnDidChange(); diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 9fc12fd2a4..47813acac5 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -254,6 +254,10 @@ export function createApiFactory( extHostDataProvider.$onSessionStopped(provider.handle, response); }); + provider.registerOnProfilerSessionCreated((response: sqlops.ProfilerSessionCreatedParams) => { + extHostDataProvider.$onProfilerSessionCreated(provider.handle, response); + }); + return extHostDataProvider.$registerProfilerProvider(provider); }; diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 05aa78f264..09f9c8113e 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -315,10 +315,15 @@ export abstract class ExtHostDataProtocolShape { * Profiler Provider methods */ + /** + * Create a profiler session + */ + $createSession(handle: number, sessionId: string, createStatement: string, template: sqlops.ProfilerSessionTemplate): Thenable { throw ni(); } + /** * Start a profiler session */ - $startSession(handle: number, sessionId: string): Thenable { throw ni(); } + $startSession(handle: number, sessionId: string, sessionName: string): Thenable { throw ni(); } /** * Stop a profiler session @@ -330,6 +335,10 @@ export abstract class ExtHostDataProtocolShape { */ $pauseSession(handle: number, sessionId: string): Thenable { throw ni(); } + /** + * Get list of running XEvent sessions on the profiler session's target server + */ + $getXEventSessions(handle: number, sessionId: string): Thenable { throw ni(); } /** * Get Agent Job list @@ -469,6 +478,7 @@ export interface MainThreadDataProtocolShape extends IDisposable { $onScriptingComplete(handle: number, message: sqlops.ScriptingCompleteResult): void; $onSessionEventsAvailable(handle: number, response: sqlops.ProfilerSessionEvents): void; $onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void; + $onProfilerSessionCreated(handle: number, response: sqlops.ProfilerSessionCreatedParams): void; $onJobDataUpdated(handle: Number): void; /**