diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/Contracts/PauseProfilingRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/Contracts/PauseProfilingRequest.cs
new file mode 100644
index 00000000..c3f0d1f6
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/Contracts/PauseProfilingRequest.cs
@@ -0,0 +1,34 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Utility;
+using Microsoft.SqlTools.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.Profiler.Contracts
+{
+ ///
+ /// Pause Profiling request parameters
+ ///
+ public class PauseProfilingParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+ }
+
+ public class PauseProfilingResult{}
+
+ ///
+ /// Pause Profile request type
+ ///
+ public class PauseProfilingRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("profiler/pause");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/Contracts/StopProfilingRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/Contracts/StopProfilingRequest.cs
index 8510083b..da652675 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/Contracts/StopProfilingRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/Contracts/StopProfilingRequest.cs
@@ -16,12 +16,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler.Contracts
public string OwnerUri { get; set; }
}
- public class StopProfilingResult
- {
- public bool Succeeded { get; set; }
-
- public string ErrorMessage { get; set; }
- }
+ public class StopProfilingResult{}
///
/// Start Profile request type
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs
index 1579f839..b47a1a50 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs
@@ -22,11 +22,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
/// Starts monitoring a profiler session
///
- bool StartMonitoringSession(ProfilerSession session);
+ bool StartMonitoringSession(string viewerId, IXEventSession session);
///
/// Stops monitoring a profiler session
///
- bool StopMonitoringSession(string sessionId, out ProfilerSession session);
+ bool StopMonitoringSession(string viewerId, out ProfilerSession session);
+
+ ///
+ /// Pauses or Unpauses the stream of events to the viewer
+ ///
+ void PauseViewer(string viewerId);
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs
index 5d46dd3b..4e91be3a 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs
@@ -11,13 +11,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
public interface IXEventSession
{
///
- /// Reads XEvent XML from the default session target
+ /// Gets unique XEvent session Id
///
- string GetTargetXml();
+ int Id { get; }
+
+ ///
+ /// Starts XEvent session
+ ///
+ void Start();
///
/// Stops XEvent session
///
void Stop();
+
+ ///
+ /// Reads XEvent XML from the default session target
+ ///
+ string GetTargetXml();
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSessionFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSessionFactory.cs
index 24e46152..36bb7b82 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSessionFactory.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSessionFactory.cs
@@ -16,8 +16,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
public interface IXEventSessionFactory
{
///
- /// Create a new XEvent session
+ /// Gets or creates an XEvent session with the given template
///
- IXEventSession CreateXEventSession(ConnectionInfo connInfo);
+ IXEventSession GetOrCreateXEventSession(string template, ConnectionInfo connInfo);
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs
index c25629d2..169030ff 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs
@@ -110,6 +110,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
this.ServiceHost = serviceHost;
this.ServiceHost.SetRequestHandler(StartProfilingRequest.Type, HandleStartProfilingRequest);
this.ServiceHost.SetRequestHandler(StopProfilingRequest.Type, HandleStopProfilingRequest);
+ this.ServiceHost.SetRequestHandler(PauseProfilingRequest.Type, HandlePauseProfilingRequest);
this.SessionMonitor.AddSessionListener(this);
}
@@ -129,8 +130,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
if (connInfo != null)
{
- ProfilerSession session = StartSession(parameters.OwnerUri, connInfo);
- result.SessionId = session.SessionId;
+ int xEventSessionId = StartSession(parameters.OwnerUri, parameters.TemplateName, connInfo);
+ result.SessionId = xEventSessionId.ToString();
result.Succeeded = true;
}
else
@@ -158,10 +159,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
monitor.StopMonitoringSession(parameters.OwnerUri, out session);
session.XEventSession.Stop();
- await requestContext.SendResult(new StopProfilingResult
- {
- Succeeded = true
- });
+ await requestContext.SendResult(new StopProfilingResult{});
}
catch (Exception e)
{
@@ -170,48 +168,53 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
///
- /// Starts a new profiler session for the provided connection
+ /// Handle request to pause a profiling session
///
- internal ProfilerSession StartSession(string sessionId, ConnectionInfo connInfo)
+ internal async Task HandlePauseProfilingRequest(PauseProfilingParams parameters, RequestContext requestContext)
+ {
+ try
+ {
+ monitor.PauseViewer(parameters.OwnerUri);
+
+ await requestContext.SendResult(new PauseProfilingResult{});
+ }
+ catch (Exception e)
+ {
+ await requestContext.SendError(e);
+ }
+ }
+
+ ///
+ /// Starts a new profiler session or connects to an existing session
+ /// for the provided connection and template info
+ ///
+ ///
+ /// The XEvent Session Id that was started
+ ///
+ internal int StartSession(string ownerUri, string template, ConnectionInfo connInfo)
{
// create a new XEvent session and Profiler session
- var xeSession = this.XEventSessionFactory.CreateXEventSession(connInfo);
- var profilerSession = new ProfilerSession()
- {
- SessionId = sessionId,
- XEventSession = xeSession
- };
+ var xeSession = this.XEventSessionFactory.GetOrCreateXEventSession(template, connInfo);
// start monitoring the profiler session
- monitor.StartMonitoringSession(profilerSession);
+ monitor.StartMonitoringSession(ownerUri, xeSession);
- return profilerSession;
+ return xeSession.Id;
}
///
- /// Create a new XEvent sessions per the IXEventSessionFactory contract
- ///
- public IXEventSession CreateXEventSession(ConnectionInfo connInfo)
- {
- var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
- SqlStoreConnection connection = new SqlStoreConnection(sqlConnection);
- Session session = ProfilerService.GetOrCreateSession(connection, "Profiler");
-
- // create xevent session wrapper
- return new XEventSession()
- {
- Session = session
- };
- }
-
- ///
- /// Gets an existing XEvent session or creates one if no matching session exists.
+ /// Gets or creates an XEvent session with the given template per the IXEventSessionFactory contract
/// Also starts the session if it isn't currently running
///
- private static Session GetOrCreateSession(SqlStoreConnection connection, string sessionName)
+ public IXEventSession GetOrCreateXEventSession(string template, ConnectionInfo connInfo)
{
+ string sessionName = "Profiler";
+
+ var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
+ SqlStoreConnection connection = new SqlStoreConnection(sqlConnection);
XEStore store = new XEStore(connection);
Session session = store.Sessions[sessionName];
+
// start the session if it isn't already running
if (session == null)
{
@@ -222,7 +225,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
session.Start();
}
- return session;
+
+ // create xevent session wrapper
+ return new XEventSession()
+ {
+ Session = session
+ };
}
private static Session CreateSession(SqlStoreConnection connection, string sessionName)
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs
index 9d5f59f7..66a73bf2 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs
@@ -24,10 +24,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
private TimeSpan pollingDelay = DefaultPollingDelay;
private ProfilerEvent lastSeenEvent = null;
- ///
- /// Unique ID for the session
- ///
- public string SessionId { get; set; }
+ public bool pollImmediatly = false;
///
/// Connection to use for the session
@@ -47,10 +44,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
lock (this.pollingLock)
{
- if (!this.isPolling && DateTime.Now.Subtract(this.lastPollTime) >= pollingDelay)
+ if (pollImmediatly || (!this.isPolling && DateTime.Now.Subtract(this.lastPollTime) >= pollingDelay))
{
this.isPolling = true;
this.lastPollTime = DateTime.Now;
+ this.pollImmediatly = false;
return true;
}
else
@@ -81,7 +79,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
/// The delay between session polls
///
- public TimeSpan PollingDelay
+ public TimeSpan PollingDelay
{
get
{
@@ -108,15 +106,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
return false;
}
-
+
///
/// Removed profiler polling events from event list
- ///
+ ///
public List FilterProfilerEvents(List events)
{
int idx = events.Count;
while (--idx >= 0)
- {
+ {
if (IsProfilerEvent(events[idx]))
{
events.RemoveAt(idx);
@@ -126,7 +124,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
///
- /// Filter the event list to not include previously seen events,
+ /// Filter the event list to not include previously seen events,
/// and to exclude events that happened before the profiling session began.
///
public void FilterOldEvents(List events)
@@ -151,7 +149,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
events.RemoveRange(0, idx + 1);
}
- // save the last event so we know where to clean-up the list from next time
+ // save the last event so we know where to clean-up the list from next time
if (events.Count > 0)
{
lastSeenEvent = events.LastOrDefault();
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs
index 09050b5d..a8bd72a9 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs
@@ -20,7 +20,7 @@ using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
///
- /// Classs to monitor active profiler sessions
+ /// Class to monitor active profiler sessions
///
public class ProfilerSessionMonitor : IProfilerSessionMonitor
{
@@ -30,9 +30,33 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
private object listenersLock = new object();
+ private object pollingLock = new object();
+
private Task processorThread = null;
- private Dictionary monitoredSessions = new Dictionary();
+ private struct Viewer
+ {
+ public string Id { get; set; }
+ public bool active { get; set; }
+
+ public int xeSessionId { get; set; }
+
+ public Viewer(string Id, bool active, int xeId)
+ {
+ this.Id = Id;
+ this.active = active;
+ this.xeSessionId = xeId;
+ }
+ };
+
+ // XEvent Session Id's matched to the Profiler Id's watching them
+ private Dictionary> sessionViewers = new Dictionary>();
+
+ // XEvent Session Id's matched to their Profiler Sessions
+ private Dictionary monitoredSessions = new Dictionary();
+
+ // ViewerId -> Viewer objects
+ private Dictionary allViewers = new Dictionary();
private List listeners = new List();
@@ -40,29 +64,58 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
/// Registers a session event listener to receive a callback when events arrive
///
public void AddSessionListener(IProfilerSessionListener listener)
- {
- lock (this.listenersLock)
+ {
+ lock (this.listenersLock)
{
this.listeners.Add(listener);
}
}
///
- /// Start monitoring the provided sessions
+ /// Start monitoring the provided session
///
- public bool StartMonitoringSession(ProfilerSession session)
+ public bool StartMonitoringSession(string viewerId, IXEventSession session)
{
lock (this.sessionsLock)
{
- // start the monitoring thread
+ // start the monitoring thread
if (this.processorThread == null)
{
- this.processorThread = Task.Factory.StartNew(ProcessSessions);;
+ this.processorThread = Task.Factory.StartNew(ProcessSessions);
}
- if (!this.monitoredSessions.ContainsKey(session.SessionId))
+ // create new profiling session if needed
+ if (!this.monitoredSessions.ContainsKey(session.Id))
{
- this.monitoredSessions.Add(session.SessionId, session);
+ var profilerSession = new ProfilerSession();
+ profilerSession.XEventSession = session;
+
+ this.monitoredSessions.Add(session.Id, profilerSession);
+ }
+
+ // create a new viewer, or configure existing viewer
+ Viewer viewer;
+ if (!this.allViewers.TryGetValue(viewerId, out viewer))
+ {
+ viewer = new Viewer(viewerId, true, session.Id);
+ allViewers.Add(viewerId, viewer);
+ }
+ else
+ {
+ viewer.active = true;
+ viewer.xeSessionId = session.Id;
+ }
+
+ // add viewer to XEvent session viewers
+ List viewers;
+ if (this.sessionViewers.TryGetValue(session.Id, out viewers))
+ {
+ viewers.Add(viewerId);
+ }
+ else
+ {
+ viewers = new List{ viewerId };
+ sessionViewers.Add(session.Id, viewers);
}
}
@@ -70,15 +123,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
///
- /// Stop monitoring the session specified by the sessionId
+ /// Stop monitoring the session watched by viewerId
///
- public bool StopMonitoringSession(string sessionId, out ProfilerSession session)
+ public bool StopMonitoringSession(string viewerId, out ProfilerSession session)
{
lock (this.sessionsLock)
{
- if (this.monitoredSessions.ContainsKey(sessionId))
+ Viewer v;
+ if (this.allViewers.TryGetValue(viewerId, out v))
{
- return this.monitoredSessions.Remove(sessionId, out session);
+ return RemoveSession(v.xeSessionId, out session);
}
else
{
@@ -88,23 +142,84 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
}
+ ///
+ /// Toggle the pause state for the viewer
+ ///
+ public void PauseViewer(string viewerId)
+ {
+ lock (this.sessionsLock)
+ {
+ Viewer v = this.allViewers[viewerId];
+ v.active = !v.active;
+ this.allViewers[viewerId] = v;
+ }
+ }
+
+ private bool RemoveSession(int sessionId, out ProfilerSession session)
+ {
+ lock (this.sessionsLock)
+ {
+ if (this.monitoredSessions.Remove(sessionId, out session))
+ {
+ //remove all viewers for this session
+ List viewerIds;
+ if (sessionViewers.Remove(sessionId, out viewerIds))
+ {
+ foreach (String viewerId in viewerIds)
+ {
+ this.allViewers.Remove(viewerId);
+ }
+ return true;
+ }
+ else
+ {
+ session = null;
+ return false;
+ }
+ }
+ else
+ {
+ session = null;
+ return false;
+ }
+ }
+ }
+
+ public void PollSession(int sessionId)
+ {
+ lock (this.sessionsLock)
+ {
+ this.monitoredSessions[sessionId].pollImmediatly = true;
+ }
+ lock (this.pollingLock)
+ {
+ Monitor.Pulse(pollingLock);
+ }
+ }
+
///
/// The core queue processing method
///
///
private void ProcessSessions()
- {
+ {
while (true)
{
- lock (this.sessionsLock)
+ lock (this.pollingLock)
{
- foreach (var session in this.monitoredSessions.Values)
+ lock (this.sessionsLock)
{
- ProcessSession(session);
+ foreach (var session in this.monitoredSessions.Values)
+ {
+ List viewers = this.sessionViewers[session.XEventSession.Id];
+ if (viewers.Any(v => allViewers[v].active))
+ {
+ ProcessSession(session);
+ }
+ }
}
+ Monitor.Wait(this.pollingLock, PollingLoopDelay);
}
-
- Thread.Sleep(PollingLoopDelay);
}
}
@@ -115,12 +230,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
if (session.TryEnterPolling())
{
- Task.Factory.StartNew(() =>
+ Task.Factory.StartNew(() =>
{
var events = PollSession(session);
if (events.Count > 0)
{
- SendEventsToListeners(session.SessionId, events);
+ // notify all viewers for the polled session
+ List viewerIds = this.sessionViewers[session.XEventSession.Id];
+ foreach (string viewerId in viewerIds)
+ {
+ if (allViewers[viewerId].active)
+ {
+ SendEventsToListeners(viewerId, events);
+ }
+ }
}
});
}
@@ -151,7 +274,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
}
}
- catch(Exception ex)
+ catch (Exception ex)
{
Logger.Write(LogLevel.Warning, "Failed to pool session. error: " + ex.Message);
}
@@ -187,7 +310,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
var timestamp = node.Attributes["timestamp"];
var profilerEvent = new ProfilerEvent(name.InnerText, timestamp.InnerText);
-
+
foreach (XmlNode childNode in node.ChildNodes)
{
var childName = childNode.Attributes["name"];
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs
index 4a65c67b..b1a3903c 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs
@@ -10,10 +10,23 @@ using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
+ ///
+ /// Class to access underlying XEvent session.
+ ///
public class XEventSession : IXEventSession
{
public Session Session { get; set; }
+ public int Id
+ {
+ get { return Session.ID; }
+ }
+
+ public void Start()
+ {
+ this.Session.Start();
+ }
+
public void Stop()
{
this.Session.Stop();
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Profiler/ProfilerServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Profiler/ProfilerServiceTests.cs
index c384ef9a..f0a730d8 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Profiler/ProfilerServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Profiler/ProfilerServiceTests.cs
@@ -44,7 +44,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Profiler
string sessionId = null;
var startContext = new Mock>();
startContext.Setup(rc => rc.SendResult(It.IsAny()))
- .Returns((result) =>
+ .Returns((result) =>
{
// capture the session id for sending the stop message
sessionId = result.SessionId;
@@ -71,7 +71,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Profiler
await profilerService.HandleStopProfilingRequest(stopParams, stopContext.Object);
stopContext.VerifyAll();
- }
+ }
}
///
@@ -82,7 +82,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Profiler
{
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
ProfilerService profilerService = new ProfilerService();
- IXEventSession xeSession = profilerService.CreateXEventSession(liveConnection.ConnectionInfo);
+ IXEventSession xeSession = profilerService.GetOrCreateXEventSession("Profiler", liveConnection.ConnectionInfo);
Assert.NotNull(xeSession);
Assert.NotNull(xeSession.GetTargetXml());
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs
index ac5b22d2..24740383 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs
@@ -7,6 +7,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using System.Collections.Generic;
using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
@@ -32,6 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
public async Task TestStartProfilingRequest()
{
string sessionId = null;
+ bool recievedEvents = false;
string testUri = "profiler_uri";
var requestContext = new Mock>();
requestContext.Setup(rc => rc.SendResult(It.IsAny()))
@@ -42,10 +44,15 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
return Task.FromResult(0);
});
- var sessionListener = new TestSessionListener();
+ // capture listener event notifications
+ var mockListener = new Mock();
+ mockListener.Setup(p => p.EventsAvailable(It.IsAny(), It.IsAny>())).Callback(() =>
+ {
+ recievedEvents = true;
+ });
var profilerService = new ProfilerService();
- profilerService.SessionMonitor.AddSessionListener(sessionListener);
+ profilerService.SessionMonitor.AddSessionListener(mockListener.Object);
profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo);
@@ -55,15 +62,33 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
requestParams.OwnerUri = testUri;
requestParams.TemplateName = "Standard";
+ // start profiling session
await profilerService.HandleStartProfilingRequest(requestParams, requestContext.Object);
- // wait a bit for profile sessions to be polled
- Thread.Sleep(500);
+ profilerService.SessionMonitor.PollSession(1);
+ // simulate a short polling delay
+ Thread.Sleep(200);
+ profilerService.SessionMonitor.PollSession(1);
+
+ // wait for polling to finish, or for timeout
+ System.Timers.Timer pollingTimer = new System.Timers.Timer();
+ pollingTimer.Interval = 10000;
+ pollingTimer.Start();
+ bool timeout = false;
+ pollingTimer.Elapsed += new System.Timers.ElapsedEventHandler((s_, e_) => {timeout = true;});
+ while (sessionId == null && !timeout)
+ {
+ Thread.Sleep(250);
+ }
+ pollingTimer.Stop();
requestContext.VerifyAll();
- Assert.Equal(sessionListener.PreviousSessionId, sessionId);
- Assert.Equal(sessionListener.PreviousEvents.Count, 1);
+ // Check that the correct XEvent session was started
+ Assert.Equal(sessionId, "1");
+
+ // check that the proper owner Uri was used
+ Assert.True(recievedEvents);
}
///
@@ -82,11 +107,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
requestContext.Setup(rc => rc.SendResult(It.IsAny()))
.Returns((result) =>
{
- success = result.Succeeded;
+ success = true;
return Task.FromResult(0);
});
- // capture if session was dropped
+ // capture if session was stopped
var mockSession = new Mock();
mockSession.Setup(p => p.Stop()).Callback(() =>
{
@@ -104,17 +129,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
var requestParams = new StopProfilingParams();
requestParams.OwnerUri = testUri;
- ProfilerSession session = new ProfilerSession();
- session.XEventSession = mockSession.Object;
- session.SessionId = testUri;
-
- profilerService.SessionMonitor.StartMonitoringSession(session);
+ profilerService.SessionMonitor.StartMonitoringSession(testUri, mockSession.Object);
await profilerService.HandleStopProfilingRequest(requestParams, requestContext.Object);
requestContext.VerifyAll();
- // check that session was succesfully stopped and drop was called
+ // check that session was succesfully stopped and stop was called
Assert.True(success);
Assert.True(stopped);
@@ -122,5 +143,97 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
ProfilerSession ps;
Assert.False(profilerService.SessionMonitor.StopMonitoringSession(testUri, out ps));
}
+
+ ///
+ /// Test pausing then resuming a session
+ ///
+ ///
+ [Fact]
+ public async Task TestPauseProfilingRequest()
+ {
+ bool success = false;
+ string testUri = "test_session";
+ bool recievedEvents = false;
+
+ // capture pausing results
+ var requestContext = new Mock>();
+ requestContext.Setup(rc => rc.SendResult(It.IsAny()))
+ .Returns((result) =>
+ {
+ success = true;
+ return Task.FromResult(0);
+ });
+
+ // capture listener event notifications
+ var mockListener = new Mock();
+ mockListener.Setup(p => p.EventsAvailable(It.IsAny(), It.IsAny>())).Callback(() =>
+ {
+ recievedEvents = true;
+ });
+
+ // setup profiler service
+ var profilerService = new ProfilerService();
+ profilerService.SessionMonitor.AddSessionListener(mockListener.Object);
+ profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
+ ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
+ profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo);
+
+ var requestParams = new PauseProfilingParams();
+ requestParams.OwnerUri = testUri;
+
+ // begin monitoring session
+ profilerService.SessionMonitor.StartMonitoringSession(testUri, new TestXEventSession1());
+
+ // poll the session
+ profilerService.SessionMonitor.PollSession(1);
+ Thread.Sleep(500);
+ profilerService.SessionMonitor.PollSession(1);
+
+ // wait for polling to finish, or for timeout
+ System.Timers.Timer pollingTimer = new System.Timers.Timer();
+ pollingTimer.Interval = 10000;
+ pollingTimer.Start();
+ bool timeout = false;
+ pollingTimer.Elapsed += new System.Timers.ElapsedEventHandler((s_, e_) => {timeout = true;});
+ while (!recievedEvents && !timeout)
+ {
+ Thread.Sleep(250);
+ }
+ pollingTimer.Stop();
+
+ // confirm that polling works
+ Assert.True(recievedEvents);
+
+ // pause viewer
+ await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object);
+ Assert.True(success);
+
+ recievedEvents = false;
+ success = false;
+
+ profilerService.SessionMonitor.PollSession(1);
+
+ // confirm that no events were sent to paused listener
+ Assert.False(recievedEvents);
+
+ // unpause viewer
+ await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object);
+ Assert.True(success);
+
+ profilerService.SessionMonitor.PollSession(1);
+
+ // wait for polling to finish, or for timeout
+ timeout = false;
+ pollingTimer.Start();
+ while (!recievedEvents && !timeout)
+ {
+ Thread.Sleep(250);
+ }
+
+ // check that events got sent to listener
+ Assert.True(recievedEvents);
+
+ requestContext.VerifyAll();
+ }
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs
index a79f8336..f05adeef 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs
@@ -188,19 +188,168 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
" " +
"";
+
+
+ public int Id { get { return 51; } }
+
+ public void Start(){}
+
+ public void Stop(){}
public string GetTargetXml()
{
return testXEventXml;
}
+ }
+
+ public class TestXEventSession1 : IXEventSession
+ {
+ private const string testXEventXml_1 =
+ "" +
+ " " +
+ " " +
+ " " +
+ " 1" +
+ " " +
+ " " +
+ "";
+
+ private const string testXEventXml_2 =
+ "" +
+ " " +
+ " " +
+ " " +
+ " 1" +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " 1" +
+ " " +
+ " " +
+ "";
+
+ private const string testXEventXml_3 =
+ "" +
+ " " +
+ " " +
+ " " +
+ " 1" +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " 1" +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " 1" +
+ " " +
+ " " +
+ "";
+
+ public int Id { get { return 1; } }
+
+ public void Start(){}
public void Stop(){}
+
+ private int pollCount = 0;
+ private string[] poll_returns = { testXEventXml_1, testXEventXml_2, testXEventXml_3 };
+ public string GetTargetXml()
+ {
+ string res = poll_returns[pollCount];
+ pollCount++;
+ pollCount = pollCount > 2 ? 0 : pollCount;
+ return res;
+ }
+ }
+
+ public class TestXEventSession2 : IXEventSession
+ {
+ private const string testXEventXml_1 =
+ "" +
+ " " +
+ " " +
+ " " +
+ " 2" +
+ " " +
+ " " +
+ "";
+
+ private const string testXEventXml_2 =
+ "" +
+ " " +
+ " " +
+ " " +
+ " 2" +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " 2" +
+ " " +
+ " " +
+ "";
+
+ private const string testXEventXml_3 =
+ "" +
+ " " +
+ " " +
+ " " +
+ " 2" +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " 2" +
+ " " +
+ " " +
+ " " +
+ " " +
+ " " +
+ " 2" +
+ " " +
+ " " +
+ "";
+
+ public int Id { get { return 2; } }
+
+ public void Start(){}
+
+ public void Stop(){}
+
+ private int pollCount = 0;
+ private string[] poll_returns = { testXEventXml_1, testXEventXml_2, testXEventXml_3 };
+ public string GetTargetXml()
+ {
+ string res = poll_returns[pollCount];
+ pollCount++;
+ pollCount = pollCount > 2 ? 0 : pollCount;
+ return res;
+ }
}
public class TestXEventSessionFactory : IXEventSessionFactory
{
- public IXEventSession CreateXEventSession(ConnectionInfo connInfo)
+ private int sessionNum = 1;
+ public IXEventSession GetOrCreateXEventSession(string template, ConnectionInfo connInfo)
{
- return new TestXEventSession();
+ if(sessionNum == 1)
+ {
+ sessionNum = 2;
+ return new TestXEventSession1();
+ }
+ else
+ {
+ sessionNum = 1;
+ return new TestXEventSession2();
+ }
}
}
}