From cc2ca4cad3108c058fdb9a3ddf9a3d9426984629 Mon Sep 17 00:00:00 2001 From: Madeline MacDonald Date: Tue, 26 Jun 2018 10:55:30 -0700 Subject: [PATCH] Support profiling on Azure instances (#643) * Support profiling Azure instances * Spacing fixes * Localizing error message for azure DB profiling --- .../Localization/sr.cs | 11 ++++ .../Localization/sr.resx | 4 ++ .../Localization/sr.strings | 1 + .../Localization/sr.xlf | 5 ++ .../Profiler/ProfilerService.cs | 56 +++++++++++++++++-- .../Profiler/ProfilerSession.cs | 3 +- 6 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index 22562b48..165b8de3 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -3661,6 +3661,14 @@ namespace Microsoft.SqlTools.ServiceLayer } } + public static string AzureSystemDbProfilingError + { + get + { + return Keys.GetString(Keys.AzureSystemDbProfilingError); + } + } + public static string UserCancelledSelectStep { get @@ -6147,6 +6155,9 @@ namespace Microsoft.SqlTools.ServiceLayer public const string ProfilerConnectionNotFound = "ProfilerConnectionNotFound"; + public const string AzureSystemDbProfilingError = "AzureSystemDbProfilingError"; + + public const string EnableAlertsTitle = "EnableAlertsTitle"; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index 2e2fe638..903eb56f 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -2023,6 +2023,10 @@ Connection not found + + Cannot profile Azure system databases + + Enable Alerts - {0} . diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings index b446650c..248de2dc 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings @@ -887,6 +887,7 @@ InvalidPathError = Cannot access the specified path on the server: {0} ############################################################################ # Profiler ProfilerConnectionNotFound = Connection not found +AzureSystemDbProfilingError = Cannot profile Azure system databases ############################################################################# # SQL Agent diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf index a51ae56e..566ba978 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf @@ -2256,6 +2256,11 @@ Connection not found + + Cannot profile Azure system databases + Cannot profile Azure system databases + + Please provide a file path instead of directory path Please provide a file path instead of directory path diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs index cd0640c1..9f327f8b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs @@ -17,12 +17,14 @@ using System.Xml; using Microsoft.SqlServer.Management.Sdk.Sfc; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.XEvent; +using Microsoft.SqlServer.Management.XEventDbScoped; using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.Hosting.Protocol.Contracts; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.Hosting; using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.Profiler @@ -202,6 +204,24 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler return xeSession.Id; } + private static BaseXEStore CreateXEventStore(ConnectionInfo connInfo, SqlStoreConnection connection) + { + BaseXEStore store = null; + if (connInfo.IsCloud) + { + if (DatabaseUtils.IsSystemDatabaseConnection(connInfo.ConnectionDetails.DatabaseName)) + { + throw new NotSupportedException(SR.AzureSystemDbProfilingError); + } + store = new DatabaseXEStore(connection, connInfo.ConnectionDetails.DatabaseName); + } + else + { + store = new XEStore(connection); + } + return store; + } + /// /// Gets or creates an XEvent session with the given template per the IXEventSessionFactory contract /// Also starts the session if it isn't currently running @@ -212,13 +232,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler var sqlConnection = ConnectionService.OpenSqlConnection(connInfo); SqlStoreConnection connection = new SqlStoreConnection(sqlConnection); - XEStore store = new XEStore(connection); + BaseXEStore store = CreateXEventStore(connInfo, connection); Session session = store.Sessions[sessionName]; // start the session if it isn't already running if (session == null) { - session = CreateSession(connection, sessionName); + session = CreateSession(connInfo, connection, sessionName); } if (session != null && !session.IsRunning) @@ -233,8 +253,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler }; } - private static Session CreateSession(SqlStoreConnection connection, string sessionName) - { + private static Session CreateSession(ConnectionInfo connInfo, SqlStoreConnection connection, string sessionName) + { string createSessionSql = @" CREATE EVENT SESSION [Profiler] ON SERVER @@ -259,9 +279,33 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler 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)"; - connection.ServerConnection.ExecuteNonQuery(createSessionSql); + string createAzureSessionSql = + @" + CREATE EVENT SESSION [Profiler] 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)"; - XEStore store = new XEStore(connection); + string createStatement = connInfo.IsCloud ? createAzureSessionSql : createSessionSql; + connection.ServerConnection.ExecuteNonQuery(createStatement); + BaseXEStore store = CreateXEventStore(connInfo, connection); return store.Sessions[sessionName]; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs index 4d04fb38..5835cd3b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs @@ -115,7 +115,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler || currentEvent.Name.Equals("sql_batch_starting")) && currentEvent.Values.ContainsKey("batch_text")) { - return currentEvent.Values["batch_text"].Contains("SELECT target_data FROM sys.dm_xe_session_targets"); + return currentEvent.Values["batch_text"].Contains("SELECT target_data FROM sys.dm_xe_session_targets") + || currentEvent.Values["batch_text"].Contains("SELECT target_data FROM sys.dm_xe_database_session_targets"); } return false;