mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-27 09:35:38 -05:00
Add "Open XEL file" support to profiler in sqltoolsservice (#2091)
* Open XEL file changes * placeholders for openxel * add observable xe reader * md format tweaks * implement localfile as a new session type * add ErrorMessage to session stopped notice * fix flaky test * handle already running session * fix stopped session event send on file completion * fix flaky unit test * Update XElite and dependent versions * Fix errors after merge and remove failing tests for now * Fix main merge mess-up. Address comments. Add one more relevant test. * Remove extra namespace. * Remove unnecessary import * Fix build error * Address comments. * Remove disabiling JSON002 compiler warning * Address comments and update json handling * Fix build error * Fix integration test (emerged due to Main merge mess up) * Clean up code (no functional changes) --------- Co-authored-by: Karl Burtram <karlb@microsoft.com> Co-authored-by: shueybubbles <david.shiflet@microsoft.com>
This commit is contained in:
@@ -7,7 +7,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
|
||||
|
||||
@@ -16,29 +19,43 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
|
||||
/// <summary>
|
||||
/// Profiler session class
|
||||
/// </summary>
|
||||
public class ProfilerSession
|
||||
public class ProfilerSession : IDisposable
|
||||
{
|
||||
private static readonly TimeSpan DefaultPollingDelay = TimeSpan.FromSeconds(1);
|
||||
private object pollingLock = new object();
|
||||
private bool isPolling = false;
|
||||
private DateTime lastPollTime = DateTime.Now.Subtract(DefaultPollingDelay);
|
||||
private TimeSpan pollingDelay = DefaultPollingDelay;
|
||||
private ProfilerEvent lastSeenEvent = null;
|
||||
|
||||
private readonly SessionObserver sessionObserver;
|
||||
private readonly IXEventSession xEventSession;
|
||||
private readonly IDisposable observerDisposable;
|
||||
private bool eventsLost = false;
|
||||
int lastSeenId = -1;
|
||||
|
||||
public bool pollImmediatly = false;
|
||||
public bool pollImmediately = false;
|
||||
|
||||
/// <summary>
|
||||
/// Connection to use for the session
|
||||
/// </summary>
|
||||
public ConnectionInfo ConnectionInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new ProfilerSession to watch the given IXeventSession's incoming events
|
||||
/// </summary>
|
||||
/// <param name="xEventSession"></param>
|
||||
public ProfilerSession(IXEventSession xEventSession)
|
||||
{
|
||||
this.xEventSession = xEventSession;
|
||||
if (xEventSession is IObservableXEventSession observableSession)
|
||||
{
|
||||
observerDisposable = observableSession.ObservableSessionEvents?.Subscribe(sessionObserver = new SessionObserver());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Underlying XEvent session wrapper
|
||||
/// </summary>
|
||||
public IXEventSession XEventSession { get; set; }
|
||||
public IXEventSession XEventSession => xEventSession;
|
||||
|
||||
/// <summary>
|
||||
/// Try to set the session into polling mode if criteria is meet
|
||||
@@ -48,11 +65,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
|
||||
{
|
||||
lock (this.pollingLock)
|
||||
{
|
||||
if (pollImmediatly || (!this.isPolling && DateTime.Now.Subtract(this.lastPollTime) >= pollingDelay))
|
||||
if (pollImmediately || (!this.isPolling && DateTime.Now.Subtract(this.lastPollTime) >= PollingDelay))
|
||||
{
|
||||
this.isPolling = true;
|
||||
this.lastPollTime = DateTime.Now;
|
||||
this.pollImmediatly = false;
|
||||
this.pollImmediately = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -83,13 +100,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
|
||||
/// <summary>
|
||||
/// The delay between session polls
|
||||
/// </summary>
|
||||
public TimeSpan PollingDelay
|
||||
{
|
||||
get
|
||||
{
|
||||
return pollingDelay;
|
||||
}
|
||||
}
|
||||
public TimeSpan PollingDelay { get; } = DefaultPollingDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Could events have been lost in the last poll
|
||||
@@ -197,5 +208,129 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
|
||||
events.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the current session has completed processing and will provide no new events
|
||||
/// </summary>
|
||||
public bool Completed
|
||||
{
|
||||
get
|
||||
{
|
||||
return (sessionObserver != null) ? sessionObserver.Completed : error != null;
|
||||
}
|
||||
}
|
||||
|
||||
private Exception error;
|
||||
/// <summary>
|
||||
/// Provides any fatal error encountered when processing a session
|
||||
/// </summary>
|
||||
public Exception Error
|
||||
{
|
||||
get
|
||||
{
|
||||
return sessionObserver?.Error ?? error;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current set of events in the session buffer.
|
||||
/// For RingBuffer sessions, returns the content of the session ring buffer by querying the server.
|
||||
/// For LiveTarget and LocalFile sessions, returns the events buffered in memory since the last call to GetCurrentEvents.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ProfilerEvent> GetCurrentEvents()
|
||||
{
|
||||
if (XEventSession == null && sessionObserver == null)
|
||||
{
|
||||
return Enumerable.Empty<ProfilerEvent>();
|
||||
}
|
||||
if (sessionObserver == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var targetXml = XEventSession.GetTargetXml();
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(targetXml);
|
||||
|
||||
var nodes = xmlDoc.DocumentElement.GetElementsByTagName("event");
|
||||
var rawEvents = nodes.Cast<XmlNode>().Select(ParseProfilerEvent).ToList();
|
||||
FilterOldEvents(rawEvents);
|
||||
return rawEvents;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error ??= e;
|
||||
return Enumerable.Empty<ProfilerEvent>();
|
||||
}
|
||||
}
|
||||
return sessionObserver.CurrentEvents;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single event node from XEvent XML
|
||||
/// </summary>
|
||||
private static ProfilerEvent ParseProfilerEvent(XmlNode node)
|
||||
{
|
||||
var name = node.Attributes["name"];
|
||||
var timestamp = node.Attributes["timestamp"];
|
||||
|
||||
var profilerEvent = new ProfilerEvent(name.InnerText, timestamp.InnerText);
|
||||
|
||||
foreach (XmlNode childNode in node.ChildNodes)
|
||||
{
|
||||
var childName = childNode.Attributes["name"];
|
||||
XmlNode typeNode = childNode.SelectSingleNode("type");
|
||||
var typeName = typeNode.Attributes["name"];
|
||||
XmlNode valueNode = childNode.SelectSingleNode("value");
|
||||
|
||||
if (!profilerEvent.Values.ContainsKey(childName.InnerText))
|
||||
{
|
||||
profilerEvent.Values.Add(childName.InnerText, valueNode.InnerText);
|
||||
}
|
||||
}
|
||||
|
||||
return profilerEvent;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
observerDisposable?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("SessionObserver. Current:{writeBuffer.Count} Total:{eventCount}")]
|
||||
class SessionObserver : IObserver<ProfilerEvent>
|
||||
{
|
||||
private List<ProfilerEvent> writeBuffer = new List<ProfilerEvent>();
|
||||
private Int64 eventCount = 0;
|
||||
public void OnCompleted()
|
||||
{
|
||||
Completed = true;
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
|
||||
public void OnNext(ProfilerEvent value)
|
||||
{
|
||||
writeBuffer.Add(value);
|
||||
eventCount++;
|
||||
}
|
||||
|
||||
public bool Completed { get; private set; }
|
||||
|
||||
public Exception Error { get; private set; }
|
||||
|
||||
public IEnumerable<ProfilerEvent> CurrentEvents
|
||||
{
|
||||
get
|
||||
{
|
||||
var newBuffer = new List<ProfilerEvent>();
|
||||
var oldBuffer = Interlocked.Exchange(ref writeBuffer, newBuffer);
|
||||
return oldBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user